Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
authorDavid S. Miller <davem@davemloft.net>
Tue, 23 Apr 2013 00:32:51 +0000 (20:32 -0400)
committerDavid S. Miller <davem@davemloft.net>
Tue, 23 Apr 2013 00:32:51 +0000 (20:32 -0400)
Conflicts:
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
include/net/scm.h
net/batman-adv/routing.c
net/ipv4/tcp_input.c

The e{uid,gid} --> {uid,gid} credentials fix conflicted with the
cleanup in net-next to now pass cred structs around.

The be2net driver had a bug fix in 'net' that overlapped with the VLAN
interface changes by Patrick McHardy in net-next.

An IGB conflict existed because in 'net' the build_skb() support was
reverted, and in 'net-next' there was a comment style fix within that
code.

Several batman-adv conflicts were resolved by making sure that all
calls to batadv_is_my_mac() are changed to have a new bat_priv first
argument.

Eric Dumazet's TS ECR fix in TCP in 'net' conflicted with the F-RTO
rewrite in 'net-next', mostly overlapping changes.

Thanks to Stephen Rothwell and Antonio Quartulli for help with several
of these merge resolutions.

Signed-off-by: David S. Miller <davem@davemloft.net>
1282 files changed:
Documentation/ABI/testing/sysfs-class-net-mesh
Documentation/DocBook/80211.tmpl
Documentation/cgroups/00-INDEX
Documentation/cgroups/net_cls.txt [new file with mode: 0644]
Documentation/devicetree/bindings/marvell.txt
Documentation/devicetree/bindings/net/can/atmel-can.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/cpsw.txt
Documentation/devicetree/bindings/net/dsa/dsa.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
Documentation/networking/ieee802154.txt
Documentation/networking/ip-sysctl.txt
Documentation/networking/netlink_mmap.txt [new file with mode: 0644]
Documentation/networking/packet_mmap.txt
Documentation/networking/stmmac.txt
MAINTAINERS
arch/alpha/include/uapi/asm/socket.h
arch/arm/boot/dts/am33xx.dtsi
arch/arm/net/bpf_jit_32.c
arch/arm/plat-orion/common.c
arch/avr32/include/uapi/asm/socket.h
arch/cris/include/uapi/asm/socket.h
arch/frv/include/uapi/asm/socket.h
arch/h8300/include/uapi/asm/socket.h
arch/ia64/include/uapi/asm/socket.h
arch/m32r/include/uapi/asm/socket.h
arch/mips/include/uapi/asm/socket.h
arch/mn10300/include/uapi/asm/socket.h
arch/parisc/include/uapi/asm/socket.h
arch/powerpc/include/uapi/asm/socket.h
arch/powerpc/net/bpf_jit_comp.c
arch/powerpc/platforms/chrp/pegasos_eth.c
arch/powerpc/sysdev/mv64x60_dev.c
arch/s390/include/uapi/asm/socket.h
arch/sparc/include/uapi/asm/socket.h
arch/sparc/net/bpf_jit_comp.c
arch/x86/net/bpf_jit_comp.c
arch/xtensa/include/uapi/asm/socket.h
drivers/bcma/core.c
drivers/bcma/driver_chipcommon.c
drivers/bcma/driver_chipcommon_pmu.c
drivers/bcma/main.c
drivers/bcma/scan.c
drivers/bcma/sprom.c
drivers/bluetooth/btmrvl_sdio.c
drivers/connector/cn_proc.c
drivers/connector/connector.c
drivers/dma/ioat/dca.c
drivers/firewire/Kconfig
drivers/firewire/net.c
drivers/infiniband/hw/cxgb4/cm.c
drivers/infiniband/hw/cxgb4/device.c
drivers/infiniband/hw/cxgb4/iw_cxgb4.h
drivers/infiniband/hw/cxgb4/mem.c
drivers/infiniband/hw/cxgb4/provider.c
drivers/infiniband/hw/cxgb4/qp.c
drivers/infiniband/hw/cxgb4/t4.h
drivers/infiniband/hw/nes/nes_hw.c
drivers/infiniband/hw/nes/nes_nic.c
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/isdn/capi/capidrv.c
drivers/isdn/divert/isdn_divert.c
drivers/isdn/hisax/fsm.c
drivers/isdn/hisax/hfc_sx.c
drivers/isdn/i4l/isdn_net.c
drivers/media/dvb-core/dvb_net.c
drivers/net/Kconfig
drivers/net/appletalk/Kconfig
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_main.c
drivers/net/caif/Kconfig
drivers/net/caif/Makefile
drivers/net/caif/caif_shm_u5500.c [deleted file]
drivers/net/caif/caif_shmcore.c [deleted file]
drivers/net/can/Kconfig
drivers/net/can/at91_can.c
drivers/net/can/bfin_can.c
drivers/net/can/mcp251x.c
drivers/net/can/sja1000/ems_pci.c
drivers/net/can/sja1000/ems_pcmcia.c
drivers/net/can/sja1000/kvaser_pci.c
drivers/net/can/sja1000/peak_pci.c
drivers/net/can/sja1000/peak_pcmcia.c
drivers/net/can/sja1000/plx_pci.c
drivers/net/can/sja1000/sja1000.c
drivers/net/can/sja1000/sja1000.h
drivers/net/ethernet/3com/typhoon.c
drivers/net/ethernet/adaptec/starfire.c
drivers/net/ethernet/adi/bfin_mac.c
drivers/net/ethernet/aeroflex/greth.c
drivers/net/ethernet/alteon/acenic.c
drivers/net/ethernet/amd/7990.c
drivers/net/ethernet/amd/a2065.c
drivers/net/ethernet/amd/am79c961a.c
drivers/net/ethernet/amd/amd8111e.c
drivers/net/ethernet/amd/ariadne.c
drivers/net/ethernet/amd/atarilance.c
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/amd/declance.c
drivers/net/ethernet/amd/mvme147.c
drivers/net/ethernet/amd/ni65.c
drivers/net/ethernet/amd/pcnet32.c
drivers/net/ethernet/amd/sun3lance.c
drivers/net/ethernet/amd/sunlance.c
drivers/net/ethernet/apple/macmace.c
drivers/net/ethernet/atheros/atl1c/atl1c_main.c
drivers/net/ethernet/atheros/atl1e/atl1e_main.c
drivers/net/ethernet/atheros/atlx/atl1.c
drivers/net/ethernet/atheros/atlx/atl2.c
drivers/net/ethernet/atheros/atlx/atlx.c
drivers/net/ethernet/broadcom/bcm63xx_enet.c
drivers/net/ethernet/broadcom/bgmac.c
drivers/net/ethernet/broadcom/bgmac.h
drivers/net/ethernet/broadcom/bnx2.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h
drivers/net/ethernet/broadcom/sb1250-mac.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/broadcom/tg3.h
drivers/net/ethernet/brocade/bna/bfa_ioc.c
drivers/net/ethernet/brocade/bna/bnad.c
drivers/net/ethernet/cadence/at91_ether.c
drivers/net/ethernet/cadence/macb.c
drivers/net/ethernet/cadence/macb.h
drivers/net/ethernet/calxeda/xgmac.c
drivers/net/ethernet/chelsio/cxgb/cxgb2.c
drivers/net/ethernet/chelsio/cxgb/sge.c
drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c
drivers/net/ethernet/chelsio/cxgb3/sge.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
drivers/net/ethernet/chelsio/cxgb4vf/adapter.h
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/chelsio/cxgb4vf/sge.c
drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h
drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
drivers/net/ethernet/cirrus/cs89x0.c
drivers/net/ethernet/cirrus/ep93xx_eth.c
drivers/net/ethernet/cisco/enic/enic_dev.c
drivers/net/ethernet/cisco/enic/enic_dev.h
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/cisco/enic/vnic_dev.c
drivers/net/ethernet/davicom/dm9000.c
drivers/net/ethernet/dec/tulip/xircom_cb.c
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/emulex/benet/be.h
drivers/net/ethernet/emulex/benet/be_cmds.c
drivers/net/ethernet/emulex/benet/be_cmds.h
drivers/net/ethernet/emulex/benet/be_ethtool.c
drivers/net/ethernet/emulex/benet/be_hw.h
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/emulex/benet/be_roce.c
drivers/net/ethernet/emulex/benet/be_roce.h
drivers/net/ethernet/faraday/ftgmac100.c
drivers/net/ethernet/faraday/ftmac100.c
drivers/net/ethernet/freescale/Makefile
drivers/net/ethernet/freescale/fec.c [deleted file]
drivers/net/ethernet/freescale/fec_main.c [new file with mode: 0644]
drivers/net/ethernet/freescale/fec_mpc52xx.c
drivers/net/ethernet/freescale/fec_ptp.c
drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/gianfar.h
drivers/net/ethernet/freescale/gianfar_ethtool.c
drivers/net/ethernet/freescale/gianfar_ptp.c
drivers/net/ethernet/freescale/gianfar_sysfs.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/freescale/ucc_geth_ethtool.c
drivers/net/ethernet/fujitsu/fmvj18x_cs.c
drivers/net/ethernet/i825xx/82596.c
drivers/net/ethernet/i825xx/lib82596.c
drivers/net/ethernet/ibm/ehea/ehea_main.c
drivers/net/ethernet/ibm/emac/mal.c
drivers/net/ethernet/ibm/ibmveth.c
drivers/net/ethernet/intel/e1000/e1000_ethtool.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/80003es2lan.c
drivers/net/ethernet/intel/e1000e/82571.c
drivers/net/ethernet/intel/e1000e/82571.h
drivers/net/ethernet/intel/e1000e/defines.h
drivers/net/ethernet/intel/e1000e/e1000.h
drivers/net/ethernet/intel/e1000e/ethtool.c
drivers/net/ethernet/intel/e1000e/hw.h
drivers/net/ethernet/intel/e1000e/ich8lan.c
drivers/net/ethernet/intel/e1000e/ich8lan.h
drivers/net/ethernet/intel/e1000e/mac.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/e1000e/nvm.c
drivers/net/ethernet/intel/e1000e/param.c
drivers/net/ethernet/intel/e1000e/phy.c
drivers/net/ethernet/intel/igb/e1000_82575.c
drivers/net/ethernet/intel/igb/e1000_82575.h
drivers/net/ethernet/intel/igb/e1000_defines.h
drivers/net/ethernet/intel/igb/e1000_hw.h
drivers/net/ethernet/intel/igb/e1000_i210.c
drivers/net/ethernet/intel/igb/e1000_i210.h
drivers/net/ethernet/intel/igb/e1000_mac.c
drivers/net/ethernet/intel/igb/e1000_mac.h
drivers/net/ethernet/intel/igb/e1000_mbx.c
drivers/net/ethernet/intel/igb/e1000_mbx.h
drivers/net/ethernet/intel/igb/e1000_nvm.c
drivers/net/ethernet/intel/igb/e1000_phy.c
drivers/net/ethernet/intel/igb/e1000_regs.h
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_hwmon.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igb/igb_ptp.c
drivers/net/ethernet/intel/igbvf/netdev.c
drivers/net/ethernet/intel/ixgb/ixgb_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/intel/ixgbevf/vf.c
drivers/net/ethernet/jme.c
drivers/net/ethernet/marvell/Kconfig
drivers/net/ethernet/marvell/Makefile
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvmdio.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/pxa168_eth.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/mellanox/mlx4/cmd.c
drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/en_selftest.c
drivers/net/ethernet/mellanox/mlx4/fw.c
drivers/net/ethernet/mellanox/mlx4/mcg.c
drivers/net/ethernet/mellanox/mlx4/mlx4.h
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx4/port.c
drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
drivers/net/ethernet/micrel/ks8695net.c
drivers/net/ethernet/micrel/ks8851.c
drivers/net/ethernet/micrel/ks8851_mll.c
drivers/net/ethernet/microchip/enc28j60.c
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/natsemi/jazzsonic.c
drivers/net/ethernet/natsemi/macsonic.c
drivers/net/ethernet/natsemi/ns83820.c
drivers/net/ethernet/natsemi/sonic.c
drivers/net/ethernet/natsemi/xtsonic.c
drivers/net/ethernet/neterion/s2io.c
drivers/net/ethernet/neterion/vxge/vxge-main.c
drivers/net/ethernet/netx-eth.c
drivers/net/ethernet/nuvoton/w90p910_ether.c
drivers/net/ethernet/nvidia/forcedeth.c
drivers/net/ethernet/nxp/lpc_eth.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
drivers/net/ethernet/pasemi/pasemi_mac.c
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/netxen/netxen_nic.h
drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/qlogic/qla3xxx.c
drivers/net/ethernet/qlogic/qlcnic/Makefile
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
drivers/net/ethernet/qlogic/qlge/qlge_main.c
drivers/net/ethernet/rdc/r6040.c
drivers/net/ethernet/realtek/8139cp.c
drivers/net/ethernet/realtek/8139too.c
drivers/net/ethernet/realtek/atp.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/s6gmac.c
drivers/net/ethernet/seeq/ether3.c
drivers/net/ethernet/seeq/sgiseeq.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/efx.h
drivers/net/ethernet/sfc/enum.h
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/falcon.c
drivers/net/ethernet/sfc/filter.c
drivers/net/ethernet/sfc/mcdi_pcol.h
drivers/net/ethernet/sfc/net_driver.h
drivers/net/ethernet/sfc/nic.c
drivers/net/ethernet/sfc/ptp.c
drivers/net/ethernet/sfc/rx.c
drivers/net/ethernet/sfc/siena.c
drivers/net/ethernet/sgi/meth.c
drivers/net/ethernet/sis/sis900.c
drivers/net/ethernet/smsc/smc9194.c
drivers/net/ethernet/smsc/smc91x.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/smsc/smsc9420.c
drivers/net/ethernet/stmicro/stmmac/Kconfig
drivers/net/ethernet/stmicro/stmmac/Makefile
drivers/net/ethernet/stmicro/stmmac/chain_mode.c
drivers/net/ethernet/stmicro/stmmac/common.h
drivers/net/ethernet/stmicro/stmmac/descs.h
drivers/net/ethernet/stmicro/stmmac/descs_com.h
drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
drivers/net/ethernet/stmicro/stmmac/enh_desc.c
drivers/net/ethernet/stmicro/stmmac/mmc.h
drivers/net/ethernet/stmicro/stmmac/norm_desc.c
drivers/net/ethernet/stmicro/stmmac/ring_mode.c
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h [new file with mode: 0644]
drivers/net/ethernet/sun/niu.c
drivers/net/ethernet/sun/sunbmac.c
drivers/net/ethernet/sun/sunhme.c
drivers/net/ethernet/sun/sunqe.c
drivers/net/ethernet/tehuti/tehuti.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/toshiba/ps3_gelic_net.c
drivers/net/ethernet/toshiba/spider_net.c
drivers/net/ethernet/tundra/tsi108_eth.c
drivers/net/ethernet/via/via-rhine.c
drivers/net/ethernet/via/via-velocity.c
drivers/net/ethernet/wiznet/w5100.c
drivers/net/ethernet/wiznet/w5300.c
drivers/net/ethernet/xilinx/ll_temac_main.c
drivers/net/ethernet/xilinx/xilinx_axienet_main.c
drivers/net/ethernet/xircom/xirc2ps_cs.c
drivers/net/fddi/defxx.c
drivers/net/hamradio/yam.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/fakehard.c
drivers/net/ieee802154/mrf24j40.c
drivers/net/ifb.c
drivers/net/irda/ali-ircc.c
drivers/net/irda/au1k_ir.c
drivers/net/irda/bfin_sir.c
drivers/net/irda/nsc-ircc.c
drivers/net/irda/pxaficp_ir.c
drivers/net/irda/smsc-ircc2.c
drivers/net/irda/via-ircc.c
drivers/net/irda/w83977af_ir.c
drivers/net/macvlan.c
drivers/net/macvtap.c
drivers/net/phy/lxt.c
drivers/net/phy/marvell.c
drivers/net/phy/mdio-gpio.c
drivers/net/phy/mdio-octeon.c
drivers/net/phy/micrel.c
drivers/net/phy/phy.c
drivers/net/phy/spi_ks8995.c
drivers/net/phy/vitesse.c
drivers/net/plip/plip.c
drivers/net/ppp/ppp_synctty.c
drivers/net/team/Kconfig
drivers/net/team/Makefile
drivers/net/team/team.c
drivers/net/team/team_mode_broadcast.c
drivers/net/team/team_mode_random.c [new file with mode: 0644]
drivers/net/team/team_mode_roundrobin.c
drivers/net/tun.c
drivers/net/usb/asix_devices.c
drivers/net/usb/ax88179_178a.c
drivers/net/usb/cdc_ether.c
drivers/net/usb/cdc_mbim.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/dm9601.c
drivers/net/usb/mcs7830.c
drivers/net/usb/sierra_net.c
drivers/net/usb/usbnet.c
drivers/net/veth.c
drivers/net/virtio_net.c
drivers/net/vmxnet3/vmxnet3_drv.c
drivers/net/vmxnet3/vmxnet3_ethtool.c
drivers/net/vxlan.c
drivers/net/wireless/ath/ar5523/ar5523.c
drivers/net/wireless/ath/ath5k/ath5k.h
drivers/net/wireless/ath/ath5k/eeprom.c
drivers/net/wireless/ath/ath5k/eeprom.h
drivers/net/wireless/ath/ath5k/phy.c
drivers/net/wireless/ath/ath5k/reset.c
drivers/net/wireless/ath/ath6kl/Kconfig
drivers/net/wireless/ath/ath6kl/Makefile
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/debug.h
drivers/net/wireless/ath/ath6kl/hif.c
drivers/net/wireless/ath/ath6kl/htc_mbox.c
drivers/net/wireless/ath/ath6kl/htc_pipe.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/ath/ath6kl/target.h
drivers/net/wireless/ath/ath6kl/trace.c [new file with mode: 0644]
drivers/net/wireless/ath/ath6kl/trace.h [new file with mode: 0644]
drivers/net/wireless/ath/ath6kl/txrx.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath9k/ar9002_calib.c
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h
drivers/net/wireless/ath/ath9k/calib.c
drivers/net/wireless/ath/ath9k/common.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/dfs.c
drivers/net/wireless/ath/ath9k/dfs_debug.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/carl9170/carl9170.h
drivers/net/wireless/ath/carl9170/debug.c
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/phy.c
drivers/net/wireless/ath/carl9170/tx.c
drivers/net/wireless/ath/key.c
drivers/net/wireless/ath/wil6210/Makefile
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/dbg_hexdump.h [deleted file]
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/ath/wil6210/wmi.h
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/b43/b43.h
drivers/net/wireless/b43/dma.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/phy_ht.c
drivers/net/wireless/b43/phy_ht.h
drivers/net/wireless/b43/phy_lp.c
drivers/net/wireless/b43/phy_n.c
drivers/net/wireless/b43/radio_2056.c
drivers/net/wireless/b43/sdio.h
drivers/net/wireless/b43/tables_nphy.c
drivers/net/wireless/b43/tables_phy_lcn.c
drivers/net/wireless/b43legacy/dma.c
drivers/net/wireless/brcm80211/Kconfig
drivers/net/wireless/brcm80211/brcmfmac/Makefile
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/brcm80211/brcmfmac/fwil.c
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/p2p.c
drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
drivers/net/wireless/brcm80211/brcmsmac/Makefile
drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
drivers/net/wireless/brcm80211/brcmsmac/d11.h
drivers/net/wireless/brcm80211/brcmsmac/led.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmsmac/led.h [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/brcmsmac/main.h
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
drivers/net/wireless/brcm80211/brcmsmac/pmu.c
drivers/net/wireless/brcm80211/brcmsmac/pmu.h
drivers/net/wireless/brcm80211/brcmsmac/pub.h
drivers/net/wireless/brcm80211/brcmutil/utils.c
drivers/net/wireless/brcm80211/include/brcmu_utils.h
drivers/net/wireless/brcm80211/include/brcmu_wifi.h
drivers/net/wireless/iwlegacy/3945-mac.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/3945.h
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/dvm/agn.h
drivers/net/wireless/iwlwifi/dvm/calib.c
drivers/net/wireless/iwlwifi/dvm/calib.h
drivers/net/wireless/iwlwifi/dvm/commands.h
drivers/net/wireless/iwlwifi/dvm/debugfs.c
drivers/net/wireless/iwlwifi/dvm/lib.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/testmode.c
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-1000.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-2000.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-5000.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-6000.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-7000.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-agn-hw.h
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-debug.c
drivers/net/wireless/iwlwifi/iwl-devtrace.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-drv.h
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
drivers/net/wireless/iwlwifi/iwl-fh.h
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-notif-wait.c
drivers/net/wireless/iwlwifi/iwl-notif-wait.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c
drivers/net/wireless/iwlwifi/iwl-phy-db.h
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-test.c
drivers/net/wireless/iwlwifi/iwl-test.h
drivers/net/wireless/iwlwifi/iwl-testmode.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/binding.c
drivers/net/wireless/iwlwifi/mvm/bt-coex.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/led.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/time-event.h
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/1000.c [deleted file]
drivers/net/wireless/iwlwifi/pcie/2000.c [deleted file]
drivers/net/wireless/iwlwifi/pcie/5000.c [deleted file]
drivers/net/wireless/iwlwifi/pcie/6000.c [deleted file]
drivers/net/wireless/iwlwifi/pcie/7000.c [deleted file]
drivers/net/wireless/iwlwifi/pcie/cfg.h [deleted file]
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11ac.c
drivers/net/wireless/mwifiex/11ac.h
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/Makefile
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/ethtool.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/txrx.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/util.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/orinoco/orinoco_usb.c
drivers/net/wireless/p54/main.c
drivers/net/wireless/p54/p54spi.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/rndis_wlan.c
drivers/net/wireless/rt2x00/Kconfig
drivers/net/wireless/rt2x00/rt2800.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00queue.h
drivers/net/wireless/rt2x00/rt2x00usb.c
drivers/net/wireless/rtlwifi/Kconfig
drivers/net/wireless/rtlwifi/Makefile
drivers/net/wireless/rtlwifi/base.c
drivers/net/wireless/rtlwifi/base.h
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/rtlwifi/debug.c
drivers/net/wireless/rtlwifi/debug.h
drivers/net/wireless/rtlwifi/efuse.c
drivers/net/wireless/rtlwifi/efuse.h
drivers/net/wireless/rtlwifi/pci.c
drivers/net/wireless/rtlwifi/pci.h
drivers/net/wireless/rtlwifi/ps.c
drivers/net/wireless/rtlwifi/ps.h
drivers/net/wireless/rtlwifi/rtl8188ee/Makefile [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/def.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/dm.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/dm.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/fw.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/fw.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/hw.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/hw.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/led.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/led.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/phy.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/phy.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/pwrseqcmd.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/pwrseqcmd.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/reg.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/rf.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/rf.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/sw.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/sw.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/table.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/table.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/trx.c [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8188ee/trx.h [new file with mode: 0644]
drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
drivers/net/wireless/rtlwifi/rtl8192ce/hw.h
drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
drivers/net/wireless/rtlwifi/rtl8192de/dm.c
drivers/net/wireless/rtlwifi/rtl8192de/hw.c
drivers/net/wireless/rtlwifi/rtl8192de/phy.c
drivers/net/wireless/rtlwifi/rtl8192de/reg.h
drivers/net/wireless/rtlwifi/rtl8192de/sw.c
drivers/net/wireless/rtlwifi/rtl8192se/def.h
drivers/net/wireless/rtlwifi/rtl8192se/dm.c
drivers/net/wireless/rtlwifi/rtl8192se/hw.c
drivers/net/wireless/rtlwifi/rtl8192se/hw.h
drivers/net/wireless/rtlwifi/rtl8192se/phy.c
drivers/net/wireless/rtlwifi/rtl8192se/phy.h
drivers/net/wireless/rtlwifi/rtl8192se/sw.c
drivers/net/wireless/rtlwifi/rtl8192se/trx.c
drivers/net/wireless/rtlwifi/rtl8723ae/dm.c
drivers/net/wireless/rtlwifi/rtl8723ae/dm.h
drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
drivers/net/wireless/rtlwifi/rtl8723ae/fw.h
drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
drivers/net/wireless/rtlwifi/rtl8723ae/led.c
drivers/net/wireless/rtlwifi/rtl8723ae/sw.c
drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
drivers/net/wireless/rtlwifi/usb.c
drivers/net/wireless/rtlwifi/usb.h
drivers/net/wireless/rtlwifi/wifi.h
drivers/net/wireless/ti/wl1251/sdio.c
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl12xx/wl12xx.h
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/reg.h
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/debug.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
drivers/net/xen-netback/netback.c
drivers/net/xen-netfront.c
drivers/of/of_mdio.c
drivers/ptp/ptp_clock.c
drivers/ptp/ptp_pch.c
drivers/s390/kvm/virtio_ccw.c
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/scsi/csiostor/Makefile
drivers/scsi/csiostor/csio_hw.c
drivers/scsi/csiostor/csio_hw.h
drivers/scsi/csiostor/csio_hw_chip.h [new file with mode: 0644]
drivers/scsi/csiostor/csio_hw_t4.c [new file with mode: 0644]
drivers/scsi/csiostor/csio_hw_t5.c [new file with mode: 0644]
drivers/scsi/csiostor/csio_init.c
drivers/scsi/csiostor/csio_init.h
drivers/scsi/csiostor/csio_lnode.h
drivers/scsi/csiostor/csio_rnode.c
drivers/scsi/csiostor/csio_rnode.h
drivers/scsi/csiostor/csio_wr.c
drivers/scsi/fcoe/fcoe.c
drivers/scsi/scsi_netlink.c
drivers/scsi/scsi_transport_fc.c
drivers/scsi/scsi_transport_iscsi.c
drivers/ssb/driver_chipcommon.c
drivers/ssb/driver_chipcommon_pmu.c
drivers/ssb/driver_mipscore.c
drivers/ssb/driver_pcicore.c
drivers/ssb/embedded.c
drivers/ssb/main.c
drivers/ssb/pci.c
drivers/ssb/pcmcia.c
drivers/ssb/scan.c
drivers/ssb/sprom.c
drivers/ssb/ssb_private.h
drivers/staging/gdm72xx/netlink_k.c
drivers/vhost/net.c
drivers/vhost/vhost.c
fs/dlm/lowcomms.c
include/linux/bcma/bcma.h
include/linux/bcma/bcma_driver_chipcommon.h
include/linux/cn_proc.h
include/linux/filter.h
include/linux/ieee80211.h
include/linux/if_arp.h
include/linux/if_team.h
include/linux/if_vlan.h
include/linux/ktime.h
include/linux/micrel_phy.h
include/linux/mlx4/device.h
include/linux/mv643xx_eth.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/netfilter.h
include/linux/netfilter/nfnetlink.h
include/linux/netlink.h
include/linux/of_net.h
include/linux/openvswitch.h
include/linux/phy.h
include/linux/platform_data/cpsw.h
include/linux/rtnetlink.h
include/linux/sctp.h
include/linux/sh_eth.h
include/linux/skbuff.h
include/linux/socket.h
include/linux/spi/at86rf230.h
include/linux/ssb/ssb.h
include/linux/ssb/ssb_regs.h
include/linux/tcp.h
include/linux/usb/usbnet.h
include/linux/virtio.h
include/linux/vm_sockets.h [new file with mode: 0644]
include/net/af_unix.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/rfcomm.h
include/net/caif/caif_shm.h [deleted file]
include/net/cfg80211.h
include/net/cls_cgroup.h
include/net/dn_fib.h
include/net/firewire.h [new file with mode: 0644]
include/net/gre.h
include/net/ieee802154_netdev.h
include/net/if_inet6.h
include/net/inet_connection_sock.h
include/net/inet_frag.h
include/net/ip6_tunnel.h
include/net/ip_tunnels.h [new file with mode: 0644]
include/net/ip_vs.h
include/net/ipip.h [deleted file]
include/net/ipv6.h
include/net/mac80211.h
include/net/net_namespace.h
include/net/netfilter/nf_conntrack.h
include/net/netfilter/nf_conntrack_core.h
include/net/netfilter/nf_conntrack_expect.h
include/net/netfilter/nf_log.h
include/net/netns/ipv6.h
include/net/netns/netfilter.h [new file with mode: 0644]
include/net/netprio_cgroup.h
include/net/request_sock.h
include/net/rtnetlink.h
include/net/scm.h
include/net/sctp/constants.h
include/net/sctp/structs.h
include/net/sctp/ulpqueue.h
include/net/sctp/user.h [deleted file]
include/net/sock.h
include/net/tcp.h
include/net/xfrm.h
include/uapi/asm-generic/socket.h
include/uapi/linux/Kbuild
include/uapi/linux/cn_proc.h
include/uapi/linux/filter.h
include/uapi/linux/if_ether.h
include/uapi/linux/if_link.h
include/uapi/linux/if_packet.h
include/uapi/linux/neighbour.h
include/uapi/linux/netfilter/xt_NFQUEUE.h
include/uapi/linux/netfilter_ipv6/ip6t_frag.h
include/uapi/linux/netlink.h
include/uapi/linux/netlink_diag.h [new file with mode: 0644]
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
include/uapi/linux/openvswitch.h [new file with mode: 0644]
include/uapi/linux/pkt_sched.h
include/uapi/linux/sctp.h [new file with mode: 0644]
include/uapi/linux/snmp.h
include/uapi/linux/tcp.h
include/uapi/linux/vm_sockets.h
include/uapi/linux/xfrm.h
include/xen/interface/io/netif.h
kernel/audit.c
kernel/signal.c
net/802/garp.c
net/8021q/Kconfig
net/8021q/vlan.c
net/8021q/vlan.h
net/8021q/vlan_core.c
net/8021q/vlan_dev.c
net/8021q/vlan_gvrp.c
net/8021q/vlan_mvrp.c
net/8021q/vlan_netlink.c
net/Kconfig
net/atm/lec.h
net/batman-adv/Kconfig
net/batman-adv/Makefile
net/batman-adv/bat_iv_ogm.c
net/batman-adv/bridge_loop_avoidance.c
net/batman-adv/debugfs.c
net/batman-adv/distributed-arp-table.c
net/batman-adv/gateway_client.c
net/batman-adv/hard-interface.c
net/batman-adv/hard-interface.h
net/batman-adv/main.c
net/batman-adv/main.h
net/batman-adv/network-coding.c [new file with mode: 0644]
net/batman-adv/network-coding.h [new file with mode: 0644]
net/batman-adv/originator.c
net/batman-adv/packet.h
net/batman-adv/routing.c
net/batman-adv/send.c
net/batman-adv/soft-interface.c
net/batman-adv/soft-interface.h
net/batman-adv/sysfs.c
net/batman-adv/translation-table.c
net/batman-adv/types.h
net/batman-adv/unicast.c
net/batman-adv/vis.c
net/bluetooth/a2mp.c
net/bluetooth/af_bluetooth.c
net/bluetooth/bnep/netdev.c
net/bluetooth/bnep/sock.c
net/bluetooth/cmtp/sock.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/hci_sysfs.c
net/bluetooth/hidp/core.c
net/bluetooth/hidp/sock.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/sco.c
net/bridge/br_device.c
net/bridge/br_fdb.c
net/bridge/br_if.c
net/bridge/br_mdb.c
net/bridge/br_multicast.c
net/bridge/br_netfilter.c
net/bridge/br_netlink.c
net/bridge/br_vlan.c
net/bridge/netfilter/ebt_log.c
net/bridge/netfilter/ebt_nflog.c
net/bridge/netfilter/ebt_ulog.c
net/bridge/netfilter/ebtable_broute.c
net/bridge/netfilter/ebtables.c
net/caif/caif_dev.c
net/caif/caif_socket.c
net/caif/caif_usb.c
net/caif/cfcnfg.c
net/caif/cfctrl.c
net/caif/cffrml.c
net/caif/cfmuxl.c
net/caif/cfpkt_skbuff.c
net/caif/cfrfml.c
net/caif/cfserl.c
net/caif/cfsrvl.c
net/caif/chnl_net.c
net/can/af_can.c
net/can/gw.c
net/can/raw.c
net/core/datagram.c
net/core/dev.c
net/core/dev_addr_lists.c
net/core/dst.c
net/core/ethtool.c
net/core/fib_rules.c
net/core/filter.c
net/core/flow.c
net/core/flow_dissector.c
net/core/neighbour.c
net/core/net-procfs.c
net/core/netpoll.c
net/core/rtnetlink.c
net/core/scm.c
net/core/skbuff.c
net/core/sock.c
net/core/utils.c
net/dcb/dcbevent.c
net/dcb/dcbnl.c
net/dccp/ipv4.c
net/dccp/ipv6.c
net/decnet/dn_dev.c
net/decnet/dn_fib.c
net/decnet/dn_route.c
net/decnet/dn_table.c
net/decnet/netfilter/dn_rtmsg.c
net/dsa/dsa.c
net/ethernet/eth.c
net/ieee802154/6lowpan.c
net/ieee802154/6lowpan.h
net/ieee802154/dgram.c
net/ieee802154/netlink.c
net/ieee802154/nl-mac.c
net/ipv4/Kconfig
net/ipv4/Makefile
net/ipv4/af_inet.c
net/ipv4/arp.c
net/ipv4/devinet.c
net/ipv4/fib_frontend.c
net/ipv4/gre.c
net/ipv4/inet_connection_sock.c
net/ipv4/inet_diag.c
net/ipv4/inet_fragment.c
net/ipv4/inet_lro.c
net/ipv4/ip_fragment.c
net/ipv4/ip_gre.c
net/ipv4/ip_output.c
net/ipv4/ip_tunnel.c [new file with mode: 0644]
net/ipv4/ip_vti.c
net/ipv4/ipcomp.c
net/ipv4/ipconfig.c
net/ipv4/ipip.c
net/ipv4/ipmr.c
net/ipv4/netfilter.c
net/ipv4/netfilter/Kconfig
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/arptable_filter.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_ULOG.c
net/ipv4/netfilter/iptable_mangle.c
net/ipv4/netfilter/iptable_nat.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
net/ipv4/netfilter/nf_conntrack_proto_icmp.c
net/ipv4/netfilter/nf_nat_h323.c
net/ipv4/netfilter/nf_nat_pptp.c
net/ipv4/netfilter/nf_nat_proto_gre.c
net/ipv4/netfilter/nf_nat_snmp_basic.c
net/ipv4/ping.c
net/ipv4/proc.c
net/ipv4/route.c
net/ipv4/syncookies.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_memcontrol.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_output.c
net/ipv4/tcp_timer.c
net/ipv4/tcp_westwood.c
net/ipv4/udp.c
net/ipv4/udp_diag.c
net/ipv4/xfrm4_mode_tunnel.c
net/ipv6/Kconfig
net/ipv6/addrconf.c
net/ipv6/addrlabel.c
net/ipv6/af_inet6.c
net/ipv6/datagram.c
net/ipv6/icmp.c
net/ipv6/inet6_connection_sock.c
net/ipv6/ip6_flowlabel.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_offload.c
net/ipv6/ip6_output.c
net/ipv6/ip6_tunnel.c
net/ipv6/ip6mr.c
net/ipv6/ndisc.c
net/ipv6/netfilter.c
net/ipv6/netfilter/Kconfig
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/ip6t_NPT.c
net/ipv6/netfilter/ip6t_REJECT.c
net/ipv6/netfilter/ip6table_mangle.c
net/ipv6/netfilter/ip6table_nat.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/raw.c
net/ipv6/reassembly.c
net/ipv6/route.c
net/ipv6/sit.c
net/ipv6/syncookies.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/ipv6/udp_offload.c
net/ipv6/xfrm6_mode_tunnel.c
net/irda/af_irda.c
net/irda/ircomm/ircomm_core.c
net/iucv/af_iucv.c
net/l2tp/l2tp_core.c
net/mac80211/cfg.c
net/mac80211/debugfs_key.c
net/mac80211/debugfs_netdev.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/pm.c
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel.h
net/mac80211/rc80211_minstrel_debugfs.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_minstrel_ht.h
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac802154/mac802154.h
net/mac802154/mac_cmd.c
net/mac802154/mib.c
net/mac802154/tx.c
net/mac802154/wpan.c
net/netfilter/core.c
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipvs/ip_vs_app.c
net/netfilter/ipvs/ip_vs_conn.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_dh.c
net/netfilter/ipvs/ip_vs_est.c
net/netfilter/ipvs/ip_vs_ftp.c
net/netfilter/ipvs/ip_vs_lblc.c
net/netfilter/ipvs/ip_vs_lblcr.c
net/netfilter/ipvs/ip_vs_lc.c
net/netfilter/ipvs/ip_vs_nq.c
net/netfilter/ipvs/ip_vs_pe.c
net/netfilter/ipvs/ip_vs_pe_sip.c
net/netfilter/ipvs/ip_vs_proto_sctp.c
net/netfilter/ipvs/ip_vs_proto_tcp.c
net/netfilter/ipvs/ip_vs_proto_udp.c
net/netfilter/ipvs/ip_vs_rr.c
net/netfilter/ipvs/ip_vs_sched.c
net/netfilter/ipvs/ip_vs_sed.c
net/netfilter/ipvs/ip_vs_sh.c
net/netfilter/ipvs/ip_vs_sync.c
net/netfilter/ipvs/ip_vs_wlc.c
net/netfilter/ipvs/ip_vs_wrr.c
net/netfilter/ipvs/ip_vs_xmit.c
net/netfilter/nf_conntrack_amanda.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_ecache.c
net/netfilter/nf_conntrack_expect.c
net/netfilter/nf_conntrack_ftp.c
net/netfilter/nf_conntrack_h323_main.c
net/netfilter/nf_conntrack_helper.c
net/netfilter/nf_conntrack_irc.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_pptp.c
net/netfilter/nf_conntrack_proto.c
net/netfilter/nf_conntrack_proto_dccp.c
net/netfilter/nf_conntrack_proto_gre.c
net/netfilter/nf_conntrack_proto_sctp.c
net/netfilter/nf_conntrack_proto_tcp.c
net/netfilter/nf_conntrack_proto_udp.c
net/netfilter/nf_conntrack_proto_udplite.c
net/netfilter/nf_conntrack_standalone.c
net/netfilter/nf_conntrack_tftp.c
net/netfilter/nf_log.c
net/netfilter/nf_nat_amanda.c
net/netfilter/nf_nat_core.c
net/netfilter/nf_nat_helper.c
net/netfilter/nf_queue.c
net/netfilter/nfnetlink.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue_core.c
net/netfilter/x_tables.c
net/netfilter/xt_LOG.c
net/netfilter/xt_NFQUEUE.c
net/netfilter/xt_TCPMSS.c
net/netfilter/xt_conntrack.c
net/netfilter/xt_hashlimit.c
net/netfilter/xt_limit.c
net/netfilter/xt_osf.c
net/netlink/Kconfig [new file with mode: 0644]
net/netlink/Makefile
net/netlink/af_netlink.c
net/netlink/af_netlink.h [new file with mode: 0644]
net/netlink/diag.c [new file with mode: 0644]
net/nfc/llcp/commands.c
net/nfc/llcp/llcp.c
net/nfc/llcp/llcp.h
net/nfc/llcp/sock.c
net/nfc/netlink.c
net/nfc/nfc.h
net/openvswitch/actions.c
net/openvswitch/datapath.c
net/openvswitch/datapath.h
net/openvswitch/dp_notify.c
net/openvswitch/flow.c
net/openvswitch/flow.h
net/openvswitch/vport-internal_dev.c
net/openvswitch/vport-netdev.c
net/openvswitch/vport.c
net/openvswitch/vport.h
net/packet/af_packet.c
net/packet/internal.h
net/phonet/pn_netlink.c
net/rfkill/rfkill-regulator.c
net/sched/act_api.c
net/sched/act_csum.c
net/sched/cls_api.c
net/sched/cls_flow.c
net/sched/sch_api.c
net/sched/sch_htb.c
net/sctp/associola.c
net/sctp/bind_addr.c
net/sctp/endpointola.c
net/sctp/inqueue.c
net/sctp/output.c
net/sctp/outqueue.c
net/sctp/proc.c
net/sctp/socket.c
net/sctp/ssnmap.c
net/sctp/transport.c
net/sctp/ulpqueue.c
net/socket.c
net/tipc/Kconfig
net/tipc/Makefile
net/tipc/bcast.c
net/tipc/bearer.c
net/tipc/bearer.h
net/tipc/core.c
net/tipc/discover.c
net/tipc/eth_media.c
net/tipc/ib_media.c [new file with mode: 0644]
net/tipc/netlink.c
net/unix/af_unix.c
net/vmw_vsock/vmci_transport.c
net/vmw_vsock/vmci_transport.h
net/wireless/ap.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/rdev-ops.h
net/wireless/reg.c
net/wireless/sme.c
net/wireless/sysfs.c
net/wireless/trace.h
net/wireless/util.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_state.c
net/xfrm/xfrm_user.c
security/selinux/hooks.c
security/selinux/netlink.c
tools/Makefile
tools/net/Makefile [new file with mode: 0644]
tools/net/bpf_jit_disasm.c [new file with mode: 0644]
tools/testing/selftests/Makefile
tools/testing/selftests/net/.gitignore [new file with mode: 0644]
tools/testing/selftests/net/Makefile [new file with mode: 0644]
tools/testing/selftests/net/psock_fanout.c [new file with mode: 0644]
tools/testing/selftests/net/psock_lib.h [new file with mode: 0644]
tools/testing/selftests/net/psock_tpacket.c [new file with mode: 0644]
tools/testing/selftests/net/run_afpackettests [new file with mode: 0644]
tools/testing/selftests/net/run_netsocktests [new file with mode: 0644]
tools/testing/selftests/net/socket.c [new file with mode: 0644]

index bc41da61608d5dfc22e0c2836aa00c56499c9d98..bdcd8b4e38f2dd404acdc36bcd123f72ff5d2ed3 100644 (file)
@@ -67,6 +67,14 @@ Description:
                 Defines the penalty which will be applied to an
                 originator message's tq-field on every hop.
 
+What:           /sys/class/net/<mesh_iface>/mesh/network_coding
+Date:           Nov 2012
+Contact:        Martin Hundeboll <martin@hundeboll.net>
+Description:
+                Controls whether Network Coding (using some magic
+                to send fewer wifi packets but still the same
+                content) is enabled or not.
+
 What:           /sys/class/net/<mesh_iface>/mesh/orig_interval
 Date:           May 2010
 Contact:        Marek Lindner <lindner_marek@yahoo.de>
index 284ced7a228f6bf2cb0dac5daec5bdc84f3e2bde..0f6a3edcd44b5c35b0bc2fa00560a0f942926c05 100644 (file)
       </section>
 !Finclude/net/mac80211.h ieee80211_get_buffered_bc
 !Finclude/net/mac80211.h ieee80211_beacon_get
-!Finclude/net/mac80211.h ieee80211_sta_eosp_irqsafe
+!Finclude/net/mac80211.h ieee80211_sta_eosp
 !Finclude/net/mac80211.h ieee80211_frame_release_type
 !Finclude/net/mac80211.h ieee80211_sta_ps_transition
 !Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni
index f5635a09c3f68e9e99c4446ac6efcd9b8b6b773a..bc461b6425a7eddb03898362ef2bee5f55a84a69 100644 (file)
@@ -18,6 +18,8 @@ memcg_test.txt
        - Memory Resource Controller; implementation details.
 memory.txt
        - Memory Resource Controller; design, accounting, interface, testing.
+net_cls.txt
+       - Network classifier cgroups details and usages.
 net_prio.txt
        - Network priority cgroups details and usages.
 resource_counter.txt
diff --git a/Documentation/cgroups/net_cls.txt b/Documentation/cgroups/net_cls.txt
new file mode 100644 (file)
index 0000000..9face6b
--- /dev/null
@@ -0,0 +1,34 @@
+Network classifier cgroup
+-------------------------
+
+The Network classifier cgroup provides an interface to
+tag network packets with a class identifier (classid).
+
+The Traffic Controller (tc) can be used to assign
+different priorities to packets from different cgroups.
+
+Creating a net_cls cgroups instance creates a net_cls.classid file.
+This net_cls.classid value is initialized to 0.
+
+You can write hexadecimal values to net_cls.classid; the format for these
+values is 0xAAAABBBB; AAAA is the major handle number and BBBB
+is the minor handle number.
+Reading net_cls.classid yields a decimal result.
+
+Example:
+mkdir /sys/fs/cgroup/net_cls
+mount -t cgroup -onet_cls net_cls /sys/fs/cgroup/net_cls
+mkdir /sys/fs/cgroup/net_cls/0
+echo 0x100001 >  /sys/fs/cgroup/net_cls/0/net_cls.classid
+       - setting a 10:1 handle.
+
+cat /sys/fs/cgroup/net_cls/0/net_cls.classid
+1048577
+
+configuring tc:
+tc qdisc add dev eth0 root handle 10: htb
+
+tc class add dev eth0 parent 10: classid 10:1 htb rate 40mbit
+ - creating traffic class 10:1
+
+tc filter add dev eth0 parent 10: protocol ip prio 10 handle 1: cgroup
index f1533d91953a9fe160145f58da488ef979492ac8..f7a0da6b4022564d24ce47e0a27fde0e4a04b5b4 100644 (file)
@@ -115,6 +115,9 @@ prefixed with the string "marvell,", for Marvell Technology Group Ltd.
      - compatible : "marvell,mv64360-eth-block"
      - reg : Offset and length of the register set for this block
 
+   Optional properties:
+     - clocks : Phandle to the clock control device and gate bit
+
    Example Discovery Ethernet block node:
      ethernet-block@2000 {
             #address-cells = <1>;
diff --git a/Documentation/devicetree/bindings/net/can/atmel-can.txt b/Documentation/devicetree/bindings/net/can/atmel-can.txt
new file mode 100644 (file)
index 0000000..72cf0c5
--- /dev/null
@@ -0,0 +1,14 @@
+* AT91 CAN *
+
+Required properties:
+  - compatible: Should be "atmel,at91sam9263-can" or "atmel,at91sam9x5-can"
+  - reg: Should contain CAN controller registers location and length
+  - interrupts: Should contain IRQ line for the CAN controller
+
+Example:
+
+       can0: can@f000c000 {
+               compatbile = "atmel,at91sam9x5-can";
+               reg = <0xf000c000 0x300>;
+               interrupts = <40 4 5>
+       };
index ecfdf756d10fb31b05dcf161ebe6ddfa4a5c31c1..4f2ca6b4a182d3f9584c72b05414deacb085aebf 100644 (file)
@@ -15,16 +15,22 @@ Required properties:
 - mac_control          : Specifies Default MAC control register content
                          for the specific platform
 - slaves               : Specifies number for slaves
-- cpts_active_slave    : Specifies the slave to use for time stamping
+- active_slave         : Specifies the slave to use for time stamping,
+                         ethtool and SIOCGMIIPHY
 - cpts_clock_mult      : Numerator to convert input clock ticks into nanoseconds
 - cpts_clock_shift     : Denominator to convert input clock ticks into nanoseconds
-- phy_id               : Specifies slave phy id
-- mac-address          : Specifies slave MAC address
 
 Optional properties:
 - ti,hwmods            : Must be "cpgmac0"
 - no_bd_ram            : Must be 0 or 1
 - dual_emac            : Specifies Switch to act as Dual EMAC
+
+Slave Properties:
+Required properties:
+- phy_id               : Specifies slave phy id
+- mac-address          : Specifies slave MAC address
+
+Optional properties:
 - dual_emac_res_vlan   : Specifies VID to be used to segregate the ports
 
 Note: "ti,hwmods" field is used to fetch the base address and irq
@@ -47,7 +53,7 @@ Examples:
                rx_descs = <64>;
                mac_control = <0x20>;
                slaves = <2>;
-               cpts_active_slave = <0>;
+               active_slave = <0>;
                cpts_clock_mult = <0x80000000>;
                cpts_clock_shift = <29>;
                cpsw_emac0: slave@0 {
@@ -73,7 +79,7 @@ Examples:
                rx_descs = <64>;
                mac_control = <0x20>;
                slaves = <2>;
-               cpts_active_slave = <0>;
+               active_slave = <0>;
                cpts_clock_mult = <0x80000000>;
                cpts_clock_shift = <29>;
                cpsw_emac0: slave@0 {
diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.txt b/Documentation/devicetree/bindings/net/dsa/dsa.txt
new file mode 100644 (file)
index 0000000..49f4f7a
--- /dev/null
@@ -0,0 +1,91 @@
+Marvell Distributed Switch Architecture Device Tree Bindings
+------------------------------------------------------------
+
+Required properties:
+- compatible           : Should be "marvell,dsa"
+- #address-cells       : Must be 2, first cell is the address on the MDIO bus
+                         and second cell is the address in the switch tree.
+                         Second cell is used only when cascading/chaining.
+- #size-cells          : Must be 0
+- dsa,ethernet         : Should be a phandle to a valid Ethernet device node
+- dsa,mii-bus          : Should be a phandle to a valid MDIO bus device node
+
+Optionnal properties:
+- interrupts           : property with a value describing the switch
+                         interrupt number (not supported by the driver)
+
+A DSA node can contain multiple switch chips which are therefore child nodes of
+the parent DSA node. The maximum number of allowed child nodes is 4
+(DSA_MAX_SWITCHES).
+Each of these switch child nodes should have the following required properties:
+
+- reg                  : Describes the switch address on the MII bus
+- #address-cells       : Must be 1
+- #size-cells          : Must be 0
+
+A switch may have multiple "port" children nodes
+
+Each port children node must have the following mandatory properties:
+- reg                  : Describes the port address in the switch
+- label                        : Describes the label associated with this port, special
+                         labels are "cpu" to indicate a CPU port and "dsa" to
+                         indicate an uplink/downlink port.
+
+Note that a port labelled "dsa" will imply checking for the uplink phandle
+described below.
+
+Optionnal property:
+- link                 : Should be a phandle to another switch's DSA port.
+                         This property is only used when switches are being
+                         chained/cascaded together.
+
+Example:
+
+       dsa@0 {
+               compatible = "marvell,dsa";
+               #address-cells = <2>;
+               #size-cells = <0>;
+
+               interrupts = <10>;
+               dsa,ethernet = <&ethernet0>;
+               dsa,mii-bus = <&mii_bus0>;
+
+               switch@0 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <16 0>;   /* MDIO address 16, switch 0 in tree */
+
+                       port@0 {
+                               reg = <0>;
+                               label = "lan1";
+                       };
+
+                       port@1 {
+                               reg = <1>;
+                               label = "lan2";
+                       };
+
+                       port@5 {
+                               reg = <5>;
+                               label = "cpu";
+                       };
+
+                       switch0uplink: port@6 {
+                               reg = <6>;
+                               label = "dsa";
+                               link = <&switch1uplink>;
+                       };
+               };
+
+               switch@1 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <17 1>;   /* MDIO address 17, switch 1 in tree */
+
+                       switch1uplink: port@0 {
+                               reg = <0>;
+                               label = "dsa";
+                               link = <&switch0uplink>;
+                       };
+               };
+       };
index 34e7aafa321c126af500ce998339db8259496d2f..9417e54c26c0eba82004047d06fda4f3dd4d02b8 100644 (file)
@@ -9,6 +9,10 @@ Required properties:
 - compatible: "marvell,orion-mdio"
 - reg: address and length of the SMI register
 
+Optional properties:
+- interrupts: interrupt line number for the SMI error/done interrupt
+- clocks: Phandle to the clock control device and gate bit
+
 The child nodes of the MDIO driver are the individual PHY devices
 connected to this MDIO bus. They must have a "reg" property given the
 PHY address on the MDIO bus.
index 703cf4370c7903c2d4c405d5eb98367f195233ae..67a9cb259d402a9bb9a557865df16ce51e6156b4 100644 (file)
@@ -71,8 +71,9 @@ submits skb to qdisc), so if you need something from that cb later, you should
 store info in the skb->data on your own.
 
 To hook the MLME interface you have to populate the ml_priv field of your
-net_device with a pointer to struct ieee802154_mlme_ops instance. All fields are
-required.
+net_device with a pointer to struct ieee802154_mlme_ops instance. The fields
+assoc_req, assoc_resp, disassoc_req, start_req, and scan_req are optional.
+All other fields are required.
 
 We provide an example of simple HardMAC driver at drivers/ieee802154/fakehard.c
 
index dc2dc87d2557b4fdf7537b9e8f4185965c3dd1ce..f98ca633b5282eb838ae1b04f9497e4a4495f198 100644 (file)
@@ -29,7 +29,7 @@ route/max_size - INTEGER
 neigh/default/gc_thresh1 - INTEGER
        Minimum number of entries to keep.  Garbage collector will not
        purge entries if there are fewer than this number.
-       Default: 256
+       Default: 128
 
 neigh/default/gc_thresh3 - INTEGER
        Maximum number of neighbor entries allowed.  Increase this
@@ -175,14 +175,6 @@ tcp_congestion_control - STRING
        is inherited.
        [see setsockopt(listenfd, SOL_TCP, TCP_CONGESTION, "name" ...) ]
 
-tcp_cookie_size - INTEGER
-       Default size of TCP Cookie Transactions (TCPCT) option, that may be
-       overridden on a per socket basis by the TCPCT socket option.
-       Values greater than the maximum (16) are interpreted as the maximum.
-       Values greater than zero and less than the minimum (8) are interpreted
-       as the minimum.  Odd values are interpreted as the next even value.
-       Default: 0 (off).
-
 tcp_dsack - BOOLEAN
        Allows TCP to send "duplicate" SACKs.
 
@@ -190,7 +182,9 @@ tcp_early_retrans - INTEGER
        Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold
        for triggering fast retransmit when the amount of outstanding data is
        small and when no previously unsent data can be transmitted (such
-       that limited transmit could be used).
+       that limited transmit could be used). Also controls the use of
+       Tail loss probe (TLP) that converts RTOs occuring due to tail
+       losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01).
        Possible values:
                0 disables ER
                1 enables ER
@@ -198,7 +192,9 @@ tcp_early_retrans - INTEGER
                  by a fourth of RTT. This mitigates connection falsely
                  recovers when network has a small degree of reordering
                  (less than 3 packets).
-       Default: 2
+               3 enables delayed ER and TLP.
+               4 enables TLP only.
+       Default: 3
 
 tcp_ecn - INTEGER
        Control use of Explicit Congestion Notification (ECN) by TCP.
@@ -229,36 +225,13 @@ tcp_fin_timeout - INTEGER
        Default: 60 seconds
 
 tcp_frto - INTEGER
-       Enables Forward RTO-Recovery (F-RTO) defined in RFC4138.
+       Enables Forward RTO-Recovery (F-RTO) defined in RFC5682.
        F-RTO is an enhanced recovery algorithm for TCP retransmission
-       timeouts.  It is particularly beneficial in wireless environments
-       where packet loss is typically due to random radio interference
-       rather than intermediate router congestion.  F-RTO is sender-side
-       only modification. Therefore it does not require any support from
-       the peer.
-
-       If set to 1, basic version is enabled.  2 enables SACK enhanced
-       F-RTO if flow uses SACK.  The basic version can be used also when
-       SACK is in use though scenario(s) with it exists where F-RTO
-       interacts badly with the packet counting of the SACK enabled TCP
-       flow.
-
-tcp_frto_response - INTEGER
-       When F-RTO has detected that a TCP retransmission timeout was
-       spurious (i.e, the timeout would have been avoided had TCP set a
-       longer retransmission timeout), TCP has several options what to do
-       next. Possible values are:
-               0 Rate halving based; a smooth and conservative response,
-                 results in halved cwnd and ssthresh after one RTT
-               1 Very conservative response; not recommended because even
-                 though being valid, it interacts poorly with the rest of
-                 Linux TCP, halves cwnd and ssthresh immediately
-               2 Aggressive response; undoes congestion control measures
-                 that are now known to be unnecessary (ignoring the
-                 possibility of a lost retransmission that would require
-                 TCP to be more cautious), cwnd and ssthresh are restored
-                 to the values prior timeout
-       Default: 0 (rate halving based)
+       timeouts.  It is particularly beneficial in networks where the
+       RTT fluctuates (e.g., wireless). F-RTO is sender-side only
+       modification. It does not require any support from the peer.
+
+       By default it's enabled with a non-zero value. 0 disables F-RTO.
 
 tcp_keepalive_time - INTEGER
        How often TCP sends out keepalive messages when keepalive is enabled.
diff --git a/Documentation/networking/netlink_mmap.txt b/Documentation/networking/netlink_mmap.txt
new file mode 100644 (file)
index 0000000..1c2dab4
--- /dev/null
@@ -0,0 +1,339 @@
+This file documents how to use memory mapped I/O with netlink.
+
+Author: Patrick McHardy <kaber@trash.net>
+
+Overview
+--------
+
+Memory mapped netlink I/O can be used to increase throughput and decrease
+overhead of unicast receive and transmit operations. Some netlink subsystems
+require high throughput, these are mainly the netfilter subsystems
+nfnetlink_queue and nfnetlink_log, but it can also help speed up large
+dump operations of f.i. the routing database.
+
+Memory mapped netlink I/O used two circular ring buffers for RX and TX which
+are mapped into the processes address space.
+
+The RX ring is used by the kernel to directly construct netlink messages into
+user-space memory without copying them as done with regular socket I/O,
+additionally as long as the ring contains messages no recvmsg() or poll()
+syscalls have to be issued by user-space to get more message.
+
+The TX ring is used to process messages directly from user-space memory, the
+kernel processes all messages contained in the ring using a single sendmsg()
+call.
+
+Usage overview
+--------------
+
+In order to use memory mapped netlink I/O, user-space needs three main changes:
+
+- ring setup
+- conversion of the RX path to get messages from the ring instead of recvmsg()
+- conversion of the TX path to construct messages into the ring
+
+Ring setup is done using setsockopt() to provide the ring parameters to the
+kernel, then a call to mmap() to map the ring into the processes address space:
+
+- setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, &params, sizeof(params));
+- setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, &params, sizeof(params));
+- ring = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
+
+Usage of either ring is optional, but even if only the RX ring is used the
+mapping still needs to be writable in order to update the frame status after
+processing.
+
+Conversion of the reception path involves calling poll() on the file
+descriptor, once the socket is readable the frames from the ring are
+processsed in order until no more messages are available, as indicated by
+a status word in the frame header.
+
+On kernel side, in order to make use of memory mapped I/O on receive, the
+originating netlink subsystem needs to support memory mapped I/O, otherwise
+it will use an allocated socket buffer as usual and the contents will be
+ copied to the ring on transmission, nullifying most of the performance gains.
+Dumps of kernel databases automatically support memory mapped I/O.
+
+Conversion of the transmit path involves changing message contruction to
+use memory from the TX ring instead of (usually) a buffer declared on the
+stack and setting up the frame header approriately. Optionally poll() can
+be used to wait for free frames in the TX ring.
+
+Structured and definitions for using memory mapped I/O are contained in
+<linux/netlink.h>.
+
+RX and TX rings
+----------------
+
+Each ring contains a number of continous memory blocks, containing frames of
+fixed size dependant on the parameters used for ring setup.
+
+Ring:  [ block 0 ]
+               [ frame 0 ]
+               [ frame 1 ]
+       [ block 1 ]
+               [ frame 2 ]
+               [ frame 3 ]
+       ...
+       [ block n ]
+               [ frame 2 * n ]
+               [ frame 2 * n + 1 ]
+
+The blocks are only visible to the kernel, from the point of view of user-space
+the ring just contains the frames in a continous memory zone.
+
+The ring parameters used for setting up the ring are defined as follows:
+
+struct nl_mmap_req {
+       unsigned int    nm_block_size;
+       unsigned int    nm_block_nr;
+       unsigned int    nm_frame_size;
+       unsigned int    nm_frame_nr;
+};
+
+Frames are grouped into blocks, where each block is a continous region of memory
+and holds nm_block_size / nm_frame_size frames. The total number of frames in
+the ring is nm_frame_nr. The following invariants hold:
+
+- frames_per_block = nm_block_size / nm_frame_size
+
+- nm_frame_nr = frames_per_block * nm_block_nr
+
+Some parameters are constrained, specifically:
+
+- nm_block_size must be a multiple of the architectures memory page size.
+  The getpagesize() function can be used to get the page size.
+
+- nm_frame_size must be equal or larger to NL_MMAP_HDRLEN, IOW a frame must be
+  able to hold at least the frame header
+
+- nm_frame_size must be smaller or equal to nm_block_size
+
+- nm_frame_size must be a multiple of NL_MMAP_MSG_ALIGNMENT
+
+- nm_frame_nr must equal the actual number of frames as specified above.
+
+When the kernel can't allocate phsyically continous memory for a ring block,
+it will fall back to use physically discontinous memory. This might affect
+performance negatively, in order to avoid this the nm_frame_size parameter
+should be chosen to be as small as possible for the required frame size and
+the number of blocks should be increased instead.
+
+Ring frames
+------------
+
+Each frames contain a frame header, consisting of a synchronization word and some
+meta-data, and the message itself.
+
+Frame: [ header message ]
+
+The frame header is defined as follows:
+
+struct nl_mmap_hdr {
+       unsigned int    nm_status;
+       unsigned int    nm_len;
+       __u32           nm_group;
+       /* credentials */
+       __u32           nm_pid;
+       __u32           nm_uid;
+       __u32           nm_gid;
+};
+
+- nm_status is used for synchronizing processing between the kernel and user-
+  space and specifies ownership of the frame as well as the operation to perform
+
+- nm_len contains the length of the message contained in the data area
+
+- nm_group specified the destination multicast group of message
+
+- nm_pid, nm_uid and nm_gid contain the netlink pid, UID and GID of the sending
+  process. These values correspond to the data available using SOCK_PASSCRED in
+  the SCM_CREDENTIALS cmsg.
+
+The possible values in the status word are:
+
+- NL_MMAP_STATUS_UNUSED:
+       RX ring:        frame belongs to the kernel and contains no message
+                       for user-space. Approriate action is to invoke poll()
+                       to wait for new messages.
+
+       TX ring:        frame belongs to user-space and can be used for
+                       message construction.
+
+- NL_MMAP_STATUS_RESERVED:
+       RX ring only:   frame is currently used by the kernel for message
+                       construction and contains no valid message yet.
+                       Appropriate action is to invoke poll() to wait for
+                       new messages.
+
+- NL_MMAP_STATUS_VALID:
+       RX ring:        frame contains a valid message. Approriate action is
+                       to process the message and release the frame back to
+                       the kernel by setting the status to
+                       NL_MMAP_STATUS_UNUSED or queue the frame by setting the
+                       status to NL_MMAP_STATUS_SKIP.
+
+       TX ring:        the frame contains a valid message from user-space to
+                       be processed by the kernel. After completing processing
+                       the kernel will release the frame back to user-space by
+                       setting the status to NL_MMAP_STATUS_UNUSED.
+
+- NL_MMAP_STATUS_COPY:
+       RX ring only:   a message is ready to be processed but could not be
+                       stored in the ring, either because it exceeded the
+                       frame size or because the originating subsystem does
+                       not support memory mapped I/O. Appropriate action is
+                       to invoke recvmsg() to receive the message and release
+                       the frame back to the kernel by setting the status to
+                       NL_MMAP_STATUS_UNUSED.
+
+- NL_MMAP_STATUS_SKIP:
+       RX ring only:   user-space queued the message for later processing, but
+                       processed some messages following it in the ring. The
+                       kernel should skip this frame when looking for unused
+                       frames.
+
+The data area of a frame begins at a offset of NL_MMAP_HDRLEN relative to the
+frame header.
+
+TX limitations
+--------------
+
+Kernel processing usually involves validation of the message received by
+user-space, then processing its contents. The kernel must assure that
+userspace is not able to modify the message contents after they have been
+validated. In order to do so, the message is copied from the ring frame
+to an allocated buffer if either of these conditions is false:
+
+- only a single mapping of the ring exists
+- the file descriptor is not shared between processes
+
+This means that for threaded programs, the kernel will fall back to copying.
+
+Example
+-------
+
+Ring setup:
+
+       unsigned int block_size = 16 * getpagesize();
+       struct nl_mmap_req req = {
+               .nm_block_size          = block_size,
+               .nm_block_nr            = 64,
+               .nm_frame_size          = 16384,
+               .nm_frame_nr            = 64 * block_size / 16384,
+       };
+       unsigned int ring_size;
+       void *rx_ring, *tx_ring;
+
+       /* Configure ring parameters */
+       if (setsockopt(fd, NETLINK_RX_RING, &req, sizeof(req)) < 0)
+               exit(1);
+       if (setsockopt(fd, NETLINK_TX_RING, &req, sizeof(req)) < 0)
+               exit(1)
+
+       /* Calculate size of each invididual ring */
+       ring_size = req.nm_block_nr * req.nm_block_size;
+
+       /* Map RX/TX rings. The TX ring is located after the RX ring */
+       rx_ring = mmap(NULL, 2 * ring_size, PROT_READ | PROT_WRITE,
+                      MAP_SHARED, fd, 0);
+       if ((long)rx_ring == -1L)
+               exit(1);
+       tx_ring = rx_ring + ring_size:
+
+Message reception:
+
+This example assumes some ring parameters of the ring setup are available.
+
+       unsigned int frame_offset = 0;
+       struct nl_mmap_hdr *hdr;
+       struct nlmsghdr *nlh;
+       unsigned char buf[16384];
+       ssize_t len;
+
+       while (1) {
+               struct pollfd pfds[1];
+
+               pfds[0].fd      = fd;
+               pfds[0].events  = POLLIN | POLLERR;
+               pfds[0].revents = 0;
+
+               if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
+                       exit(1);
+
+               /* Check for errors. Error handling omitted */
+               if (pfds[0].revents & POLLERR)
+                       <handle error>
+
+               /* If no new messages, poll again */
+               if (!(pfds[0].revents & POLLIN))
+                       continue;
+
+               /* Process all frames */
+               while (1) {
+                       /* Get next frame header */
+                       hdr = rx_ring + frame_offset;
+
+                       if (hdr->nm_status == NL_MMAP_STATUS_VALID)
+                               /* Regular memory mapped frame */
+                               nlh = (void *hdr) + NL_MMAP_HDRLEN;
+                               len = hdr->nm_len;
+
+                               /* Release empty message immediately. May happen
+                                * on error during message construction.
+                                */
+                               if (len == 0)
+                                       goto release;
+                       } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
+                               /* Frame queued to socket receive queue */
+                               len = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
+                               if (len <= 0)
+                                       break;
+                               nlh = buf;
+                       } else
+                               /* No more messages to process, continue polling */
+                               break;
+
+                       process_msg(nlh);
+release:
+                       /* Release frame back to the kernel */
+                       hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+
+                       /* Advance frame offset to next frame */
+                       frame_offset = (frame_offset + frame_size) % ring_size;
+               }
+       }
+
+Message transmission:
+
+This example assumes some ring parameters of the ring setup are available.
+A single message is constructed and transmitted, to send multiple messages
+at once they would be constructed in consecutive frames before a final call
+to sendto().
+
+       unsigned int frame_offset = 0;
+       struct nl_mmap_hdr *hdr;
+       struct nlmsghdr *nlh;
+       struct sockaddr_nl addr = {
+               .nl_family      = AF_NETLINK,
+       };
+
+       hdr = tx_ring + frame_offset;
+       if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
+               /* No frame available. Use poll() to avoid. */
+               exit(1);
+
+       nlh = (void *)hdr + NL_MMAP_HDRLEN;
+
+       /* Build message */
+       build_message(nlh);
+
+       /* Fill frame header: length and status need to be set */
+       hdr->nm_len     = nlh->nlmsg_len;
+       hdr->nm_status  = NL_MMAP_STATUS_VALID;
+
+       if (sendto(fd, NULL, 0, 0, &addr, sizeof(addr)) < 0)
+               exit(1);
+
+       /* Advance frame offset to next frame */
+       frame_offset = (frame_offset + frame_size) % ring_size;
index 94444b152fbcc7d56a3549a3578ededb357cf956..65efb85e49de0c8de65ca64720cf07b596e8cfdf 100644 (file)
@@ -684,6 +684,333 @@ int main(int argc, char **argp)
        return 0;
 }
 
+-------------------------------------------------------------------------------
++ AF_PACKET TPACKET_V3 example
+-------------------------------------------------------------------------------
+
+AF_PACKET's TPACKET_V3 ring buffer can be configured to use non-static frame
+sizes by doing it's own memory management. It is based on blocks where polling
+works on a per block basis instead of per ring as in TPACKET_V2 and predecessor.
+
+It is said that TPACKET_V3 brings the following benefits:
+ *) ~15 - 20% reduction in CPU-usage
+ *) ~20% increase in packet capture rate
+ *) ~2x increase in packet density
+ *) Port aggregation analysis
+ *) Non static frame size to capture entire packet payload
+
+So it seems to be a good candidate to be used with packet fanout.
+
+Minimal example code by Daniel Borkmann based on Chetan Loke's lolpcap (compile
+it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.):
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <poll.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+#define BLOCK_SIZE             (1 << 22)
+#define FRAME_SIZE             2048
+
+#define NUM_BLOCKS             64
+#define NUM_FRAMES             ((BLOCK_SIZE * NUM_BLOCKS) / FRAME_SIZE)
+
+#define BLOCK_RETIRE_TOV_IN_MS 64
+#define BLOCK_PRIV_AREA_SZ     13
+
+#define ALIGN_8(x)             (((x) + 8 - 1) & ~(8 - 1))
+
+#define BLOCK_STATUS(x)                ((x)->h1.block_status)
+#define BLOCK_NUM_PKTS(x)      ((x)->h1.num_pkts)
+#define BLOCK_O2FP(x)          ((x)->h1.offset_to_first_pkt)
+#define BLOCK_LEN(x)           ((x)->h1.blk_len)
+#define BLOCK_SNUM(x)          ((x)->h1.seq_num)
+#define BLOCK_O2PRIV(x)                ((x)->offset_to_priv)
+#define BLOCK_PRIV(x)          ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x)))
+#define BLOCK_HDR_LEN          (ALIGN_8(sizeof(struct block_desc)))
+#define BLOCK_PLUS_PRIV(sz_pri)        (BLOCK_HDR_LEN + ALIGN_8((sz_pri)))
+
+#ifndef likely
+# define likely(x)             __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
+# define unlikely(x)           __builtin_expect(!!(x), 0)
+#endif
+
+struct block_desc {
+       uint32_t version;
+       uint32_t offset_to_priv;
+       struct tpacket_hdr_v1 h1;
+};
+
+struct ring {
+       struct iovec *rd;
+       uint8_t *map;
+       struct tpacket_req3 req;
+};
+
+static unsigned long packets_total = 0, bytes_total = 0;
+static sig_atomic_t sigint = 0;
+
+void sighandler(int num)
+{
+       sigint = 1;
+}
+
+static int setup_socket(struct ring *ring, char *netdev)
+{
+       int err, i, fd, v = TPACKET_V3;
+       struct sockaddr_ll ll;
+
+       fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+       if (fd < 0) {
+               perror("socket");
+               exit(1);
+       }
+
+       err = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
+       if (err < 0) {
+               perror("setsockopt");
+               exit(1);
+       }
+
+       memset(&ring->req, 0, sizeof(ring->req));
+       ring->req.tp_block_size = BLOCK_SIZE;
+       ring->req.tp_frame_size = FRAME_SIZE;
+       ring->req.tp_block_nr = NUM_BLOCKS;
+       ring->req.tp_frame_nr = NUM_FRAMES;
+       ring->req.tp_retire_blk_tov = BLOCK_RETIRE_TOV_IN_MS;
+       ring->req.tp_sizeof_priv = BLOCK_PRIV_AREA_SZ;
+       ring->req.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH;
+
+       err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req,
+                        sizeof(ring->req));
+       if (err < 0) {
+               perror("setsockopt");
+               exit(1);
+       }
+
+       ring->map = mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr,
+                        PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
+                        fd, 0);
+       if (ring->map == MAP_FAILED) {
+               perror("mmap");
+               exit(1);
+       }
+
+       ring->rd = malloc(ring->req.tp_block_nr * sizeof(*ring->rd));
+       assert(ring->rd);
+       for (i = 0; i < ring->req.tp_block_nr; ++i) {
+               ring->rd[i].iov_base = ring->map + (i * ring->req.tp_block_size);
+               ring->rd[i].iov_len = ring->req.tp_block_size;
+       }
+
+       memset(&ll, 0, sizeof(ll));
+       ll.sll_family = PF_PACKET;
+       ll.sll_protocol = htons(ETH_P_ALL);
+       ll.sll_ifindex = if_nametoindex(netdev);
+       ll.sll_hatype = 0;
+       ll.sll_pkttype = 0;
+       ll.sll_halen = 0;
+
+       err = bind(fd, (struct sockaddr *) &ll, sizeof(ll));
+       if (err < 0) {
+               perror("bind");
+               exit(1);
+       }
+
+       return fd;
+}
+
+#ifdef __checked
+static uint64_t prev_block_seq_num = 0;
+
+void assert_block_seq_num(struct block_desc *pbd)
+{
+       if (unlikely(prev_block_seq_num + 1 != BLOCK_SNUM(pbd))) {
+               printf("prev_block_seq_num:%"PRIu64", expected seq:%"PRIu64" != "
+                      "actual seq:%"PRIu64"\n", prev_block_seq_num,
+                      prev_block_seq_num + 1, (uint64_t) BLOCK_SNUM(pbd));
+               exit(1);
+       }
+
+       prev_block_seq_num = BLOCK_SNUM(pbd);
+}
+
+static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num)
+{
+       if (BLOCK_NUM_PKTS(pbd)) {
+               if (unlikely(bytes != BLOCK_LEN(pbd))) {
+                       printf("block:%u with %upackets, expected len:%u != actual len:%u\n",
+                              block_num, BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd));
+                       exit(1);
+               }
+       } else {
+               if (unlikely(BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ))) {
+                       printf("block:%u, expected len:%lu != actual len:%u\n",
+                              block_num, BLOCK_HDR_LEN, BLOCK_LEN(pbd));
+                       exit(1);
+               }
+       }
+}
+
+static void assert_block_header(struct block_desc *pbd, const int block_num)
+{
+       uint32_t block_status = BLOCK_STATUS(pbd);
+
+       if (unlikely((block_status & TP_STATUS_USER) == 0)) {
+               printf("block:%u, not in TP_STATUS_USER\n", block_num);
+               exit(1);
+       }
+
+       assert_block_seq_num(pbd);
+}
+#else
+static inline void assert_block_header(struct block_desc *pbd, const int block_num)
+{
+}
+static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num)
+{
+}
+#endif
+
+static void display(struct tpacket3_hdr *ppd)
+{
+       struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac);
+       struct iphdr *ip = (struct iphdr *) ((uint8_t *) eth + ETH_HLEN);
+
+       if (eth->h_proto == htons(ETH_P_IP)) {
+               struct sockaddr_in ss, sd;
+               char sbuff[NI_MAXHOST], dbuff[NI_MAXHOST];
+
+               memset(&ss, 0, sizeof(ss));
+               ss.sin_family = PF_INET;
+               ss.sin_addr.s_addr = ip->saddr;
+               getnameinfo((struct sockaddr *) &ss, sizeof(ss),
+                           sbuff, sizeof(sbuff), NULL, 0, NI_NUMERICHOST);
+
+               memset(&sd, 0, sizeof(sd));
+               sd.sin_family = PF_INET;
+               sd.sin_addr.s_addr = ip->daddr;
+               getnameinfo((struct sockaddr *) &sd, sizeof(sd),
+                           dbuff, sizeof(dbuff), NULL, 0, NI_NUMERICHOST);
+
+               printf("%s -> %s, ", sbuff, dbuff);
+       }
+
+       printf("rxhash: 0x%x\n", ppd->hv1.tp_rxhash);
+}
+
+static void walk_block(struct block_desc *pbd, const int block_num)
+{
+       int num_pkts = BLOCK_NUM_PKTS(pbd), i;
+       unsigned long bytes = 0;
+       unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ);
+       struct tpacket3_hdr *ppd;
+
+       assert_block_header(pbd, block_num);
+
+       ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd));
+       for (i = 0; i < num_pkts; ++i) {
+               bytes += ppd->tp_snaplen;
+               if (ppd->tp_next_offset)
+                       bytes_with_padding += ppd->tp_next_offset;
+               else
+                       bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac);
+
+               display(ppd);
+
+               ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset);
+               __sync_synchronize();
+       }
+
+       assert_block_len(pbd, bytes_with_padding, block_num);
+
+       packets_total += num_pkts;
+       bytes_total += bytes;
+}
+
+void flush_block(struct block_desc *pbd)
+{
+       BLOCK_STATUS(pbd) = TP_STATUS_KERNEL;
+       __sync_synchronize();
+}
+
+static void teardown_socket(struct ring *ring, int fd)
+{
+       munmap(ring->map, ring->req.tp_block_size * ring->req.tp_block_nr);
+       free(ring->rd);
+       close(fd);
+}
+
+int main(int argc, char **argp)
+{
+       int fd, err;
+       socklen_t len;
+       struct ring ring;
+       struct pollfd pfd;
+       unsigned int block_num = 0;
+       struct block_desc *pbd;
+       struct tpacket_stats_v3 stats;
+
+       if (argc != 2) {
+               fprintf(stderr, "Usage: %s INTERFACE\n", argp[0]);
+               return EXIT_FAILURE;
+       }
+
+       signal(SIGINT, sighandler);
+
+       memset(&ring, 0, sizeof(ring));
+       fd = setup_socket(&ring, argp[argc - 1]);
+       assert(fd > 0);
+
+       memset(&pfd, 0, sizeof(pfd));
+       pfd.fd = fd;
+       pfd.events = POLLIN | POLLERR;
+       pfd.revents = 0;
+
+       while (likely(!sigint)) {
+               pbd = (struct block_desc *) ring.rd[block_num].iov_base;
+retry_block:
+               if ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) {
+                       poll(&pfd, 1, -1);
+                       goto retry_block;
+               }
+
+               walk_block(pbd, block_num);
+               flush_block(pbd);
+               block_num = (block_num + 1) % NUM_BLOCKS;
+       }
+
+       len = sizeof(stats);
+       err = getsockopt(fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len);
+       if (err < 0) {
+               perror("getsockopt");
+               exit(1);
+       }
+
+       fflush(stdout);
+       printf("\nReceived %u packets, %lu bytes, %u dropped, freeze_q_cnt: %u\n",
+              stats.tp_packets, bytes_total, stats.tp_drops,
+              stats.tp_freeze_q_cnt);
+
+       teardown_socket(&ring, fd);
+       return 0;
+}
+
 -------------------------------------------------------------------------------
 + PACKET_TIMESTAMP
 -------------------------------------------------------------------------------
index f9fa6db40a5291155d4f39141d82f89b46d0c0b5..654d2e55c8cb126f5a25b21af2ddcb677479ffb9 100644 (file)
@@ -1,6 +1,6 @@
        STMicroelectronics 10/100/1000 Synopsys Ethernet driver
 
-Copyright (C) 2007-2010  STMicroelectronics Ltd
+Copyright (C) 2007-2013  STMicroelectronics Ltd
 Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
 
 This is the driver for the MAC 10/100/1000 on-chip Ethernet controllers
@@ -10,7 +10,7 @@ Currently this network device driver is for all STM embedded MAC/GMAC
 (i.e. 7xxx/5xxx SoCs), SPEAr (arm), Loongson1B (mips) and XLINX XC2V3000
 FF1152AMT0221 D1215994A VIRTEX FPGA board.
 
-DWC Ether MAC 10/100/1000 Universal version 3.60a (and older) and DWC Ether
+DWC Ether MAC 10/100/1000 Universal version 3.70a (and older) and DWC Ether
 MAC 10/100 Universal version 4.0 have been used for developing this driver.
 
 This driver supports both the platform bus and PCI.
@@ -32,6 +32,8 @@ The kernel configuration option is STMMAC_ETH:
        watchdog: transmit timeout (in milliseconds);
        flow_ctrl: Flow control ability [on/off];
        pause: Flow Control Pause Time;
+       eee_timer: tx EEE timer;
+       chain_mode: select chain mode instead of ring.
 
 3) Command line options
 Driver parameters can be also passed in command line by using:
@@ -164,12 +166,12 @@ Where:
  o bus_setup: perform HW setup of the bus. For example, on some ST platforms
             this field is used to configure the AMBA  bridge to generate more
             efficient STBus traffic.
- o init/exit: callbacks used for calling a custom initialisation;
+ o init/exit: callbacks used for calling a custom initialization;
             this is sometime necessary on some platforms (e.g. ST boxes)
             where the HW needs to have set some PIO lines or system cfg
             registers.
  o custom_cfg/custom_data: this is a custom configuration that can be passed
-                          while initialising the resources.
+                          while initializing the resources.
  o bsp_priv: another private poiter.
 
 For MDIO bus The we have:
@@ -273,6 +275,8 @@ reset procedure etc).
  o norm_desc.c: functions for handling normal descriptors;
  o chain_mode.c/ring_mode.c:: functions to manage RING/CHAINED modes;
  o mmc_core.c/mmc.h: Management MAC Counters;
+ o stmmac_hwtstamp.c: HW timestamp support for PTP
+ o stmmac_ptp.c: PTP 1588 clock
 
 5) Debug Information
 
@@ -326,6 +330,35 @@ To enter in Tx LPI mode the driver needs to have a software timer
 that enable and disable the LPI mode when there is nothing to be
 transmitted.
 
-7) TODO:
+7) Extended descriptors
+The extended descriptors give us information about the receive Ethernet payload
+when it is carrying PTP packets or TCP/UDP/ICMP over IP.
+These are not available on GMAC Synopsys chips older than the 3.50.
+At probe time the driver will decide if these can be actually used.
+This support also is mandatory for PTPv2 because the extra descriptors 6 and 7
+are used for saving the hardware timestamps.
+
+8) Precision Time Protocol (PTP)
+The driver supports the IEEE 1588-2002, Precision Time Protocol (PTP),
+which enables precise synchronization of clocks in measurement and
+control systems implemented with technologies such as network
+communication.
+
+In addition to the basic timestamp features mentioned in IEEE 1588-2002
+Timestamps, new GMAC cores support the advanced timestamp features.
+IEEE 1588-2008 that can be enabled when configure the Kernel.
+
+9) SGMII/RGMII supports
+New GMAC devices provide own way to manage RGMII/SGMII.
+This information is available at run-time by looking at the
+HW capability register. This means that the stmmac can manage
+auto-negotiation and link status w/o using the PHYLIB stuff
+In fact, the HW provides a subset of extended registers to
+restart the ANE, verify Full/Half duplex mode and Speed.
+Also thanks to these registers it is possible to look at the
+Auto-negotiated Link Parter Ability.
+
+10) TODO:
  o XGMAC is not supported.
- o Add the PTP - precision time protocol
+ o Complete the TBI & RTBI support.
+ o extened VLAN support for 3.70a SYNP GMAC.
index 8bdd7a7ef2f4687aa26ee66a70fe8ad428e14890..1ee5d119831ee155ff4a501839f0bbd97d721969 100644 (file)
@@ -1764,7 +1764,7 @@ F:        arch/arm/configs/bcm2835_defconfig
 F:     drivers/*/*bcm2835*
 
 BROADCOM TG3 GIGABIT ETHERNET DRIVER
-M:     Matt Carlson <mcarlson@broadcom.com>
+M:     Nithin Nayak Sujir <nsujir@broadcom.com>
 M:     Michael Chan <mchan@broadcom.com>
 L:     netdev@vger.kernel.org
 S:     Supported
@@ -6336,6 +6336,7 @@ F:        drivers/acpi/apei/erst.c
 
 PTP HARDWARE CLOCK SUPPORT
 M:     Richard Cochran <richardcochran@gmail.com>
+L:     netdev@vger.kernel.org
 S:     Maintained
 W:     http://linuxptp.sourceforge.net/
 F:     Documentation/ABI/testing/sysfs-ptp
@@ -6467,6 +6468,7 @@ S:        Supported
 F:     drivers/net/ethernet/qlogic/qlcnic/
 
 QLOGIC QLGE 10Gb ETHERNET DRIVER
+M:     Shahed Shaikh <shahed.shaikh@qlogic.com>
 M:     Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
 M:     Ron Mercer <ron.mercer@qlogic.com>
 M:     linux-driver@qlogic.com
@@ -8520,7 +8522,7 @@ F:        drivers/usb/gadget/*uvc*.c
 F:     drivers/usb/gadget/webcam.c
 
 USB WIRELESS RNDIS DRIVER (rndis_wlan)
-M:     Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
+M:     Jussi Kivilinna <jussi.kivilinna@iki.fi>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
 F:     drivers/net/wireless/rndis_wlan.c
index c5195524d1ef8904af746889e9955271b2ff55c1..eee6ea76bdaff2d5bd2e07583e4e0259e595cda9 100644 (file)
@@ -79,4 +79,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 0957645b73afaca716348485193aa5060f06659c..91fe4f148f80cec2f74f55781dea2e1bf58115f9 100644 (file)
                        rx_descs = <64>;
                        mac_control = <0x20>;
                        slaves = <2>;
-                       cpts_active_slave = <0>;
+                       active_slave = <0>;
                        cpts_clock_mult = <0x80000000>;
                        cpts_clock_shift = <29>;
                        reg = <0x4a100000 0x800
index a0bd8a755bdfc9616c0515116d37dc41bc16e812..1a643ee8e082464108cf9280266fe73016b982be 100644 (file)
@@ -918,9 +918,8 @@ void bpf_jit_compile(struct sk_filter *fp)
 #endif
 
        if (bpf_jit_enable > 1)
-               print_hex_dump(KERN_INFO, "BPF JIT code: ",
-                              DUMP_PREFIX_ADDRESS, 16, 4, ctx.target,
-                              alloc_size, false);
+               /* there are 2 passes here */
+               bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
 
        fp->bpf_func = (void *)ctx.target;
 out:
index 2d4b6414609f6edf2df165ccb14b01527246ad93..251f827271e918adf62b378d59aef9e7272d8b70 100644 (file)
@@ -238,6 +238,7 @@ static __init void ge_complete(
        struct mv643xx_eth_shared_platform_data *orion_ge_shared_data,
        struct resource *orion_ge_resource, unsigned long irq,
        struct platform_device *orion_ge_shared,
+       struct platform_device *orion_ge_mvmdio,
        struct mv643xx_eth_platform_data *eth_data,
        struct platform_device *orion_ge)
 {
@@ -247,6 +248,8 @@ static __init void ge_complete(
        orion_ge->dev.platform_data = eth_data;
 
        platform_device_register(orion_ge_shared);
+       if (orion_ge_mvmdio)
+               platform_device_register(orion_ge_mvmdio);
        platform_device_register(orion_ge);
 }
 
@@ -258,8 +261,6 @@ struct mv643xx_eth_shared_platform_data orion_ge00_shared_data;
 static struct resource orion_ge00_shared_resources[] = {
        {
                .name   = "ge00 base",
-       }, {
-               .name   = "ge00 err irq",
        },
 };
 
@@ -271,6 +272,19 @@ static struct platform_device orion_ge00_shared = {
        },
 };
 
+static struct resource orion_ge_mvmdio_resources[] = {
+       {
+               .name   = "ge00 mvmdio base",
+       }, {
+               .name   = "ge00 mvmdio err irq",
+       },
+};
+
+static struct platform_device orion_ge_mvmdio = {
+       .name           = "orion-mdio",
+       .id             = -1,
+};
+
 static struct resource orion_ge00_resources[] = {
        {
                .name   = "ge00 irq",
@@ -295,26 +309,25 @@ void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data,
                            unsigned int tx_csum_limit)
 {
        fill_resources(&orion_ge00_shared, orion_ge00_shared_resources,
-                      mapbase + 0x2000, SZ_16K - 1, irq_err);
+                      mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
+       fill_resources(&orion_ge_mvmdio, orion_ge_mvmdio_resources,
+                       mapbase + 0x2004, 0x84 - 1, irq_err);
        orion_ge00_shared_data.tx_csum_limit = tx_csum_limit;
        ge_complete(&orion_ge00_shared_data,
                    orion_ge00_resources, irq, &orion_ge00_shared,
+                   &orion_ge_mvmdio,
                    eth_data, &orion_ge00);
 }
 
 /*****************************************************************************
  * GE01
  ****************************************************************************/
-struct mv643xx_eth_shared_platform_data orion_ge01_shared_data = {
-       .shared_smi     = &orion_ge00_shared,
-};
+struct mv643xx_eth_shared_platform_data orion_ge01_shared_data;
 
 static struct resource orion_ge01_shared_resources[] = {
        {
                .name   = "ge01 base",
-       }, {
-               .name   = "ge01 err irq",
-       },
+       }
 };
 
 static struct platform_device orion_ge01_shared = {
@@ -349,26 +362,23 @@ void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data,
                            unsigned int tx_csum_limit)
 {
        fill_resources(&orion_ge01_shared, orion_ge01_shared_resources,
-                      mapbase + 0x2000, SZ_16K - 1, irq_err);
+                      mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
        orion_ge01_shared_data.tx_csum_limit = tx_csum_limit;
        ge_complete(&orion_ge01_shared_data,
                    orion_ge01_resources, irq, &orion_ge01_shared,
+                   NULL,
                    eth_data, &orion_ge01);
 }
 
 /*****************************************************************************
  * GE10
  ****************************************************************************/
-struct mv643xx_eth_shared_platform_data orion_ge10_shared_data = {
-       .shared_smi     = &orion_ge00_shared,
-};
+struct mv643xx_eth_shared_platform_data orion_ge10_shared_data;
 
 static struct resource orion_ge10_shared_resources[] = {
        {
                .name   = "ge10 base",
-       }, {
-               .name   = "ge10 err irq",
-       },
+       }
 };
 
 static struct platform_device orion_ge10_shared = {
@@ -402,24 +412,21 @@ void __init orion_ge10_init(struct mv643xx_eth_platform_data *eth_data,
                            unsigned long irq_err)
 {
        fill_resources(&orion_ge10_shared, orion_ge10_shared_resources,
-                      mapbase + 0x2000, SZ_16K - 1, irq_err);
+                      mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
        ge_complete(&orion_ge10_shared_data,
                    orion_ge10_resources, irq, &orion_ge10_shared,
+                   NULL,
                    eth_data, &orion_ge10);
 }
 
 /*****************************************************************************
  * GE11
  ****************************************************************************/
-struct mv643xx_eth_shared_platform_data orion_ge11_shared_data = {
-       .shared_smi     = &orion_ge00_shared,
-};
+struct mv643xx_eth_shared_platform_data orion_ge11_shared_data;
 
 static struct resource orion_ge11_shared_resources[] = {
        {
                .name   = "ge11 base",
-       }, {
-               .name   = "ge11 err irq",
        },
 };
 
@@ -454,9 +461,10 @@ void __init orion_ge11_init(struct mv643xx_eth_platform_data *eth_data,
                            unsigned long irq_err)
 {
        fill_resources(&orion_ge11_shared, orion_ge11_shared_resources,
-                      mapbase + 0x2000, SZ_16K - 1, irq_err);
+                      mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
        ge_complete(&orion_ge11_shared_data,
                    orion_ge11_resources, irq, &orion_ge11_shared,
+                   NULL,
                    eth_data, &orion_ge11);
 }
 
index 51c6401582ea677a1d9d211ce3652b833de9516c..37401f535126695e22af87b35739aba1242dd219 100644 (file)
@@ -72,4 +72,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* __ASM_AVR32_SOCKET_H */
index 50692b738c756172af61ab84f2f8693495ea6655..ba409c9947bc2df933ca1d393b418f082424a57a 100644 (file)
@@ -74,6 +74,8 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _ASM_SOCKET_H */
 
 
index 595391f0f98c65e2bddbcbbe70b7e4d5062082d9..31dbb5d8e13debd569420493091189e5bc199aef 100644 (file)
@@ -72,5 +72,7 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _ASM_SOCKET_H */
 
index 43e32621da7d01ab20ad81f0c520d6ee83682434..5d1c6d0870e653e1f2cbfecf6e9ad7f6cf180741 100644 (file)
@@ -72,4 +72,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _ASM_SOCKET_H */
index c567adc8bea535fb5bcc15156a51f4af55424bf3..6b4329f18b29f3878c7679c8a32a4a60d931904d 100644 (file)
@@ -81,4 +81,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _ASM_IA64_SOCKET_H */
index 519afa2755db5c8738bc7138f67aecc408d27fd2..2a3b59e0e171d0fb19897f561028b1f0482180b3 100644 (file)
@@ -72,4 +72,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _ASM_M32R_SOCKET_H */
index 47132f44c9559630901ce5ca54b35220a548acf4..3b211507be7f56ffa4c125ce221a52d352f90a46 100644 (file)
@@ -90,4 +90,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 5c7c7c988544558f632a84ab5f529801a27b8963..b4ce844c9391c82dd36136c86a17141e6b5a93c0 100644 (file)
@@ -72,4 +72,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _ASM_SOCKET_H */
index 526e4b9aece0a0480f1631bcb492c83ffac2c735..70c512a386f7df86d5f086553322d40b0baa894a 100644 (file)
@@ -71,6 +71,8 @@
 
 #define SO_LOCK_FILTER         0x4025
 
+#define SO_SELECT_ERR_QUEUE    0x4026
+
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
index a26dcaece509490746ab8b2a5d254125f6d4571f..a36daf3c6f9a31faac9e922d0c77b4c5fe412c37 100644 (file)
@@ -79,4 +79,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _ASM_POWERPC_SOCKET_H */
index e834f1ec23c8a4ad16c67b0d3349565e1813cf83..c427ae36374ab4fa6eb1503c15b07de2926006e6 100644 (file)
@@ -671,16 +671,12 @@ void bpf_jit_compile(struct sk_filter *fp)
        }
 
        if (bpf_jit_enable > 1)
-               pr_info("flen=%d proglen=%u pass=%d image=%p\n",
-                      flen, proglen, pass, image);
+               /* Note that we output the base address of the code_base
+                * rather than image, since opcodes are in code_base.
+                */
+               bpf_jit_dump(flen, proglen, pass, code_base);
 
        if (image) {
-               if (bpf_jit_enable > 1)
-                       print_hex_dump(KERN_ERR, "JIT code: ",
-                                      DUMP_PREFIX_ADDRESS,
-                                      16, 1, code_base,
-                                      proglen, false);
-
                bpf_flush_icache(code_base, code_base + (proglen/4));
                /* Function descriptor nastiness: Address + TOC */
                ((u64 *)image)[0] = (u64)code_base;
index 039fc8e821997d3273e717db46001184f3f00917..2b4dc6abde6ce30a3dda84bbc94ed91b2434f36f 100644 (file)
@@ -47,6 +47,25 @@ static struct platform_device mv643xx_eth_shared_device = {
        .resource       = mv643xx_eth_shared_resources,
 };
 
+/*
+ * The orion mdio driver only covers shared + 0x4 up to shared + 0x84 - 1
+ */
+static struct resource mv643xx_eth_mvmdio_resources[] = {
+       [0] = {
+               .name   = "ethernet mdio base",
+               .start  = 0xf1000000 + MV643XX_ETH_SHARED_REGS + 0x4,
+               .end    = 0xf1000000 + MV643XX_ETH_SHARED_REGS + 0x83,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device mv643xx_eth_mvmdio_device = {
+       .name           = "orion-mdio",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(mv643xx_eth_mvmdio_resources),
+       .resource       = mv643xx_eth_shared_resources,
+};
+
 static struct resource mv643xx_eth_port1_resources[] = {
        [0] = {
                .name   = "eth port1 irq",
@@ -82,6 +101,7 @@ static struct platform_device eth_port1_device = {
 
 static struct platform_device *mv643xx_eth_pd_devs[] __initdata = {
        &mv643xx_eth_shared_device,
+       &mv643xx_eth_mvmdio_device,
        &eth_port1_device,
 };
 
index 0f6af41ebb448a5630d67ec2fc13303a5712e56a..4a25c26f0bf43e51ef5fe6d37c0fc1fcf788450d 100644 (file)
@@ -214,15 +214,27 @@ static struct platform_device * __init mv64x60_eth_register_shared_pdev(
                                                struct device_node *np, int id)
 {
        struct platform_device *pdev;
-       struct resource r[1];
+       struct resource r[2];
        int err;
 
        err = of_address_to_resource(np, 0, &r[0]);
        if (err)
                return ERR_PTR(err);
 
+       /* register an orion mdio bus driver */
+       r[1].start = r[0].start + 0x4;
+       r[1].end = r[0].start + 0x84 - 1;
+       r[1].flags = IORESOURCE_MEM;
+
+       if (id == 0) {
+               pdev = platform_device_register_simple("orion-mdio", -1, &r[1], 1);
+               if (!pdev)
+                       return pdev;
+       }
+
        pdev = platform_device_register_simple(MV643XX_ETH_SHARED_NAME, id,
-                                              r, 1);
+                                              &r[0], 1);
+
        return pdev;
 }
 
index f99eea7fff0fc4af85cf41ab1fd91ff6e402352b..2dacb306835ce217e91fe40a2c09aeae7edc05eb 100644 (file)
@@ -78,4 +78,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _ASM_SOCKET_H */
index cbbad74b2e06a0c6405ecabd4d054887b404668f..89f49b68a21c0804d6cbc55931b8beadf19f6711 100644 (file)
@@ -68,6 +68,8 @@
 
 #define SO_LOCK_FILTER         0x0028
 
+#define SO_SELECT_ERR_QUEUE    0x0029
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index 3109ca684a99f31c3a52613e3ffd2f291d6f185d..d36a85ebb5e027c38929c2a5a8f9e7be772984b9 100644 (file)
@@ -795,13 +795,9 @@ cond_branch:                       f_offset = addrs[i + filter[i].jf];
        }
 
        if (bpf_jit_enable > 1)
-               pr_err("flen=%d proglen=%u pass=%d image=%p\n",
-                      flen, proglen, pass, image);
+               bpf_jit_dump(flen, proglen, pass, image);
 
        if (image) {
-               if (bpf_jit_enable > 1)
-                       print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_ADDRESS,
-                                      16, 1, image, proglen, false);
                bpf_flush_icache(image, image + proglen);
                fp->bpf_func = (void *)image;
        }
index 3cbe45381bbb12de387729b7f070822207b3d6dc..f66b54086ce597562d72f03d5bbaacfb09622005 100644 (file)
@@ -725,17 +725,12 @@ cond_branch:                      f_offset = addrs[i + filter[i].jf] - addrs[i];
                }
                oldproglen = proglen;
        }
+
        if (bpf_jit_enable > 1)
-               pr_err("flen=%d proglen=%u pass=%d image=%p\n",
-                      flen, proglen, pass, image);
+               bpf_jit_dump(flen, proglen, pass, image);
 
        if (image) {
-               if (bpf_jit_enable > 1)
-                       print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_ADDRESS,
-                                      16, 1, image, proglen, false);
-
                bpf_flush_icache(image, image + proglen);
-
                fp->bpf_func = (void *)image;
        }
 out:
index 35905cb6e419a661c04e24bf205f5c4e1f1982f5..a8f44f50e651064b2358c7c8ab906a2103d38acc 100644 (file)
@@ -83,4 +83,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* _XTENSA_SOCKET_H */
index 03bbe104338ff70db5abb6fbace10b1595f438a0..17b26ce7e051b1374b6be01f18f3dd1cad057e56 100644 (file)
@@ -104,7 +104,13 @@ void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on)
                if (i)
                        bcma_err(core->bus, "PLL enable timeout\n");
        } else {
-               bcma_warn(core->bus, "Disabling PLL not supported yet!\n");
+               /*
+                * Mask the PLL but don't wait for it to be disabled. PLL may be
+                * shared between cores and will be still up if there is another
+                * core using it.
+                */
+               bcma_mask32(core, BCMA_CLKCTLST, ~req);
+               bcma_read32(core, BCMA_CLKCTLST);
        }
 }
 EXPORT_SYMBOL_GPL(bcma_core_pll_ctl);
index 28fa50ad87bee80630758b2c5ace883d02ce2c0d..036c6744b39bd390e2c6cafeddf3595cbbb89caa 100644 (file)
@@ -25,13 +25,14 @@ static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
        return value;
 }
 
-static u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc)
+u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc)
 {
        if (cc->capabilities & BCMA_CC_CAP_PMU)
                return bcma_pmu_get_alp_clock(cc);
 
        return 20000000;
 }
+EXPORT_SYMBOL_GPL(bcma_chipco_get_alp_clock);
 
 static u32 bcma_chipco_watchdog_get_max_timer(struct bcma_drv_cc *cc)
 {
@@ -213,6 +214,7 @@ u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value)
 
        return res;
 }
+EXPORT_SYMBOL_GPL(bcma_chipco_gpio_out);
 
 u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
 {
@@ -225,6 +227,7 @@ u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
 
        return res;
 }
+EXPORT_SYMBOL_GPL(bcma_chipco_gpio_outen);
 
 /*
  * If the bit is set to 0, chipcommon controlls this GPIO,
index 932b101dee36a5398a53904072d32d32541af23a..edca73af3cc089fd342573ec0fca20bcc5ddc677 100644 (file)
@@ -174,19 +174,35 @@ u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc)
        struct bcma_bus *bus = cc->core->bus;
 
        switch (bus->chipinfo.id) {
+       case BCMA_CHIP_ID_BCM4313:
+       case BCMA_CHIP_ID_BCM43224:
+       case BCMA_CHIP_ID_BCM43225:
+       case BCMA_CHIP_ID_BCM43227:
+       case BCMA_CHIP_ID_BCM43228:
+       case BCMA_CHIP_ID_BCM4331:
+       case BCMA_CHIP_ID_BCM43421:
+       case BCMA_CHIP_ID_BCM43428:
+       case BCMA_CHIP_ID_BCM43431:
        case BCMA_CHIP_ID_BCM4716:
-       case BCMA_CHIP_ID_BCM4748:
        case BCMA_CHIP_ID_BCM47162:
-       case BCMA_CHIP_ID_BCM4313:
-       case BCMA_CHIP_ID_BCM5357:
+       case BCMA_CHIP_ID_BCM4748:
        case BCMA_CHIP_ID_BCM4749:
+       case BCMA_CHIP_ID_BCM5357:
        case BCMA_CHIP_ID_BCM53572:
+       case BCMA_CHIP_ID_BCM6362:
                /* always 20Mhz */
                return 20000 * 1000;
-       case BCMA_CHIP_ID_BCM5356:
        case BCMA_CHIP_ID_BCM4706:
+       case BCMA_CHIP_ID_BCM5356:
                /* always 25Mhz */
                return 25000 * 1000;
+       case BCMA_CHIP_ID_BCM43460:
+       case BCMA_CHIP_ID_BCM4352:
+       case BCMA_CHIP_ID_BCM4360:
+               if (cc->status & BCMA_CC_CHIPST_4360_XTAL_40MZ)
+                       return 40000 * 1000;
+               else
+                       return 20000 * 1000;
        default:
                bcma_warn(bus, "No ALP clock specified for %04X device, pmu rev. %d, using default %d Hz\n",
                          bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK);
@@ -373,7 +389,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
                tmp |= (bcm5357_bcm43236_ndiv[spuravoid]) << BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT;
                bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp);
 
-               tmp = 1 << 10;
+               tmp = BCMA_CC_PMU_CTL_PLL_UPD;
                break;
 
        case BCMA_CHIP_ID_BCM4331:
@@ -394,7 +410,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
                        bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2,
                                                     0x03000a08);
                }
-               tmp = 1 << 10;
+               tmp = BCMA_CC_PMU_CTL_PLL_UPD;
                break;
 
        case BCMA_CHIP_ID_BCM43224:
@@ -427,7 +443,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
                        bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
                                                     0x88888815);
                }
-               tmp = 1 << 10;
+               tmp = BCMA_CC_PMU_CTL_PLL_UPD;
                break;
 
        case BCMA_CHIP_ID_BCM4716:
@@ -461,7 +477,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
                                                     0x88888815);
                }
 
-               tmp = 3 << 9;
+               tmp = BCMA_CC_PMU_CTL_PLL_UPD | BCMA_CC_PMU_CTL_NOILPONW;
                break;
 
        case BCMA_CHIP_ID_BCM43227:
@@ -497,7 +513,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
                        bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5,
                                                     0x88888815);
                }
-               tmp = 1 << 10;
+               tmp = BCMA_CC_PMU_CTL_PLL_UPD;
                break;
        default:
                bcma_err(bus, "Unknown spuravoidance settings for chip 0x%04X, not changing PLL\n",
index 9a6188add5908904887339df04182eda5a6bb358..f72f52b4b1dde78311ff4872f57eaf8d838ad456 100644 (file)
@@ -120,6 +120,11 @@ static int bcma_register_cores(struct bcma_bus *bus)
                        continue;
                }
 
+               /* Only first GMAC core on BCM4706 is connected and working */
+               if (core->id.id == BCMA_CORE_4706_MAC_GBIT &&
+                   core->core_unit > 0)
+                       continue;
+
                core->dev.release = bcma_release_core_dev;
                core->dev.bus = &bcma_bus_type;
                dev_set_name(&core->dev, "bcma%d:%d", bus->num, dev_id);
index 8d0b57164018d7f14cdacccb3214bd6fe6008c3b..bca9c80056febb4f90215534ca67d6adbf0aee80 100644 (file)
@@ -137,19 +137,19 @@ static void bcma_scan_switch_core(struct bcma_bus *bus, u32 addr)
                                       addr);
 }
 
-static u32 bcma_erom_get_ent(struct bcma_bus *bus, u32 **eromptr)
+static u32 bcma_erom_get_ent(struct bcma_bus *bus, u32 __iomem **eromptr)
 {
        u32 ent = readl(*eromptr);
        (*eromptr)++;
        return ent;
 }
 
-static void bcma_erom_push_ent(u32 **eromptr)
+static void bcma_erom_push_ent(u32 __iomem **eromptr)
 {
        (*eromptr)--;
 }
 
-static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 **eromptr)
+static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 __iomem **eromptr)
 {
        u32 ent = bcma_erom_get_ent(bus, eromptr);
        if (!(ent & SCAN_ER_VALID))
@@ -159,14 +159,14 @@ static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 **eromptr)
        return ent;
 }
 
-static bool bcma_erom_is_end(struct bcma_bus *bus, u32 **eromptr)
+static bool bcma_erom_is_end(struct bcma_bus *bus, u32 __iomem **eromptr)
 {
        u32 ent = bcma_erom_get_ent(bus, eromptr);
        bcma_erom_push_ent(eromptr);
        return (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID));
 }
 
-static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 **eromptr)
+static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 __iomem **eromptr)
 {
        u32 ent = bcma_erom_get_ent(bus, eromptr);
        bcma_erom_push_ent(eromptr);
@@ -175,7 +175,7 @@ static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 **eromptr)
                ((ent & SCAN_ADDR_TYPE) == SCAN_ADDR_TYPE_BRIDGE));
 }
 
-static void bcma_erom_skip_component(struct bcma_bus *bus, u32 **eromptr)
+static void bcma_erom_skip_component(struct bcma_bus *bus, u32 __iomem **eromptr)
 {
        u32 ent;
        while (1) {
@@ -189,7 +189,7 @@ static void bcma_erom_skip_component(struct bcma_bus *bus, u32 **eromptr)
        bcma_erom_push_ent(eromptr);
 }
 
-static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 **eromptr)
+static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 __iomem **eromptr)
 {
        u32 ent = bcma_erom_get_ent(bus, eromptr);
        if (!(ent & SCAN_ER_VALID))
@@ -199,7 +199,7 @@ static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 **eromptr)
        return ent;
 }
 
-static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 **eromptr,
+static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
                                  u32 type, u8 port)
 {
        u32 addrl, addrh, sizel, sizeh = 0;
index 4adf9ef9a113010e2b8f51cabd27e07c57898c83..8934298a638dfdbfb6d2e5e4fa6e43cb65aeac8b 100644 (file)
@@ -217,6 +217,7 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
        }
 
        SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
+       SPEX(board_type, SSB_SPROM1_SPID, ~0, 0);
 
        SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0,
             SSB_SPROM4_TXPID2G0_SHIFT);
index 9959d4cb23dcc2ddad7deefa008def72ac513925..1cb51839912d0e791254349a64f171ec2a9754a2 100644 (file)
@@ -83,8 +83,8 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
 };
 
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
-       .helper         = "sd8688_helper.bin",
-       .firmware       = "sd8688.bin",
+       .helper         = "mrvl/sd8688_helper.bin",
+       .firmware       = "mrvl/sd8688.bin",
        .reg            = &btmrvl_reg_8688,
        .sd_blksz_fw_dl = 64,
 };
@@ -1185,7 +1185,7 @@ MODULE_AUTHOR("Marvell International Ltd.");
 MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION);
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL v2");
-MODULE_FIRMWARE("sd8688_helper.bin");
-MODULE_FIRMWARE("sd8688.bin");
+MODULE_FIRMWARE("mrvl/sd8688_helper.bin");
+MODULE_FIRMWARE("mrvl/sd8688.bin");
 MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
index 1110478dd0fdb83b6bbdbae5fa1f40c0a2406655..08ae128cce9be2e930454088c1041478bce8d8f5 100644 (file)
@@ -232,6 +232,31 @@ void proc_comm_connector(struct task_struct *task)
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
+void proc_coredump_connector(struct task_struct *task)
+{
+       struct cn_msg *msg;
+       struct proc_event *ev;
+       __u8 buffer[CN_PROC_MSG_SIZE];
+       struct timespec ts;
+
+       if (atomic_read(&proc_event_num_listeners) < 1)
+               return;
+
+       msg = (struct cn_msg *)buffer;
+       ev = (struct proc_event *)msg->data;
+       get_seq(&msg->seq, &ev->cpu);
+       ktime_get_ts(&ts); /* get high res monotonic timestamp */
+       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->what = PROC_EVENT_COREDUMP;
+       ev->event_data.coredump.process_pid = task->pid;
+       ev->event_data.coredump.process_tgid = task->tgid;
+
+       memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
+       msg->ack = 0; /* not used */
+       msg->len = sizeof(*ev);
+       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+}
+
 void proc_exit_connector(struct task_struct *task)
 {
        struct cn_msg *msg;
index f1b7e244bfc153f5cdba58f113de17332516119a..6ecfa758942c50a4b33ebd399cd831ac1420277d 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/module.h>
 #include <linux/list.h>
 #include <linux/skbuff.h>
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <linux/moduleparam.h>
 #include <linux/connector.h>
 #include <linux/slab.h>
@@ -95,13 +95,13 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
        if (!netlink_has_listeners(dev->nls, group))
                return -ESRCH;
 
-       size = NLMSG_SPACE(sizeof(*msg) + msg->len);
+       size = sizeof(*msg) + msg->len;
 
-       skb = alloc_skb(size, gfp_mask);
+       skb = nlmsg_new(size, gfp_mask);
        if (!skb)
                return -ENOMEM;
 
-       nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh), 0);
+       nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size, 0);
        if (!nlh) {
                kfree_skb(skb);
                return -EMSGSIZE;
@@ -124,7 +124,7 @@ static int cn_call_callback(struct sk_buff *skb)
 {
        struct cn_callback_entry *i, *cbq = NULL;
        struct cn_dev *dev = &cdev;
-       struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(skb));
+       struct cn_msg *msg = nlmsg_data(nlmsg_hdr(skb));
        struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
        int err = -ENODEV;
 
@@ -162,7 +162,7 @@ static void cn_rx_skb(struct sk_buff *__skb)
 
        skb = skb_get(__skb);
 
-       if (skb->len >= NLMSG_SPACE(0)) {
+       if (skb->len >= NLMSG_HDRLEN) {
                nlh = nlmsg_hdr(skb);
 
                if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
index 9b041858d10dcb6bdc1fcbef9eab59006116c96d..9e84d5bc930779d4b8f60c98a68ac43108bd3660 100644 (file)
@@ -470,8 +470,10 @@ struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase)
        }
 
        if (!dca2_tag_map_valid(ioatdca->tag_map)) {
-               dev_err(&pdev->dev, "APICID_TAG_MAP set incorrectly by BIOS, "
-                       "disabling DCA\n");
+               WARN_TAINT_ONCE(1, TAINT_FIRMWARE_WORKAROUND,
+                               "%s %s: APICID_TAG_MAP set incorrectly by BIOS, disabling DCA\n",
+                               dev_driver_string(&pdev->dev),
+                               dev_name(&pdev->dev));
                free_dca_provider(dca);
                return NULL;
        }
@@ -689,7 +691,10 @@ struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase)
        }
 
        if (dca3_tag_map_invalid(ioatdca->tag_map)) {
-               dev_err(&pdev->dev, "APICID_TAG_MAP set incorrectly by BIOS, disabling DCA\n");
+               WARN_TAINT_ONCE(1, TAINT_FIRMWARE_WORKAROUND,
+                               "%s %s: APICID_TAG_MAP set incorrectly by BIOS, disabling DCA\n",
+                               dev_driver_string(&pdev->dev),
+                               dev_name(&pdev->dev));
                free_dca_provider(dca);
                return NULL;
        }
index 7224533e8ca6fbbdee323d4f819d8ff29208b088..7a701a58bbf058c6417b911e8c1597a5d0b4e5b5 100644 (file)
@@ -47,9 +47,9 @@ config FIREWIRE_NET
        tristate "IP networking over 1394"
        depends on FIREWIRE && INET
        help
-         This enables IPv4 over IEEE 1394, providing IP connectivity with
-         other implementations of RFC 2734 as found on several operating
-         systems.  Multicast support is currently limited.
+         This enables IPv4/IPv6 over IEEE 1394, providing IP connectivity
+         with other implementations of RFC 2734/3146 as found on several
+         operating systems.  Multicast support is currently limited.
 
          To compile this driver as a module, say M here:  The module will be
          called firewire-net.
index 2b27bff2591a029a193878812e3e8d00ca1134b4..4d565365e476c1f7bdef79070fe66b7cb17a0457 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * IPv4 over IEEE 1394, per RFC 2734
+ * IPv6 over IEEE 1394, per RFC 3146
  *
  * Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com>
  *
@@ -28,6 +29,7 @@
 
 #include <asm/unaligned.h>
 #include <net/arp.h>
+#include <net/firewire.h>
 
 /* rx limits */
 #define FWNET_MAX_FRAGMENTS            30 /* arbitrary, > TX queue depth */
@@ -45,6 +47,7 @@
 
 #define IANA_SPECIFIER_ID              0x00005eU
 #define RFC2734_SW_VERSION             0x000001U
+#define RFC3146_SW_VERSION             0x000002U
 
 #define IEEE1394_GASP_HDR_SIZE 8
 
 #define RFC2374_HDR_LASTFRAG   2       /* last fragment        */
 #define RFC2374_HDR_INTFRAG    3       /* interior fragment    */
 
-#define RFC2734_HW_ADDR_LEN    16
-
-struct rfc2734_arp {
-       __be16 hw_type;         /* 0x0018       */
-       __be16 proto_type;      /* 0x0806       */
-       u8 hw_addr_len;         /* 16           */
-       u8 ip_addr_len;         /* 4            */
-       __be16 opcode;          /* ARP Opcode   */
-       /* Above is exactly the same format as struct arphdr */
-
-       __be64 s_uniq_id;       /* Sender's 64bit EUI                   */
-       u8 max_rec;             /* Sender's max packet size             */
-       u8 sspd;                /* Sender's max speed                   */
-       __be16 fifo_hi;         /* hi 16bits of sender's FIFO addr      */
-       __be32 fifo_lo;         /* lo 32bits of sender's FIFO addr      */
-       __be32 sip;             /* Sender's IP Address                  */
-       __be32 tip;             /* IP Address of requested hw addr      */
-} __packed;
-
-/* This header format is specific to this driver implementation. */
-#define FWNET_ALEN     8
-#define FWNET_HLEN     10
-struct fwnet_header {
-       u8 h_dest[FWNET_ALEN];  /* destination address */
-       __be16 h_proto;         /* packet type ID field */
-} __packed;
+static bool fwnet_hwaddr_is_multicast(u8 *ha)
+{
+       return !!(*ha & 1);
+}
 
 /* IPv4 and IPv6 encapsulation header */
 struct rfc2734_header {
@@ -191,8 +172,6 @@ struct fwnet_peer {
        struct list_head peer_link;
        struct fwnet_device *dev;
        u64 guid;
-       u64 fifo;
-       __be32 ip;
 
        /* guarded by dev->lock */
        struct list_head pd_list; /* received partial datagrams */
@@ -221,6 +200,15 @@ struct fwnet_packet_task {
        u8 enqueued;
 };
 
+/*
+ * Get fifo address embedded in hwaddr
+ */
+static __u64 fwnet_hwaddr_fifo(union fwnet_hwaddr *ha)
+{
+       return (u64)get_unaligned_be16(&ha->uc.fifo_hi) << 32
+              | get_unaligned_be32(&ha->uc.fifo_lo);
+}
+
 /*
  * saddr == NULL means use device source address.
  * daddr == NULL means leave destination address (eg unresolved arp).
@@ -513,10 +501,20 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
                                        bool is_broadcast, u16 ether_type)
 {
        struct fwnet_device *dev;
-       static const __be64 broadcast_hw = cpu_to_be64(~0ULL);
        int status;
        __be64 guid;
 
+       switch (ether_type) {
+       case ETH_P_ARP:
+       case ETH_P_IP:
+#if IS_ENABLED(CONFIG_IPV6)
+       case ETH_P_IPV6:
+#endif
+               break;
+       default:
+               goto err;
+       }
+
        dev = netdev_priv(net);
        /* Write metadata, and then pass to the receive level */
        skb->dev = net;
@@ -524,92 +522,11 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
 
        /*
         * Parse the encapsulation header. This actually does the job of
-        * converting to an ethernet frame header, as well as arp
-        * conversion if needed. ARP conversion is easier in this
-        * direction, since we are using ethernet as our backend.
+        * converting to an ethernet-like pseudo frame header.
         */
-       /*
-        * If this is an ARP packet, convert it. First, we want to make
-        * use of some of the fields, since they tell us a little bit
-        * about the sending machine.
-        */
-       if (ether_type == ETH_P_ARP) {
-               struct rfc2734_arp *arp1394;
-               struct arphdr *arp;
-               unsigned char *arp_ptr;
-               u64 fifo_addr;
-               u64 peer_guid;
-               unsigned sspd;
-               u16 max_payload;
-               struct fwnet_peer *peer;
-               unsigned long flags;
-
-               arp1394   = (struct rfc2734_arp *)skb->data;
-               arp       = (struct arphdr *)skb->data;
-               arp_ptr   = (unsigned char *)(arp + 1);
-               peer_guid = get_unaligned_be64(&arp1394->s_uniq_id);
-               fifo_addr = (u64)get_unaligned_be16(&arp1394->fifo_hi) << 32
-                               | get_unaligned_be32(&arp1394->fifo_lo);
-
-               sspd = arp1394->sspd;
-               /* Sanity check.  OS X 10.3 PPC reportedly sends 131. */
-               if (sspd > SCODE_3200) {
-                       dev_notice(&net->dev, "sspd %x out of range\n", sspd);
-                       sspd = SCODE_3200;
-               }
-               max_payload = fwnet_max_payload(arp1394->max_rec, sspd);
-
-               spin_lock_irqsave(&dev->lock, flags);
-               peer = fwnet_peer_find_by_guid(dev, peer_guid);
-               if (peer) {
-                       peer->fifo = fifo_addr;
-
-                       if (peer->speed > sspd)
-                               peer->speed = sspd;
-                       if (peer->max_payload > max_payload)
-                               peer->max_payload = max_payload;
-
-                       peer->ip = arp1394->sip;
-               }
-               spin_unlock_irqrestore(&dev->lock, flags);
-
-               if (!peer) {
-                       dev_notice(&net->dev,
-                                  "no peer for ARP packet from %016llx\n",
-                                  (unsigned long long)peer_guid);
-                       goto no_peer;
-               }
-
-               /*
-                * Now that we're done with the 1394 specific stuff, we'll
-                * need to alter some of the data.  Believe it or not, all
-                * that needs to be done is sender_IP_address needs to be
-                * moved, the destination hardware address get stuffed
-                * in and the hardware address length set to 8.
-                *
-                * IMPORTANT: The code below overwrites 1394 specific data
-                * needed above so keep the munging of the data for the
-                * higher level IP stack last.
-                */
-
-               arp->ar_hln = 8;
-               /* skip over sender unique id */
-               arp_ptr += arp->ar_hln;
-               /* move sender IP addr */
-               put_unaligned(arp1394->sip, (u32 *)arp_ptr);
-               /* skip over sender IP addr */
-               arp_ptr += arp->ar_pln;
-
-               if (arp->ar_op == htons(ARPOP_REQUEST))
-                       memset(arp_ptr, 0, sizeof(u64));
-               else
-                       memcpy(arp_ptr, net->dev_addr, sizeof(u64));
-       }
-
-       /* Now add the ethernet header. */
        guid = cpu_to_be64(dev->card->guid);
        if (dev_hard_header(skb, net, ether_type,
-                          is_broadcast ? &broadcast_hw : &guid,
+                          is_broadcast ? net->broadcast : net->dev_addr,
                           NULL, skb->len) >= 0) {
                struct fwnet_header *eth;
                u16 *rawp;
@@ -618,7 +535,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
                skb_reset_mac_header(skb);
                skb_pull(skb, sizeof(*eth));
                eth = (struct fwnet_header *)skb_mac_header(skb);
-               if (*eth->h_dest & 1) {
+               if (fwnet_hwaddr_is_multicast(eth->h_dest)) {
                        if (memcmp(eth->h_dest, net->broadcast,
                                   net->addr_len) == 0)
                                skb->pkt_type = PACKET_BROADCAST;
@@ -630,7 +547,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
                        if (memcmp(eth->h_dest, net->dev_addr, net->addr_len))
                                skb->pkt_type = PACKET_OTHERHOST;
                }
-               if (ntohs(eth->h_proto) >= 1536) {
+               if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
                        protocol = eth->h_proto;
                } else {
                        rawp = (u16 *)skb->data;
@@ -652,7 +569,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
 
        return 0;
 
no_peer:
err:
        net->stats.rx_errors++;
        net->stats.rx_dropped++;
 
@@ -856,7 +773,12 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
        ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
        source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
 
-       if (specifier_id == IANA_SPECIFIER_ID && ver == RFC2734_SW_VERSION) {
+       if (specifier_id == IANA_SPECIFIER_ID &&
+           (ver == RFC2734_SW_VERSION
+#if IS_ENABLED(CONFIG_IPV6)
+            || ver == RFC3146_SW_VERSION
+#endif
+           )) {
                buf_ptr += 2;
                length -= IEEE1394_GASP_HDR_SIZE;
                fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
@@ -1059,16 +981,27 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
                u8 *p;
                int generation;
                int node_id;
+               unsigned int sw_version;
 
                /* ptask->generation may not have been set yet */
                generation = dev->card->generation;
                smp_rmb();
                node_id = dev->card->node_id;
 
+               switch (ptask->skb->protocol) {
+               default:
+                       sw_version = RFC2734_SW_VERSION;
+                       break;
+#if IS_ENABLED(CONFIG_IPV6)
+               case htons(ETH_P_IPV6):
+                       sw_version = RFC3146_SW_VERSION;
+#endif
+               }
+
                p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
                put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
                put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
-                                               | RFC2734_SW_VERSION, &p[4]);
+                                               | sw_version, &p[4]);
 
                /* We should not transmit if broadcast_channel.valid == 0. */
                fw_send_request(dev->card, &ptask->transaction,
@@ -1116,6 +1049,62 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
        return 0;
 }
 
+static void fwnet_fifo_stop(struct fwnet_device *dev)
+{
+       if (dev->local_fifo == FWNET_NO_FIFO_ADDR)
+               return;
+
+       fw_core_remove_address_handler(&dev->handler);
+       dev->local_fifo = FWNET_NO_FIFO_ADDR;
+}
+
+static int fwnet_fifo_start(struct fwnet_device *dev)
+{
+       int retval;
+
+       if (dev->local_fifo != FWNET_NO_FIFO_ADDR)
+               return 0;
+
+       dev->handler.length = 4096;
+       dev->handler.address_callback = fwnet_receive_packet;
+       dev->handler.callback_data = dev;
+
+       retval = fw_core_add_address_handler(&dev->handler,
+                                            &fw_high_memory_region);
+       if (retval < 0)
+               return retval;
+
+       dev->local_fifo = dev->handler.offset;
+
+       return 0;
+}
+
+static void __fwnet_broadcast_stop(struct fwnet_device *dev)
+{
+       unsigned u;
+
+       if (dev->broadcast_state != FWNET_BROADCAST_ERROR) {
+               for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++)
+                       kunmap(dev->broadcast_rcv_buffer.pages[u]);
+               fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card);
+       }
+       if (dev->broadcast_rcv_context) {
+               fw_iso_context_destroy(dev->broadcast_rcv_context);
+               dev->broadcast_rcv_context = NULL;
+       }
+       kfree(dev->broadcast_rcv_buffer_ptrs);
+       dev->broadcast_rcv_buffer_ptrs = NULL;
+       dev->broadcast_state = FWNET_BROADCAST_ERROR;
+}
+
+static void fwnet_broadcast_stop(struct fwnet_device *dev)
+{
+       if (dev->broadcast_state == FWNET_BROADCAST_ERROR)
+               return;
+       fw_iso_context_stop(dev->broadcast_rcv_context);
+       __fwnet_broadcast_stop(dev);
+}
+
 static int fwnet_broadcast_start(struct fwnet_device *dev)
 {
        struct fw_iso_context *context;
@@ -1124,60 +1113,47 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
        unsigned max_receive;
        struct fw_iso_packet packet;
        unsigned long offset;
+       void **ptrptr;
        unsigned u;
 
-       if (dev->local_fifo == FWNET_NO_FIFO_ADDR) {
-               dev->handler.length = 4096;
-               dev->handler.address_callback = fwnet_receive_packet;
-               dev->handler.callback_data = dev;
-
-               retval = fw_core_add_address_handler(&dev->handler,
-                                       &fw_high_memory_region);
-               if (retval < 0)
-                       goto failed_initial;
-
-               dev->local_fifo = dev->handler.offset;
-       }
+       if (dev->broadcast_state != FWNET_BROADCAST_ERROR)
+               return 0;
 
        max_receive = 1U << (dev->card->max_receive + 1);
        num_packets = (FWNET_ISO_PAGE_COUNT * PAGE_SIZE) / max_receive;
 
-       if (!dev->broadcast_rcv_context) {
-               void **ptrptr;
-
-               context = fw_iso_context_create(dev->card,
-                   FW_ISO_CONTEXT_RECEIVE, IEEE1394_BROADCAST_CHANNEL,
-                   dev->card->link_speed, 8, fwnet_receive_broadcast, dev);
-               if (IS_ERR(context)) {
-                       retval = PTR_ERR(context);
-                       goto failed_context_create;
-               }
+       ptrptr = kmalloc(sizeof(void *) * num_packets, GFP_KERNEL);
+       if (!ptrptr) {
+               retval = -ENOMEM;
+               goto failed;
+       }
+       dev->broadcast_rcv_buffer_ptrs = ptrptr;
+
+       context = fw_iso_context_create(dev->card, FW_ISO_CONTEXT_RECEIVE,
+                                       IEEE1394_BROADCAST_CHANNEL,
+                                       dev->card->link_speed, 8,
+                                       fwnet_receive_broadcast, dev);
+       if (IS_ERR(context)) {
+               retval = PTR_ERR(context);
+               goto failed;
+       }
 
-               retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer,
-                   dev->card, FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE);
-               if (retval < 0)
-                       goto failed_buffer_init;
+       retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer, dev->card,
+                                   FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE);
+       if (retval < 0)
+               goto failed;
 
-               ptrptr = kmalloc(sizeof(void *) * num_packets, GFP_KERNEL);
-               if (!ptrptr) {
-                       retval = -ENOMEM;
-                       goto failed_ptrs_alloc;
-               }
+       dev->broadcast_state = FWNET_BROADCAST_STOPPED;
 
-               dev->broadcast_rcv_buffer_ptrs = ptrptr;
-               for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) {
-                       void *ptr;
-                       unsigned v;
+       for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) {
+               void *ptr;
+               unsigned v;
 
-                       ptr = kmap(dev->broadcast_rcv_buffer.pages[u]);
-                       for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++)
-                               *ptrptr++ = (void *)
-                                               ((char *)ptr + v * max_receive);
-               }
-               dev->broadcast_rcv_context = context;
-       } else {
-               context = dev->broadcast_rcv_context;
+               ptr = kmap(dev->broadcast_rcv_buffer.pages[u]);
+               for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++)
+                       *ptrptr++ = (void *) ((char *)ptr + v * max_receive);
        }
+       dev->broadcast_rcv_context = context;
 
        packet.payload_length = max_receive;
        packet.interrupt = 1;
@@ -1191,7 +1167,7 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
                retval = fw_iso_context_queue(context, &packet,
                                &dev->broadcast_rcv_buffer, offset);
                if (retval < 0)
-                       goto failed_rcv_queue;
+                       goto failed;
 
                offset += max_receive;
        }
@@ -1201,7 +1177,7 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
        retval = fw_iso_context_start(context, -1, 0,
                        FW_ISO_CONTEXT_MATCH_ALL_TAGS); /* ??? sync */
        if (retval < 0)
-               goto failed_rcv_queue;
+               goto failed;
 
        /* FIXME: adjust it according to the min. speed of all known peers? */
        dev->broadcast_xmt_max_payload = IEEE1394_MAX_PAYLOAD_S100
@@ -1210,19 +1186,8 @@ static int fwnet_broadcast_start(struct fwnet_device *dev)
 
        return 0;
 
- failed_rcv_queue:
-       kfree(dev->broadcast_rcv_buffer_ptrs);
-       dev->broadcast_rcv_buffer_ptrs = NULL;
- failed_ptrs_alloc:
-       fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card);
- failed_buffer_init:
-       fw_iso_context_destroy(context);
-       dev->broadcast_rcv_context = NULL;
- failed_context_create:
-       fw_core_remove_address_handler(&dev->handler);
- failed_initial:
-       dev->local_fifo = FWNET_NO_FIFO_ADDR;
-
+ failed:
+       __fwnet_broadcast_stop(dev);
        return retval;
 }
 
@@ -1240,11 +1205,10 @@ static int fwnet_open(struct net_device *net)
        struct fwnet_device *dev = netdev_priv(net);
        int ret;
 
-       if (dev->broadcast_state == FWNET_BROADCAST_ERROR) {
-               ret = fwnet_broadcast_start(dev);
-               if (ret)
-                       return ret;
-       }
+       ret = fwnet_broadcast_start(dev);
+       if (ret)
+               return ret;
+
        netif_start_queue(net);
 
        spin_lock_irq(&dev->lock);
@@ -1257,9 +1221,10 @@ static int fwnet_open(struct net_device *net)
 /* ifdown */
 static int fwnet_stop(struct net_device *net)
 {
-       netif_stop_queue(net);
+       struct fwnet_device *dev = netdev_priv(net);
 
-       /* Deallocate iso context for use by other applications? */
+       netif_stop_queue(net);
+       fwnet_broadcast_stop(dev);
 
        return 0;
 }
@@ -1299,19 +1264,27 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
         * We might need to rebuild the header on tx failure.
         */
        memcpy(&hdr_buf, skb->data, sizeof(hdr_buf));
-       skb_pull(skb, sizeof(hdr_buf));
-
        proto = hdr_buf.h_proto;
+
+       switch (proto) {
+       case htons(ETH_P_ARP):
+       case htons(ETH_P_IP):
+#if IS_ENABLED(CONFIG_IPV6)
+       case htons(ETH_P_IPV6):
+#endif
+               break;
+       default:
+               goto fail;
+       }
+
+       skb_pull(skb, sizeof(hdr_buf));
        dg_size = skb->len;
 
        /*
         * Set the transmission type for the packet.  ARP packets and IP
         * broadcast packets are sent via GASP.
         */
-       if (memcmp(hdr_buf.h_dest, net->broadcast, FWNET_ALEN) == 0
-           || proto == htons(ETH_P_ARP)
-           || (proto == htons(ETH_P_IP)
-               && IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) {
+       if (fwnet_hwaddr_is_multicast(hdr_buf.h_dest)) {
                max_payload        = dev->broadcast_xmt_max_payload;
                datagram_label_ptr = &dev->broadcast_xmt_datagramlabel;
 
@@ -1320,11 +1293,12 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
                ptask->dest_node   = IEEE1394_ALL_NODES;
                ptask->speed       = SCODE_100;
        } else {
-               __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest);
+               union fwnet_hwaddr *ha = (union fwnet_hwaddr *)hdr_buf.h_dest;
+               __be64 guid = get_unaligned(&ha->uc.uniq_id);
                u8 generation;
 
                peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid));
-               if (!peer || peer->fifo == FWNET_NO_FIFO_ADDR)
+               if (!peer)
                        goto fail;
 
                generation         = peer->generation;
@@ -1332,32 +1306,12 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
                max_payload        = peer->max_payload;
                datagram_label_ptr = &peer->datagram_label;
 
-               ptask->fifo_addr   = peer->fifo;
+               ptask->fifo_addr   = fwnet_hwaddr_fifo(ha);
                ptask->generation  = generation;
                ptask->dest_node   = dest_node;
                ptask->speed       = peer->speed;
        }
 
-       /* If this is an ARP packet, convert it */
-       if (proto == htons(ETH_P_ARP)) {
-               struct arphdr *arp = (struct arphdr *)skb->data;
-               unsigned char *arp_ptr = (unsigned char *)(arp + 1);
-               struct rfc2734_arp *arp1394 = (struct rfc2734_arp *)skb->data;
-               __be32 ipaddr;
-
-               ipaddr = get_unaligned((__be32 *)(arp_ptr + FWNET_ALEN));
-
-               arp1394->hw_addr_len    = RFC2734_HW_ADDR_LEN;
-               arp1394->max_rec        = dev->card->max_receive;
-               arp1394->sspd           = dev->card->link_speed;
-
-               put_unaligned_be16(dev->local_fifo >> 32,
-                                  &arp1394->fifo_hi);
-               put_unaligned_be32(dev->local_fifo & 0xffffffff,
-                                  &arp1394->fifo_lo);
-               put_unaligned(ipaddr, &arp1394->sip);
-       }
-
        ptask->hdr.w0 = 0;
        ptask->hdr.w1 = 0;
        ptask->skb = skb;
@@ -1472,8 +1426,6 @@ static int fwnet_add_peer(struct fwnet_device *dev,
 
        peer->dev = dev;
        peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
-       peer->fifo = FWNET_NO_FIFO_ADDR;
-       peer->ip = 0;
        INIT_LIST_HEAD(&peer->pd_list);
        peer->pdg_size = 0;
        peer->datagram_label = 0;
@@ -1503,6 +1455,7 @@ static int fwnet_probe(struct device *_dev)
        struct fwnet_device *dev;
        unsigned max_mtu;
        int ret;
+       union fwnet_hwaddr *ha;
 
        mutex_lock(&fwnet_device_mutex);
 
@@ -1533,6 +1486,11 @@ static int fwnet_probe(struct device *_dev)
        dev->card = card;
        dev->netdev = net;
 
+       ret = fwnet_fifo_start(dev);
+       if (ret < 0)
+               goto out;
+       dev->local_fifo = dev->handler.offset;
+
        /*
         * Use the RFC 2734 default 1500 octets or the maximum payload
         * as initial MTU
@@ -1542,24 +1500,31 @@ static int fwnet_probe(struct device *_dev)
        net->mtu = min(1500U, max_mtu);
 
        /* Set our hardware address while we're at it */
-       put_unaligned_be64(card->guid, net->dev_addr);
-       put_unaligned_be64(~0ULL, net->broadcast);
+       ha = (union fwnet_hwaddr *)net->dev_addr;
+       put_unaligned_be64(card->guid, &ha->uc.uniq_id);
+       ha->uc.max_rec = dev->card->max_receive;
+       ha->uc.sspd = dev->card->link_speed;
+       put_unaligned_be16(dev->local_fifo >> 32, &ha->uc.fifo_hi);
+       put_unaligned_be32(dev->local_fifo & 0xffffffff, &ha->uc.fifo_lo);
+
+       memset(net->broadcast, -1, net->addr_len);
+
        ret = register_netdev(net);
        if (ret)
                goto out;
 
        list_add_tail(&dev->dev_link, &fwnet_device_list);
-       dev_notice(&net->dev, "IPv4 over IEEE 1394 on card %s\n",
+       dev_notice(&net->dev, "IP over IEEE 1394 on card %s\n",
                   dev_name(card->device));
  have_dev:
        ret = fwnet_add_peer(dev, unit, device);
        if (ret && allocated_netdev) {
                unregister_netdev(net);
                list_del(&dev->dev_link);
-       }
  out:
-       if (ret && allocated_netdev)
+               fwnet_fifo_stop(dev);
                free_netdev(net);
+       }
 
        mutex_unlock(&fwnet_device_mutex);
 
@@ -1592,22 +1557,14 @@ static int fwnet_remove(struct device *_dev)
        mutex_lock(&fwnet_device_mutex);
 
        net = dev->netdev;
-       if (net && peer->ip)
-               arp_invalidate(net, peer->ip);
 
        fwnet_remove_peer(peer, dev);
 
        if (list_empty(&dev->peer_list)) {
                unregister_netdev(net);
 
-               if (dev->local_fifo != FWNET_NO_FIFO_ADDR)
-                       fw_core_remove_address_handler(&dev->handler);
-               if (dev->broadcast_rcv_context) {
-                       fw_iso_context_stop(dev->broadcast_rcv_context);
-                       fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer,
-                                             dev->card);
-                       fw_iso_context_destroy(dev->broadcast_rcv_context);
-               }
+               fwnet_fifo_stop(dev);
+
                for (i = 0; dev->queued_datagrams && i < 5; i++)
                        ssleep(1);
                WARN_ON(dev->queued_datagrams);
@@ -1646,6 +1603,14 @@ static const struct ieee1394_device_id fwnet_id_table[] = {
                .specifier_id = IANA_SPECIFIER_ID,
                .version      = RFC2734_SW_VERSION,
        },
+#if IS_ENABLED(CONFIG_IPV6)
+       {
+               .match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
+                               IEEE1394_MATCH_VERSION,
+               .specifier_id = IANA_SPECIFIER_ID,
+               .version      = RFC3146_SW_VERSION,
+       },
+#endif
        { }
 };
 
@@ -1683,6 +1648,30 @@ static struct fw_descriptor rfc2374_unit_directory = {
        .data   = rfc2374_unit_directory_data
 };
 
+#if IS_ENABLED(CONFIG_IPV6)
+static const u32 rfc3146_unit_directory_data[] = {
+       0x00040000,     /* directory_length             */
+       0x1200005e,     /* unit_specifier_id: IANA      */
+       0x81000003,     /* textual descriptor offset    */
+       0x13000002,     /* unit_sw_version: RFC 3146    */
+       0x81000005,     /* textual descriptor offset    */
+       0x00030000,     /* descriptor_length            */
+       0x00000000,     /* text                         */
+       0x00000000,     /* minimal ASCII, en            */
+       0x49414e41,     /* I A N A                      */
+       0x00030000,     /* descriptor_length            */
+       0x00000000,     /* text                         */
+       0x00000000,     /* minimal ASCII, en            */
+       0x49507636,     /* I P v 6                      */
+};
+
+static struct fw_descriptor rfc3146_unit_directory = {
+       .length = ARRAY_SIZE(rfc3146_unit_directory_data),
+       .key    = (CSR_DIRECTORY | CSR_UNIT) << 24,
+       .data   = rfc3146_unit_directory_data
+};
+#endif
+
 static int __init fwnet_init(void)
 {
        int err;
@@ -1691,11 +1680,17 @@ static int __init fwnet_init(void)
        if (err)
                return err;
 
+#if IS_ENABLED(CONFIG_IPV6)
+       err = fw_core_add_descriptor(&rfc3146_unit_directory);
+       if (err)
+               goto out;
+#endif
+
        fwnet_packet_task_cache = kmem_cache_create("packet_task",
                        sizeof(struct fwnet_packet_task), 0, 0, NULL);
        if (!fwnet_packet_task_cache) {
                err = -ENOMEM;
-               goto out;
+               goto out2;
        }
 
        err = driver_register(&fwnet_driver.driver);
@@ -1703,7 +1698,11 @@ static int __init fwnet_init(void)
                return 0;
 
        kmem_cache_destroy(fwnet_packet_task_cache);
+out2:
+#if IS_ENABLED(CONFIG_IPV6)
+       fw_core_remove_descriptor(&rfc3146_unit_directory);
 out:
+#endif
        fw_core_remove_descriptor(&rfc2374_unit_directory);
 
        return err;
@@ -1714,11 +1713,14 @@ static void __exit fwnet_cleanup(void)
 {
        driver_unregister(&fwnet_driver.driver);
        kmem_cache_destroy(fwnet_packet_task_cache);
+#if IS_ENABLED(CONFIG_IPV6)
+       fw_core_remove_descriptor(&rfc3146_unit_directory);
+#endif
        fw_core_remove_descriptor(&rfc2374_unit_directory);
 }
 module_exit(fwnet_cleanup);
 
 MODULE_AUTHOR("Jay Fenlason <fenlason@redhat.com>");
-MODULE_DESCRIPTION("IPv4 over IEEE1394 as per RFC 2734");
+MODULE_DESCRIPTION("IP over IEEE1394 as per RFC 2734/3146");
 MODULE_LICENSE("GPL");
 MODULE_DEVICE_TABLE(ieee1394, fwnet_id_table);
index a3fde52840ca29b5c046e11533a86b2dcdad67b3..65c30ea8c1a156f0f7d81bc4e039723b321be8dd 100644 (file)
@@ -511,12 +511,16 @@ static unsigned int select_ntuple(struct c4iw_dev *dev, struct dst_entry *dst,
 static int send_connect(struct c4iw_ep *ep)
 {
        struct cpl_act_open_req *req;
+       struct cpl_t5_act_open_req *t5_req;
        struct sk_buff *skb;
        u64 opt0;
        u32 opt2;
        unsigned int mtu_idx;
        int wscale;
-       int wrlen = roundup(sizeof *req, 16);
+       int size = is_t4(ep->com.dev->rdev.lldi.adapter_type) ?
+               sizeof(struct cpl_act_open_req) :
+               sizeof(struct cpl_t5_act_open_req);
+       int wrlen = roundup(size, 16);
 
        PDBG("%s ep %p atid %u\n", __func__, ep, ep->atid);
 
@@ -552,17 +556,36 @@ static int send_connect(struct c4iw_ep *ep)
                opt2 |= WND_SCALE_EN(1);
        t4_set_arp_err_handler(skb, NULL, act_open_req_arp_failure);
 
-       req = (struct cpl_act_open_req *) skb_put(skb, wrlen);
-       INIT_TP_WR(req, 0);
-       OPCODE_TID(req) = cpu_to_be32(
-               MK_OPCODE_TID(CPL_ACT_OPEN_REQ, ((ep->rss_qid<<14)|ep->atid)));
-       req->local_port = ep->com.local_addr.sin_port;
-       req->peer_port = ep->com.remote_addr.sin_port;
-       req->local_ip = ep->com.local_addr.sin_addr.s_addr;
-       req->peer_ip = ep->com.remote_addr.sin_addr.s_addr;
-       req->opt0 = cpu_to_be64(opt0);
-       req->params = cpu_to_be32(select_ntuple(ep->com.dev, ep->dst, ep->l2t));
-       req->opt2 = cpu_to_be32(opt2);
+       if (is_t4(ep->com.dev->rdev.lldi.adapter_type)) {
+               req = (struct cpl_act_open_req *) skb_put(skb, wrlen);
+               INIT_TP_WR(req, 0);
+               OPCODE_TID(req) = cpu_to_be32(
+                               MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
+                               ((ep->rss_qid << 14) | ep->atid)));
+               req->local_port = ep->com.local_addr.sin_port;
+               req->peer_port = ep->com.remote_addr.sin_port;
+               req->local_ip = ep->com.local_addr.sin_addr.s_addr;
+               req->peer_ip = ep->com.remote_addr.sin_addr.s_addr;
+               req->opt0 = cpu_to_be64(opt0);
+               req->params = cpu_to_be32(select_ntuple(ep->com.dev,
+                                       ep->dst, ep->l2t));
+               req->opt2 = cpu_to_be32(opt2);
+       } else {
+               t5_req = (struct cpl_t5_act_open_req *) skb_put(skb, wrlen);
+               INIT_TP_WR(t5_req, 0);
+               OPCODE_TID(t5_req) = cpu_to_be32(
+                                       MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
+                                       ((ep->rss_qid << 14) | ep->atid)));
+               t5_req->local_port = ep->com.local_addr.sin_port;
+               t5_req->peer_port = ep->com.remote_addr.sin_port;
+               t5_req->local_ip = ep->com.local_addr.sin_addr.s_addr;
+               t5_req->peer_ip = ep->com.remote_addr.sin_addr.s_addr;
+               t5_req->opt0 = cpu_to_be64(opt0);
+               t5_req->params = cpu_to_be64(V_FILTER_TUPLE(
+                               select_ntuple(ep->com.dev, ep->dst, ep->l2t)));
+               t5_req->opt2 = cpu_to_be32(opt2);
+       }
+
        set_bit(ACT_OPEN_REQ, &ep->com.history);
        return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t);
 }
@@ -1676,9 +1699,9 @@ static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
        case CPL_ERR_CONN_TIMEDOUT:
                break;
        case CPL_ERR_TCAM_FULL:
+               dev->rdev.stats.tcam_full++;
                if (dev->rdev.lldi.enable_fw_ofld_conn) {
                        mutex_lock(&dev->rdev.stats.lock);
-                       dev->rdev.stats.tcam_full++;
                        mutex_unlock(&dev->rdev.stats.lock);
                        send_fw_act_open_req(ep,
                                             GET_TID_TID(GET_AOPEN_ATID(
@@ -2875,12 +2898,14 @@ static int deferred_fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb)
 static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos)
 {
        u32 l2info;
-       u16 vlantag, len, hdr_len;
+       u16 vlantag, len, hdr_len, eth_hdr_len;
        u8 intf;
        struct cpl_rx_pkt *cpl = cplhdr(skb);
        struct cpl_pass_accept_req *req;
        struct tcp_options_received tmp_opt;
+       struct c4iw_dev *dev;
 
+       dev = *((struct c4iw_dev **) (skb->cb + sizeof(void *)));
        /* Store values from cpl_rx_pkt in temporary location. */
        vlantag = (__force u16) cpl->vlan;
        len = (__force u16) cpl->len;
@@ -2896,7 +2921,7 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos)
         */
        memset(&tmp_opt, 0, sizeof(tmp_opt));
        tcp_clear_options(&tmp_opt);
-       tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL);
+       tcp_parse_options(skb, &tmp_opt, 0, NULL);
 
        req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req));
        memset(req, 0, sizeof(*req));
@@ -2904,14 +2929,16 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos)
                         V_SYN_MAC_IDX(G_RX_MACIDX(
                         (__force int) htonl(l2info))) |
                         F_SYN_XACT_MATCH);
+       eth_hdr_len = is_t4(dev->rdev.lldi.adapter_type) ?
+                           G_RX_ETHHDR_LEN((__force int) htonl(l2info)) :
+                           G_RX_T5_ETHHDR_LEN((__force int) htonl(l2info));
        req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(G_RX_CHAN(
                                        (__force int) htonl(l2info))) |
                                   V_TCP_HDR_LEN(G_RX_TCPHDR_LEN(
                                        (__force int) htons(hdr_len))) |
                                   V_IP_HDR_LEN(G_RX_IPHDR_LEN(
                                        (__force int) htons(hdr_len))) |
-                                  V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(
-                                       (__force int) htonl(l2info))));
+                                  V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(eth_hdr_len)));
        req->vlan = (__force __be16) vlantag;
        req->len = (__force __be16) len;
        req->tos_stid = cpu_to_be32(PASS_OPEN_TID(stid) |
@@ -2999,7 +3026,7 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb)
        u16 window;
        struct port_info *pi;
        struct net_device *pdev;
-       u16 rss_qid;
+       u16 rss_qid, eth_hdr_len;
        int step;
        u32 tx_chan;
        struct neighbour *neigh;
@@ -3028,7 +3055,10 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb)
                goto reject;
        }
 
-       if (G_RX_ETHHDR_LEN(ntohl(cpl->l2info)) == ETH_HLEN) {
+       eth_hdr_len = is_t4(dev->rdev.lldi.adapter_type) ?
+                           G_RX_ETHHDR_LEN(htonl(cpl->l2info)) :
+                           G_RX_T5_ETHHDR_LEN(htonl(cpl->l2info));
+       if (eth_hdr_len == ETH_HLEN) {
                eh = (struct ethhdr *)(req + 1);
                iph = (struct iphdr *)(eh + 1);
        } else {
index 80069ad595c143093a3ff68d471fa1c33edba20e..ae656016e1ae968bdf4fff69e3c1d39de3a87e50 100644 (file)
 #define DRV_VERSION "0.1"
 
 MODULE_AUTHOR("Steve Wise");
-MODULE_DESCRIPTION("Chelsio T4 RDMA Driver");
+MODULE_DESCRIPTION("Chelsio T4/T5 RDMA Driver");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION(DRV_VERSION);
 
+static int allow_db_fc_on_t5;
+module_param(allow_db_fc_on_t5, int, 0644);
+MODULE_PARM_DESC(allow_db_fc_on_t5,
+                "Allow DB Flow Control on T5 (default = 0)");
+
+static int allow_db_coalescing_on_t5;
+module_param(allow_db_coalescing_on_t5, int, 0644);
+MODULE_PARM_DESC(allow_db_coalescing_on_t5,
+                "Allow DB Coalescing on T5 (default = 0)");
+
 struct uld_ctx {
        struct list_head entry;
        struct cxgb4_lld_info lldi;
@@ -614,7 +624,7 @@ static int rdma_supported(const struct cxgb4_lld_info *infop)
 {
        return infop->vr->stag.size > 0 && infop->vr->pbl.size > 0 &&
               infop->vr->rq.size > 0 && infop->vr->qp.size > 0 &&
-              infop->vr->cq.size > 0 && infop->vr->ocq.size > 0;
+              infop->vr->cq.size > 0;
 }
 
 static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
@@ -627,6 +637,22 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
                       pci_name(infop->pdev));
                return ERR_PTR(-ENOSYS);
        }
+       if (!ocqp_supported(infop))
+               pr_info("%s: On-Chip Queues not supported on this device.\n",
+                       pci_name(infop->pdev));
+
+       if (!is_t4(infop->adapter_type)) {
+               if (!allow_db_fc_on_t5) {
+                       db_fc_threshold = 100000;
+                       pr_info("DB Flow Control Disabled.\n");
+               }
+
+               if (!allow_db_coalescing_on_t5) {
+                       db_coalescing_threshold = -1;
+                       pr_info("DB Coalescing Disabled.\n");
+               }
+       }
+
        devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp));
        if (!devp) {
                printk(KERN_ERR MOD "Cannot allocate ib device\n");
@@ -678,8 +704,8 @@ static void *c4iw_uld_add(const struct cxgb4_lld_info *infop)
        int i;
 
        if (!vers_printed++)
-               printk(KERN_INFO MOD "Chelsio T4 RDMA Driver - version %s\n",
-                      DRV_VERSION);
+               pr_info("Chelsio T4/T5 RDMA Driver - version %s\n",
+                       DRV_VERSION);
 
        ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
        if (!ctx) {
index 7eec5e13fa8c34c372659f81878b4338596ab5a0..485183ad34cd7ab7b2db8c2e310974fb319bd616 100644 (file)
@@ -162,7 +162,7 @@ static inline int c4iw_num_stags(struct c4iw_rdev *rdev)
        return min((int)T4_MAX_NUM_STAG, (int)(rdev->lldi.vr->stag.size >> 5));
 }
 
-#define C4IW_WR_TO (10*HZ)
+#define C4IW_WR_TO (30*HZ)
 
 struct c4iw_wr_wait {
        struct completion completion;
@@ -369,7 +369,6 @@ struct c4iw_fr_page_list {
        DEFINE_DMA_UNMAP_ADDR(mapping);
        dma_addr_t dma_addr;
        struct c4iw_dev *dev;
-       int size;
 };
 
 static inline struct c4iw_fr_page_list *to_c4iw_fr_page_list(
@@ -817,6 +816,15 @@ static inline int compute_wscale(int win)
        return wscale;
 }
 
+static inline int ocqp_supported(const struct cxgb4_lld_info *infop)
+{
+#if defined(__i386__) || defined(__x86_64__) || defined(CONFIG_PPC64)
+       return infop->vr->ocq.size > 0;
+#else
+       return 0;
+#endif
+}
+
 u32 c4iw_id_alloc(struct c4iw_id_table *alloc);
 void c4iw_id_free(struct c4iw_id_table *alloc, u32 obj);
 int c4iw_id_table_alloc(struct c4iw_id_table *alloc, u32 start, u32 num,
@@ -930,6 +938,8 @@ extern struct cxgb4_client t4c_client;
 extern c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS];
 extern int c4iw_max_read_depth;
 extern int db_fc_threshold;
+extern int db_coalescing_threshold;
+extern int use_dsgl;
 
 
 #endif
index 903a92d6f91dc3ad5e0147ea85d0a07f2ab84505..4cb8eb24497cfa61821b4863c4026ff5a061405a 100644 (file)
  * SOFTWARE.
  */
 
+#include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <rdma/ib_umem.h>
 #include <linux/atomic.h>
 
 #include "iw_cxgb4.h"
 
+int use_dsgl = 1;
+module_param(use_dsgl, int, 0644);
+MODULE_PARM_DESC(use_dsgl, "Use DSGL for PBL/FastReg (default=1)");
+
 #define T4_ULPTX_MIN_IO 32
 #define C4IW_MAX_INLINE_SIZE 96
+#define T4_ULPTX_MAX_DMA 1024
+#define C4IW_INLINE_THRESHOLD 128
 
-static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
-                            void *data)
+static int inline_threshold = C4IW_INLINE_THRESHOLD;
+module_param(inline_threshold, int, 0644);
+MODULE_PARM_DESC(inline_threshold, "inline vs dsgl threshold (default=128)");
+
+static int _c4iw_write_mem_dma_aligned(struct c4iw_rdev *rdev, u32 addr,
+                                      u32 len, dma_addr_t data, int wait)
+{
+       struct sk_buff *skb;
+       struct ulp_mem_io *req;
+       struct ulptx_sgl *sgl;
+       u8 wr_len;
+       int ret = 0;
+       struct c4iw_wr_wait wr_wait;
+
+       addr &= 0x7FFFFFF;
+
+       if (wait)
+               c4iw_init_wr_wait(&wr_wait);
+       wr_len = roundup(sizeof(*req) + sizeof(*sgl), 16);
+
+       skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL);
+       if (!skb)
+               return -ENOMEM;
+       set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0);
+
+       req = (struct ulp_mem_io *)__skb_put(skb, wr_len);
+       memset(req, 0, wr_len);
+       INIT_ULPTX_WR(req, wr_len, 0, 0);
+       req->wr.wr_hi = cpu_to_be32(FW_WR_OP(FW_ULPTX_WR) |
+                       (wait ? FW_WR_COMPL(1) : 0));
+       req->wr.wr_lo = wait ? (__force __be64)&wr_wait : 0;
+       req->wr.wr_mid = cpu_to_be32(FW_WR_LEN16(DIV_ROUND_UP(wr_len, 16)));
+       req->cmd = cpu_to_be32(ULPTX_CMD(ULP_TX_MEM_WRITE));
+       req->cmd |= cpu_to_be32(V_T5_ULP_MEMIO_ORDER(1));
+       req->dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN(len>>5));
+       req->len16 = cpu_to_be32(DIV_ROUND_UP(wr_len-sizeof(req->wr), 16));
+       req->lock_addr = cpu_to_be32(ULP_MEMIO_ADDR(addr));
+
+       sgl = (struct ulptx_sgl *)(req + 1);
+       sgl->cmd_nsge = cpu_to_be32(ULPTX_CMD(ULP_TX_SC_DSGL) |
+                                   ULPTX_NSGE(1));
+       sgl->len0 = cpu_to_be32(len);
+       sgl->addr0 = cpu_to_be64(data);
+
+       ret = c4iw_ofld_send(rdev, skb);
+       if (ret)
+               return ret;
+       if (wait)
+               ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, 0, __func__);
+       return ret;
+}
+
+static int _c4iw_write_mem_inline(struct c4iw_rdev *rdev, u32 addr, u32 len,
+                                 void *data)
 {
        struct sk_buff *skb;
        struct ulp_mem_io *req;
@@ -47,6 +107,12 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
        u8 wr_len, *to_dp, *from_dp;
        int copy_len, num_wqe, i, ret = 0;
        struct c4iw_wr_wait wr_wait;
+       __be32 cmd = cpu_to_be32(ULPTX_CMD(ULP_TX_MEM_WRITE));
+
+       if (is_t4(rdev->lldi.adapter_type))
+               cmd |= cpu_to_be32(ULP_MEMIO_ORDER(1));
+       else
+               cmd |= cpu_to_be32(V_T5_ULP_MEMIO_IMM(1));
 
        addr &= 0x7FFFFFF;
        PDBG("%s addr 0x%x len %u\n", __func__, addr, len);
@@ -77,7 +143,7 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
                req->wr.wr_mid = cpu_to_be32(
                                       FW_WR_LEN16(DIV_ROUND_UP(wr_len, 16)));
 
-               req->cmd = cpu_to_be32(ULPTX_CMD(ULP_TX_MEM_WRITE) | (1<<23));
+               req->cmd = cmd;
                req->dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN(
                                DIV_ROUND_UP(copy_len, T4_ULPTX_MIN_IO)));
                req->len16 = cpu_to_be32(DIV_ROUND_UP(wr_len-sizeof(req->wr),
@@ -107,6 +173,67 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
        return ret;
 }
 
+int _c4iw_write_mem_dma(struct c4iw_rdev *rdev, u32 addr, u32 len, void *data)
+{
+       u32 remain = len;
+       u32 dmalen;
+       int ret = 0;
+       dma_addr_t daddr;
+       dma_addr_t save;
+
+       daddr = dma_map_single(&rdev->lldi.pdev->dev, data, len, DMA_TO_DEVICE);
+       if (dma_mapping_error(&rdev->lldi.pdev->dev, daddr))
+               return -1;
+       save = daddr;
+
+       while (remain > inline_threshold) {
+               if (remain < T4_ULPTX_MAX_DMA) {
+                       if (remain & ~T4_ULPTX_MIN_IO)
+                               dmalen = remain & ~(T4_ULPTX_MIN_IO-1);
+                       else
+                               dmalen = remain;
+               } else
+                       dmalen = T4_ULPTX_MAX_DMA;
+               remain -= dmalen;
+               ret = _c4iw_write_mem_dma_aligned(rdev, addr, dmalen, daddr,
+                                                !remain);
+               if (ret)
+                       goto out;
+               addr += dmalen >> 5;
+               data += dmalen;
+               daddr += dmalen;
+       }
+       if (remain)
+               ret = _c4iw_write_mem_inline(rdev, addr, remain, data);
+out:
+       dma_unmap_single(&rdev->lldi.pdev->dev, save, len, DMA_TO_DEVICE);
+       return ret;
+}
+
+/*
+ * write len bytes of data into addr (32B aligned address)
+ * If data is NULL, clear len byte of memory to zero.
+ */
+static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
+                            void *data)
+{
+       if (is_t5(rdev->lldi.adapter_type) && use_dsgl) {
+               if (len > inline_threshold) {
+                       if (_c4iw_write_mem_dma(rdev, addr, len, data)) {
+                               printk_ratelimited(KERN_WARNING
+                                                  "%s: dma map"
+                                                  " failure (non fatal)\n",
+                                                  pci_name(rdev->lldi.pdev));
+                               return _c4iw_write_mem_inline(rdev, addr, len,
+                                                             data);
+                       } else
+                               return 0;
+               } else
+                       return _c4iw_write_mem_inline(rdev, addr, len, data);
+       } else
+               return _c4iw_write_mem_inline(rdev, addr, len, data);
+}
+
 /*
  * Build and write a TPT entry.
  * IN: stag key, pdid, perm, bind_enabled, zbva, to, len, page_size,
@@ -760,19 +887,23 @@ struct ib_fast_reg_page_list *c4iw_alloc_fastreg_pbl(struct ib_device *device,
        struct c4iw_fr_page_list *c4pl;
        struct c4iw_dev *dev = to_c4iw_dev(device);
        dma_addr_t dma_addr;
-       int size = sizeof *c4pl + page_list_len * sizeof(u64);
+       int pll_len = roundup(page_list_len * sizeof(u64), 32);
 
-       c4pl = dma_alloc_coherent(&dev->rdev.lldi.pdev->dev, size,
-                                 &dma_addr, GFP_KERNEL);
+       c4pl = kmalloc(sizeof(*c4pl), GFP_KERNEL);
        if (!c4pl)
                return ERR_PTR(-ENOMEM);
 
+       c4pl->ibpl.page_list = dma_alloc_coherent(&dev->rdev.lldi.pdev->dev,
+                                                 pll_len, &dma_addr,
+                                                 GFP_KERNEL);
+       if (!c4pl->ibpl.page_list) {
+               kfree(c4pl);
+               return ERR_PTR(-ENOMEM);
+       }
        dma_unmap_addr_set(c4pl, mapping, dma_addr);
        c4pl->dma_addr = dma_addr;
        c4pl->dev = dev;
-       c4pl->size = size;
-       c4pl->ibpl.page_list = (u64 *)(c4pl + 1);
-       c4pl->ibpl.max_page_list_len = page_list_len;
+       c4pl->ibpl.max_page_list_len = pll_len;
 
        return &c4pl->ibpl;
 }
@@ -781,8 +912,10 @@ void c4iw_free_fastreg_pbl(struct ib_fast_reg_page_list *ibpl)
 {
        struct c4iw_fr_page_list *c4pl = to_c4iw_fr_page_list(ibpl);
 
-       dma_free_coherent(&c4pl->dev->rdev.lldi.pdev->dev, c4pl->size,
-                         c4pl, dma_unmap_addr(c4pl, mapping));
+       dma_free_coherent(&c4pl->dev->rdev.lldi.pdev->dev,
+                         c4pl->ibpl.max_page_list_len,
+                         c4pl->ibpl.page_list, dma_unmap_addr(c4pl, mapping));
+       kfree(c4pl);
 }
 
 int c4iw_dereg_mr(struct ib_mr *ib_mr)
index e084fdc6da7f0b12a0cca43e13d0e1327de9c72d..7e94c9a656a1429d37c14bd2e7b8f4b8590d494e 100644 (file)
@@ -162,8 +162,14 @@ static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
                 */
                if (addr >= rdev->oc_mw_pa)
                        vma->vm_page_prot = t4_pgprot_wc(vma->vm_page_prot);
-               else
-                       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+               else {
+                       if (is_t5(rdev->lldi.adapter_type))
+                               vma->vm_page_prot =
+                                       t4_pgprot_wc(vma->vm_page_prot);
+                       else
+                               vma->vm_page_prot =
+                                       pgprot_noncached(vma->vm_page_prot);
+               }
                ret = io_remap_pfn_range(vma, vma->vm_start,
                                         addr >> PAGE_SHIFT,
                                         len, vma->vm_page_prot);
@@ -263,7 +269,7 @@ static int c4iw_query_device(struct ib_device *ibdev,
        dev = to_c4iw_dev(ibdev);
        memset(props, 0, sizeof *props);
        memcpy(&props->sys_image_guid, dev->rdev.lldi.ports[0]->dev_addr, 6);
-       props->hw_ver = dev->rdev.lldi.adapter_type;
+       props->hw_ver = CHELSIO_CHIP_RELEASE(dev->rdev.lldi.adapter_type);
        props->fw_ver = dev->rdev.lldi.fw_vers;
        props->device_cap_flags = dev->device_cap_flags;
        props->page_size_cap = T4_PAGESIZE_MASK;
@@ -346,7 +352,8 @@ static ssize_t show_rev(struct device *dev, struct device_attribute *attr,
        struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev,
                                                 ibdev.dev);
        PDBG("%s dev 0x%p\n", __func__, dev);
-       return sprintf(buf, "%d\n", c4iw_dev->rdev.lldi.adapter_type);
+       return sprintf(buf, "%d\n",
+                      CHELSIO_CHIP_RELEASE(c4iw_dev->rdev.lldi.adapter_type));
 }
 
 static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
index 70b1808a08f4d12bf9d1668cb5bae06d1cd2e202..5b059e2d80cc18019b060a7143bdf59098879ef0 100644 (file)
@@ -42,10 +42,21 @@ static int ocqp_support = 1;
 module_param(ocqp_support, int, 0644);
 MODULE_PARM_DESC(ocqp_support, "Support on-chip SQs (default=1)");
 
-int db_fc_threshold = 2000;
+int db_fc_threshold = 1000;
 module_param(db_fc_threshold, int, 0644);
-MODULE_PARM_DESC(db_fc_threshold, "QP count/threshold that triggers automatic "
-                "db flow control mode (default = 2000)");
+MODULE_PARM_DESC(db_fc_threshold,
+                "QP count/threshold that triggers"
+                " automatic db flow control mode (default = 1000)");
+
+int db_coalescing_threshold;
+module_param(db_coalescing_threshold, int, 0644);
+MODULE_PARM_DESC(db_coalescing_threshold,
+                "QP count/threshold that triggers"
+                " disabling db coalescing (default = 0)");
+
+static int max_fr_immd = T4_MAX_FR_IMMD;
+module_param(max_fr_immd, int, 0644);
+MODULE_PARM_DESC(max_fr_immd, "fastreg threshold for using DSGL instead of immedate");
 
 static void set_state(struct c4iw_qp *qhp, enum c4iw_qp_state state)
 {
@@ -76,7 +87,7 @@ static void dealloc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
 
 static int alloc_oc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
 {
-       if (!ocqp_support || !t4_ocqp_supported())
+       if (!ocqp_support || !ocqp_supported(&rdev->lldi))
                return -ENOSYS;
        sq->dma_addr = c4iw_ocqp_pool_alloc(rdev, sq->memsize);
        if (!sq->dma_addr)
@@ -129,7 +140,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
        int wr_len;
        struct c4iw_wr_wait wr_wait;
        struct sk_buff *skb;
-       int ret;
+       int ret = 0;
        int eqsize;
 
        wq->sq.qid = c4iw_get_qpid(rdev, uctx);
@@ -169,17 +180,14 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
        }
 
        if (user) {
-               ret = alloc_oc_sq(rdev, &wq->sq);
-               if (ret)
+               if (alloc_oc_sq(rdev, &wq->sq) && alloc_host_sq(rdev, &wq->sq))
                        goto free_hwaddr;
-
-               ret = alloc_host_sq(rdev, &wq->sq);
-               if (ret)
-                       goto free_sq;
-       } else
+       } else {
                ret = alloc_host_sq(rdev, &wq->sq);
                if (ret)
                        goto free_hwaddr;
+       }
+
        memset(wq->sq.queue, 0, wq->sq.memsize);
        dma_unmap_addr_set(&wq->sq, mapping, wq->sq.dma_addr);
 
@@ -534,7 +542,7 @@ static int build_rdma_recv(struct c4iw_qp *qhp, union t4_recv_wr *wqe,
 }
 
 static int build_fastreg(struct t4_sq *sq, union t4_wr *wqe,
-                        struct ib_send_wr *wr, u8 *len16)
+                        struct ib_send_wr *wr, u8 *len16, u8 t5dev)
 {
 
        struct fw_ri_immd *imdp;
@@ -556,28 +564,51 @@ static int build_fastreg(struct t4_sq *sq, union t4_wr *wqe,
        wqe->fr.va_hi = cpu_to_be32(wr->wr.fast_reg.iova_start >> 32);
        wqe->fr.va_lo_fbo = cpu_to_be32(wr->wr.fast_reg.iova_start &
                                        0xffffffff);
-       WARN_ON(pbllen > T4_MAX_FR_IMMD);
-       imdp = (struct fw_ri_immd *)(&wqe->fr + 1);
-       imdp->op = FW_RI_DATA_IMMD;
-       imdp->r1 = 0;
-       imdp->r2 = 0;
-       imdp->immdlen = cpu_to_be32(pbllen);
-       p = (__be64 *)(imdp + 1);
-       rem = pbllen;
-       for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) {
-               *p = cpu_to_be64((u64)wr->wr.fast_reg.page_list->page_list[i]);
-               rem -= sizeof *p;
-               if (++p == (__be64 *)&sq->queue[sq->size])
-                       p = (__be64 *)sq->queue;
-       }
-       BUG_ON(rem < 0);
-       while (rem) {
-               *p = 0;
-               rem -= sizeof *p;
-               if (++p == (__be64 *)&sq->queue[sq->size])
-                       p = (__be64 *)sq->queue;
+
+       if (t5dev && use_dsgl && (pbllen > max_fr_immd)) {
+               struct c4iw_fr_page_list *c4pl =
+                       to_c4iw_fr_page_list(wr->wr.fast_reg.page_list);
+               struct fw_ri_dsgl *sglp;
+
+               for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) {
+                       wr->wr.fast_reg.page_list->page_list[i] = (__force u64)
+                               cpu_to_be64((u64)
+                               wr->wr.fast_reg.page_list->page_list[i]);
+               }
+
+               sglp = (struct fw_ri_dsgl *)(&wqe->fr + 1);
+               sglp->op = FW_RI_DATA_DSGL;
+               sglp->r1 = 0;
+               sglp->nsge = cpu_to_be16(1);
+               sglp->addr0 = cpu_to_be64(c4pl->dma_addr);
+               sglp->len0 = cpu_to_be32(pbllen);
+
+               *len16 = DIV_ROUND_UP(sizeof(wqe->fr) + sizeof(*sglp), 16);
+       } else {
+               imdp = (struct fw_ri_immd *)(&wqe->fr + 1);
+               imdp->op = FW_RI_DATA_IMMD;
+               imdp->r1 = 0;
+               imdp->r2 = 0;
+               imdp->immdlen = cpu_to_be32(pbllen);
+               p = (__be64 *)(imdp + 1);
+               rem = pbllen;
+               for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) {
+                       *p = cpu_to_be64(
+                               (u64)wr->wr.fast_reg.page_list->page_list[i]);
+                       rem -= sizeof(*p);
+                       if (++p == (__be64 *)&sq->queue[sq->size])
+                               p = (__be64 *)sq->queue;
+               }
+               BUG_ON(rem < 0);
+               while (rem) {
+                       *p = 0;
+                       rem -= sizeof(*p);
+                       if (++p == (__be64 *)&sq->queue[sq->size])
+                               p = (__be64 *)sq->queue;
+               }
+               *len16 = DIV_ROUND_UP(sizeof(wqe->fr) + sizeof(*imdp)
+                                     + pbllen, 16);
        }
-       *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *imdp + pbllen, 16);
        return 0;
 }
 
@@ -678,7 +709,10 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                case IB_WR_FAST_REG_MR:
                        fw_opcode = FW_RI_FR_NSMR_WR;
                        swsqe->opcode = FW_RI_FAST_REGISTER;
-                       err = build_fastreg(&qhp->wq.sq, wqe, wr, &len16);
+                       err = build_fastreg(&qhp->wq.sq, wqe, wr, &len16,
+                                           is_t5(
+                                           qhp->rhp->rdev.lldi.adapter_type) ?
+                                           1 : 0);
                        break;
                case IB_WR_LOCAL_INV:
                        if (wr->send_flags & IB_SEND_FENCE)
@@ -1450,6 +1484,9 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp)
                rhp->db_state = NORMAL;
                idr_for_each(&rhp->qpidr, enable_qp_db, NULL);
        }
+       if (db_coalescing_threshold >= 0)
+               if (rhp->qpcnt <= db_coalescing_threshold)
+                       cxgb4_enable_db_coalescing(rhp->rdev.lldi.ports[0]);
        spin_unlock_irq(&rhp->lock);
        atomic_dec(&qhp->refcnt);
        wait_event(qhp->wait, !atomic_read(&qhp->refcnt));
@@ -1561,11 +1598,15 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
        spin_lock_irq(&rhp->lock);
        if (rhp->db_state != NORMAL)
                t4_disable_wq_db(&qhp->wq);
-       if (++rhp->qpcnt > db_fc_threshold && rhp->db_state == NORMAL) {
+       rhp->qpcnt++;
+       if (rhp->qpcnt > db_fc_threshold && rhp->db_state == NORMAL) {
                rhp->rdev.stats.db_state_transitions++;
                rhp->db_state = FLOW_CONTROL;
                idr_for_each(&rhp->qpidr, disable_qp_db, NULL);
        }
+       if (db_coalescing_threshold >= 0)
+               if (rhp->qpcnt > db_coalescing_threshold)
+                       cxgb4_disable_db_coalescing(rhp->rdev.lldi.ports[0]);
        ret = insert_handle_nolock(rhp, &rhp->qpidr, qhp, qhp->wq.sq.qid);
        spin_unlock_irq(&rhp->lock);
        if (ret)
index 16f26ab293022f797a4182ff179961619bad3382..ebcb03bd1b72ed6003bd7ae85e88dd8ce9c5595d 100644 (file)
@@ -84,7 +84,7 @@ struct t4_status_page {
                        sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge))
 #define T4_MAX_FR_IMMD ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_fr_nsmr_wr) - \
                        sizeof(struct fw_ri_immd)) & ~31UL)
-#define T4_MAX_FR_DEPTH (T4_MAX_FR_IMMD / sizeof(u64))
+#define T4_MAX_FR_DEPTH (1024 / sizeof(u64))
 
 #define T4_RQ_NUM_SLOTS 2
 #define T4_RQ_NUM_BYTES (T4_EQ_ENTRY_SIZE * T4_RQ_NUM_SLOTS)
@@ -280,15 +280,6 @@ static inline pgprot_t t4_pgprot_wc(pgprot_t prot)
 #endif
 }
 
-static inline int t4_ocqp_supported(void)
-{
-#if defined(__i386__) || defined(__x86_64__) || defined(CONFIG_PPC64)
-       return 1;
-#else
-       return 0;
-#endif
-}
-
 enum {
        T4_SQ_ONCHIP = (1<<0),
 };
index 67647e264611ecc7d6f7e54b602683d93ed3bd5c..418004c93feb772c641dde6b102620fc2be45900 100644 (file)
@@ -2948,7 +2948,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
                                        nes_debug(NES_DBG_CQ, "%s: Reporting stripped VLAN packet. Tag = 0x%04X\n",
                                                        nesvnic->netdev->name, vlan_tag);
 
-                                       __vlan_hwaccel_put_tag(rx_skb, vlan_tag);
+                                       __vlan_hwaccel_put_tag(rx_skb, htons(ETH_P_8021Q), vlan_tag);
                                }
                                if (nes_use_lro)
                                        lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL);
index 85cf4d1ac4429898e039fb7c58ca805bb9fe9403..49eb5111d2cd0b96c2f8ce8e4260e1fbfd5497cd 100644 (file)
@@ -1599,7 +1599,7 @@ static void nes_vlan_mode(struct net_device *netdev, struct nes_device *nesdev,
 
        /* Enable/Disable VLAN Stripping */
        u32temp = nes_read_indexed(nesdev, NES_IDX_PCIX_DIAG);
-       if (features & NETIF_F_HW_VLAN_RX)
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
                u32temp &= 0xfdffffff;
        else
                u32temp |= 0x02000000;
@@ -1614,10 +1614,10 @@ static netdev_features_t nes_fix_features(struct net_device *netdev, netdev_feat
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -1628,7 +1628,7 @@ static int nes_set_features(struct net_device *netdev, netdev_features_t feature
        struct nes_device *nesdev = nesvnic->nesdev;
        u32 changed = netdev->features ^ features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                nes_vlan_mode(netdev, nesdev, features);
 
        return 0;
@@ -1706,11 +1706,11 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
        netdev->dev_addr[4] = (u8)(u64temp>>8);
        netdev->dev_addr[5] = (u8)u64temp;
 
-       netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX;
+       netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX;
        if ((nesvnic->logical_port < 2) || (nesdev->nesadapter->hw_rev != NE020_REV))
                netdev->hw_features |= NETIF_F_TSO;
 
-       netdev->features = netdev->hw_features | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_TX;
+       netdev->features = netdev->hw_features | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX;
        netdev->hw_features |= NETIF_F_LRO;
 
        nes_debug(NES_DBG_INIT, "nesvnic = %p, reported features = 0x%lX, QPid = %d,"
index 8534afd04e7cbbef29b541202f6fd41da2e7fcfa..554b9063da5492aab4763051146121eca6774e84 100644 (file)
@@ -730,7 +730,8 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
                if ((header->proto != htons(ETH_P_IP)) &&
                    (header->proto != htons(ETH_P_IPV6)) &&
                    (header->proto != htons(ETH_P_ARP)) &&
-                   (header->proto != htons(ETH_P_RARP))) {
+                   (header->proto != htons(ETH_P_RARP)) &&
+                   (header->proto != htons(ETH_P_TIPC))) {
                        /* ethertype not supported by IPoIB */
                        ++dev->stats.tx_dropped;
                        dev_kfree_skb_any(skb);
@@ -751,6 +752,7 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
        switch (header->proto) {
        case htons(ETH_P_IP):
        case htons(ETH_P_IPV6):
+       case htons(ETH_P_TIPC):
                neigh = ipoib_neigh_get(dev, cb->hwaddr);
                if (unlikely(!neigh)) {
                        neigh_add_path(skb, cb->hwaddr, dev);
index 832bc807ed20ec38a565578b209ec363190ad31f..cc9f1927a322ec9d8aa1a5a05cad351711d9f861 100644 (file)
@@ -469,8 +469,7 @@ static int capidrv_add_ack(struct capidrv_ncci *nccip,
 {
        struct ncci_datahandle_queue *n, **pp;
 
-       n = (struct ncci_datahandle_queue *)
-               kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
+       n = kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
        if (!n) {
                printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n");
                return -1;
index db432e635496d1b206f731832900eb17cad304d9..50749a70c5cacb99ee2dfbb0a1957b6baf336247 100644 (file)
@@ -441,8 +441,7 @@ static int isdn_divert_icall(isdn_ctrl *ic)
 
                switch (dv->rule.action) {
                case DEFLECT_IGNORE:
-                       return (0);
-                       break;
+                       return 0;
 
                case DEFLECT_ALERT:
                case DEFLECT_PROCEED:
@@ -510,10 +509,9 @@ static int isdn_divert_icall(isdn_ctrl *ic)
                        break;
 
                default:
-                       return (0); /* ignore call */
-                       break;
+                       return 0; /* ignore call */
                } /* switch action */
-               break;
+               break; /* will break the 'for' looping */
        } /* scan_table */
 
        if (cs) {
index 1bb291021fdbad40ff6c094e4aa23f0cee584112..c7a94713e9ec98fae0c36d61ffdc40cd27884d70 100644 (file)
@@ -26,7 +26,7 @@ FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount)
 {
        int i;
 
-       fsm->jumpmatrix = (FSMFNPTR *)
+       fsm->jumpmatrix =
                kzalloc(sizeof(FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL);
        if (!fsm->jumpmatrix)
                return -ENOMEM;
index 90f34ae2b80fb340401abbe0c9941a3a95cfc736..dc4574f735ef59f24fecbeaf576e58280a56404f 100644 (file)
@@ -1479,7 +1479,7 @@ int setup_hfcsx(struct IsdnCard *card)
                        release_region(cs->hw.hfcsx.base, 2);
                        return (0);
                }
-               if (!(cs->hw.hfcsx.extra = (void *)
+               if (!(cs->hw.hfcsx.extra =
                      kmalloc(sizeof(struct hfcsx_extra), GFP_ATOMIC))) {
                        release_region(cs->hw.hfcsx.base, 2);
                        printk(KERN_WARNING "HFC-SX: unable to allocate memory\n");
index babc621a07fb62c35a905af491294144978d9952..88d657dff47454a3c374469db0ac51d03c53e502 100644 (file)
@@ -1385,7 +1385,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev)
                if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
                        skb->pkt_type = PACKET_OTHERHOST;
        }
-       if (ntohs(eth->h_proto) >= 1536)
+       if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
                return eth->h_proto;
 
        rawp = skb->data;
index 44225b186f6df4a42945a8f9998361558d470451..83a23afb13ab7f82e51f1dcba32b7de317af3557 100644 (file)
@@ -185,7 +185,7 @@ static __be16 dvb_net_eth_type_trans(struct sk_buff *skb,
                        skb->pkt_type=PACKET_MULTICAST;
        }
 
-       if (ntohs(eth->h_proto) >= 1536)
+       if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
                return eth->h_proto;
 
        rawp = skb->data;
@@ -228,9 +228,9 @@ static int ule_test_sndu( struct dvb_net_priv *p )
 static int ule_bridged_sndu( struct dvb_net_priv *p )
 {
        struct ethhdr *hdr = (struct ethhdr*) p->ule_next_hdr;
-       if(ntohs(hdr->h_proto) < 1536) {
+       if(ntohs(hdr->h_proto) < ETH_P_802_3_MIN) {
                int framelen = p->ule_sndu_len - ((p->ule_next_hdr+sizeof(struct ethhdr)) - p->ule_skb->data);
-               /* A frame Type < 1536 for a bridged frame, introduces a LLC Length field. */
+               /* A frame Type < ETH_P_802_3_MIN for a bridged frame, introduces a LLC Length field. */
                if(framelen != ntohs(hdr->h_proto)) {
                        return -1;
                }
@@ -320,7 +320,7 @@ static int handle_ule_extensions( struct dvb_net_priv *p )
                        (int) p->ule_sndu_type, l, total_ext_len);
 #endif
 
-       } while (p->ule_sndu_type < 1536);
+       } while (p->ule_sndu_type < ETH_P_802_3_MIN);
 
        return total_ext_len;
 }
@@ -712,7 +712,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
                                }
 
                                /* Handle ULE Extension Headers. */
-                               if (priv->ule_sndu_type < 1536) {
+                               if (priv->ule_sndu_type < ETH_P_802_3_MIN) {
                                        /* There is an extension header.  Handle it accordingly. */
                                        int l = handle_ule_extensions(priv);
                                        if (l < 0) {
index 87f1d39ca5517b4c4f2777427b46daf952f72a32..3835321b8cf38bbc86b26383d5796964a3c494a5 100644 (file)
@@ -151,6 +151,7 @@ config MACVTAP
 config VXLAN
        tristate "Virtual eXtensible Local Area Network (VXLAN)"
        depends on INET
+       select NET_IP_TUNNEL
        ---help---
          This allows one to create vxlan virtual interfaces that provide
          Layer 2 Networks over Layer 3 Networks. VXLAN is often used
index f5a89164e779498ac1e554c7c253dc20aaccdc42..4ce6ca5f3d365a48ab554c7477b157437fff9357 100644 (file)
@@ -106,20 +106,4 @@ config IPDDP_ENCAP
          IP packets inside AppleTalk frames; this is useful if your Linux box
          is stuck on an AppleTalk network (which hopefully contains a
          decapsulator somewhere). Please see
-         <file:Documentation/networking/ipddp.txt> for more information. If
-         you said Y to "AppleTalk-IP driver support" above and you say Y
-         here, then you cannot say Y to "AppleTalk-IP to IP Decapsulation
-         support", below.
-
-config IPDDP_DECAP
-       bool "Appletalk-IP to IP Decapsulation support"
-       depends on IPDDP
-       help
-         If you say Y here, the AppleTalk-IP code will be able to decapsulate
-         AppleTalk-IP frames to IP packets; this is useful if you want your
-         Linux box to act as an Internet gateway for an AppleTalk network.
-         Please see <file:Documentation/networking/ipddp.txt> for more
-         information.  If you said Y to "AppleTalk-IP driver support" above
-         and you say Y here, then you cannot say Y to "IP to AppleTalk-IP
-         Encapsulation support", above.
-
+         <file:Documentation/networking/ipddp.txt> for more information.
index f5e05272302995cddb352ce4310524047d1a9dbe..e02cc265723abbaf397abbf84b0751d817dc5efd 100644 (file)
@@ -514,7 +514,7 @@ static void rlb_update_client(struct rlb_client_info *client_info)
                skb->dev = client_info->slave->dev;
 
                if (client_info->tag) {
-                       skb = vlan_put_tag(skb, client_info->vlan_id);
+                       skb = vlan_put_tag(skb, htons(ETH_P_8021Q), client_info->vlan_id);
                        if (!skb) {
                                pr_err("%s: Error: failed to insert VLAN tag\n",
                                       client_info->slave->bond->dev->name);
@@ -1014,7 +1014,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
                                continue;
                        }
 
-                       skb = vlan_put_tag(skb, vlan->vlan_id);
+                       skb = vlan_put_tag(skb, htons(ETH_P_8021Q), vlan->vlan_id);
                        if (!skb) {
                                pr_err("%s: Error: failed to insert VLAN tag\n",
                                       bond->dev->name);
index dbbea0eec134f1b2f0225161a33111264450f48e..532153db1f9c7026235b47bd963b8e06391c5dfd 100644 (file)
@@ -428,14 +428,15 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
  * @bond_dev: bonding net device that got called
  * @vid: vlan id being added
  */
-static int bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
+static int bond_vlan_rx_add_vid(struct net_device *bond_dev,
+                               __be16 proto, u16 vid)
 {
        struct bonding *bond = netdev_priv(bond_dev);
        struct slave *slave, *stop_at;
        int i, res;
 
        bond_for_each_slave(bond, slave, i) {
-               res = vlan_vid_add(slave->dev, vid);
+               res = vlan_vid_add(slave->dev, proto, vid);
                if (res)
                        goto unwind;
        }
@@ -453,7 +454,7 @@ unwind:
        /* unwind from head to the slave that failed */
        stop_at = slave;
        bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at)
-               vlan_vid_del(slave->dev, vid);
+               vlan_vid_del(slave->dev, proto, vid);
 
        return res;
 }
@@ -463,14 +464,15 @@ unwind:
  * @bond_dev: bonding net device that got called
  * @vid: vlan id being removed
  */
-static int bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
+static int bond_vlan_rx_kill_vid(struct net_device *bond_dev,
+                                __be16 proto, u16 vid)
 {
        struct bonding *bond = netdev_priv(bond_dev);
        struct slave *slave;
        int i, res;
 
        bond_for_each_slave(bond, slave, i)
-               vlan_vid_del(slave->dev, vid);
+               vlan_vid_del(slave->dev, proto, vid);
 
        res = bond_del_vlan(bond, vid);
        if (res) {
@@ -488,7 +490,8 @@ static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *sla
        int res;
 
        list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
-               res = vlan_vid_add(slave_dev, vlan->vlan_id);
+               res = vlan_vid_add(slave_dev, htons(ETH_P_8021Q),
+                                  vlan->vlan_id);
                if (res)
                        pr_warning("%s: Failed to add vlan id %d to device %s\n",
                                   bond->dev->name, vlan->vlan_id,
@@ -504,7 +507,7 @@ static void bond_del_vlans_from_slave(struct bonding *bond,
        list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
                if (!vlan->vlan_id)
                        continue;
-               vlan_vid_del(slave_dev, vlan->vlan_id);
+               vlan_vid_del(slave_dev, htons(ETH_P_8021Q), vlan->vlan_id);
        }
 }
 
@@ -779,7 +782,7 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
 
        /* rejoin all groups on vlan devices */
        list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
-               vlan_dev = __vlan_find_dev_deep(bond_dev,
+               vlan_dev = __vlan_find_dev_deep(bond_dev, htons(ETH_P_8021Q),
                                                vlan->vlan_id);
                if (vlan_dev)
                        __bond_resend_igmp_join_requests(vlan_dev);
@@ -796,9 +799,8 @@ static void bond_resend_igmp_join_requests_delayed(struct work_struct *work)
 {
        struct bonding *bond = container_of(work, struct bonding,
                                            mcast_work.work);
-       rcu_read_lock();
+
        bond_resend_igmp_join_requests(bond);
-       rcu_read_unlock();
 }
 
 /*
@@ -2532,7 +2534,8 @@ static int bond_has_this_ip(struct bonding *bond, __be32 ip)
 
        list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
                rcu_read_lock();
-               vlan_dev = __vlan_find_dev_deep(bond->dev, vlan->vlan_id);
+               vlan_dev = __vlan_find_dev_deep(bond->dev, htons(ETH_P_8021Q),
+                                               vlan->vlan_id);
                rcu_read_unlock();
                if (vlan_dev && ip == bond_confirm_addr(vlan_dev, 0, ip))
                        return 1;
@@ -2561,7 +2564,7 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_
                return;
        }
        if (vlan_id) {
-               skb = vlan_put_tag(skb, vlan_id);
+               skb = vlan_put_tag(skb, htons(ETH_P_8021Q), vlan_id);
                if (!skb) {
                        pr_err("failed to insert VLAN tag\n");
                        return;
@@ -2623,6 +2626,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
                list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
                        rcu_read_lock();
                        vlan_dev = __vlan_find_dev_deep(bond->dev,
+                                                       htons(ETH_P_8021Q),
                                                        vlan->vlan_id);
                        rcu_read_unlock();
                        if (vlan_dev == rt->dst.dev) {
@@ -4258,6 +4262,37 @@ void bond_set_mode_ops(struct bonding *bond, int mode)
        }
 }
 
+static int bond_ethtool_get_settings(struct net_device *bond_dev,
+                                    struct ethtool_cmd *ecmd)
+{
+       struct bonding *bond = netdev_priv(bond_dev);
+       struct slave *slave;
+       int i;
+       unsigned long speed = 0;
+
+       ecmd->duplex = DUPLEX_UNKNOWN;
+       ecmd->port = PORT_OTHER;
+
+       /* Since SLAVE_IS_OK returns false for all inactive or down slaves, we
+        * do not need to check mode.  Though link speed might not represent
+        * the true receive or transmit bandwidth (not all modes are symmetric)
+        * this is an accurate maximum.
+        */
+       read_lock(&bond->lock);
+       bond_for_each_slave(bond, slave, i) {
+               if (SLAVE_IS_OK(slave)) {
+                       if (slave->speed != SPEED_UNKNOWN)
+                               speed += slave->speed;
+                       if (ecmd->duplex == DUPLEX_UNKNOWN &&
+                           slave->duplex != DUPLEX_UNKNOWN)
+                               ecmd->duplex = slave->duplex;
+               }
+       }
+       ethtool_cmd_speed_set(ecmd, speed ? : SPEED_UNKNOWN);
+       read_unlock(&bond->lock);
+       return 0;
+}
+
 static void bond_ethtool_get_drvinfo(struct net_device *bond_dev,
                                     struct ethtool_drvinfo *drvinfo)
 {
@@ -4269,6 +4304,7 @@ static void bond_ethtool_get_drvinfo(struct net_device *bond_dev,
 
 static const struct ethtool_ops bond_ethtool_ops = {
        .get_drvinfo            = bond_ethtool_get_drvinfo,
+       .get_settings           = bond_ethtool_get_settings,
        .get_link               = ethtool_op_get_link,
 };
 
@@ -4359,9 +4395,9 @@ static void bond_setup(struct net_device *bond_dev)
         */
 
        bond_dev->hw_features = BOND_VLAN_FEATURES |
-                               NETIF_F_HW_VLAN_TX |
-                               NETIF_F_HW_VLAN_RX |
-                               NETIF_F_HW_VLAN_FILTER;
+                               NETIF_F_HW_VLAN_CTAG_TX |
+                               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->features |= bond_dev->hw_features;
index 60c2142373c99a62d1a5029179c559712928ee43..a966128c2a7ab8014126ccad2370b3946a4c0ad7 100644 (file)
@@ -32,13 +32,6 @@ config CAIF_SPI_SYNC
        help to synchronize to the next transfer in case of over or under-runs.
        This option also needs to be enabled on the modem.
 
-config CAIF_SHM
-       tristate "CAIF shared memory protocol driver"
-       depends on CAIF && U5500_MBOX
-       default n
-       ---help---
-       The CAIF shared memory protocol driver for the STE UX5500 platform.
-
 config CAIF_HSI
        tristate "CAIF HSI transport driver"
        depends on CAIF
index 91dff861560f71c8d58010066b614268ce60a935..15a9d2fc753dcc8ef1ea6153b3be59c3ed6d5eb7 100644 (file)
@@ -7,9 +7,5 @@ obj-$(CONFIG_CAIF_TTY) += caif_serial.o
 cfspi_slave-objs := caif_spi.o caif_spi_slave.o
 obj-$(CONFIG_CAIF_SPI_SLAVE) += cfspi_slave.o
 
-# Shared memory
-caif_shm-objs := caif_shmcore.o caif_shm_u5500.o
-obj-$(CONFIG_CAIF_SHM) += caif_shm.o
-
 # HSI interface
 obj-$(CONFIG_CAIF_HSI) += caif_hsi.o
diff --git a/drivers/net/caif/caif_shm_u5500.c b/drivers/net/caif/caif_shm_u5500.c
deleted file mode 100644 (file)
index 89d76b7..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
- * Author:  Amarnath Revanna / amarnath.bangalore.revanna@stericsson.com
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <mach/mbox-db5500.h>
-#include <net/caif/caif_shm.h>
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("CAIF Shared Memory protocol driver");
-
-#define MAX_SHM_INSTANCES      1
-
-enum {
-       MBX_ACC0,
-       MBX_ACC1,
-       MBX_DSP
-};
-
-static struct shmdev_layer shmdev_lyr[MAX_SHM_INSTANCES];
-
-static unsigned int shm_start;
-static unsigned int shm_size;
-
-module_param(shm_size, uint  , 0440);
-MODULE_PARM_DESC(shm_total_size, "Start of SHM shared memory");
-
-module_param(shm_start, uint  , 0440);
-MODULE_PARM_DESC(shm_total_start, "Total Size of SHM shared memory");
-
-static int shmdev_send_msg(u32 dev_id, u32 mbx_msg)
-{
-       /* Always block until msg is written successfully */
-       mbox_send(shmdev_lyr[dev_id].hmbx, mbx_msg, true);
-       return 0;
-}
-
-static int shmdev_mbx_setup(void *pshmdrv_cb, struct shmdev_layer *pshm_dev,
-                                                        void *pshm_drv)
-{
-       /*
-        * For UX5500, we have only 1 SHM instance which uses MBX0
-        * for communication with the peer modem
-        */
-       pshm_dev->hmbx = mbox_setup(MBX_ACC0, pshmdrv_cb, pshm_drv);
-
-       if (!pshm_dev->hmbx)
-               return -ENODEV;
-       else
-               return 0;
-}
-
-static int __init caif_shmdev_init(void)
-{
-       int i, result;
-
-       /* Loop is currently overkill, there is only one instance */
-       for (i = 0; i < MAX_SHM_INSTANCES; i++) {
-
-               shmdev_lyr[i].shm_base_addr = shm_start;
-               shmdev_lyr[i].shm_total_sz = shm_size;
-
-               if (((char *)shmdev_lyr[i].shm_base_addr == NULL)
-                              || (shmdev_lyr[i].shm_total_sz <= 0))    {
-                       pr_warn("ERROR,"
-                               "Shared memory Address and/or Size incorrect"
-                               ", Bailing out ...\n");
-                       result = -EINVAL;
-                       goto clean;
-               }
-
-               pr_info("SHM AREA (instance %d) STARTS"
-                       " AT %p\n", i, (char *)shmdev_lyr[i].shm_base_addr);
-
-               shmdev_lyr[i].shm_id = i;
-               shmdev_lyr[i].pshmdev_mbxsend = shmdev_send_msg;
-               shmdev_lyr[i].pshmdev_mbxsetup = shmdev_mbx_setup;
-
-               /*
-                * Finally, CAIF core module is called with details in place:
-                * 1. SHM base address
-                * 2. SHM size
-                * 3. MBX handle
-                */
-               result = caif_shmcore_probe(&shmdev_lyr[i]);
-               if (result) {
-                       pr_warn("ERROR[%d],"
-                               "Could not probe SHM core (instance %d)"
-                               " Bailing out ...\n", result, i);
-                       goto clean;
-               }
-       }
-
-       return 0;
-
-clean:
-       /*
-        * For now, we assume that even if one instance of SHM fails, we bail
-        * out of the driver support completely. For this, we need to release
-        * any memory allocated and unregister any instance of SHM net device.
-        */
-       for (i = 0; i < MAX_SHM_INSTANCES; i++) {
-               if (shmdev_lyr[i].pshm_netdev)
-                       unregister_netdev(shmdev_lyr[i].pshm_netdev);
-       }
-       return result;
-}
-
-static void __exit caif_shmdev_exit(void)
-{
-       int i;
-
-       for (i = 0; i < MAX_SHM_INSTANCES; i++) {
-               caif_shmcore_remove(shmdev_lyr[i].pshm_netdev);
-               kfree((void *)shmdev_lyr[i].shm_base_addr);
-       }
-
-}
-
-module_init(caif_shmdev_init);
-module_exit(caif_shmdev_exit);
diff --git a/drivers/net/caif/caif_shmcore.c b/drivers/net/caif/caif_shmcore.c
deleted file mode 100644 (file)
index bce8bac..0000000
+++ /dev/null
@@ -1,747 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
- * Authors:  Amarnath Revanna / amarnath.bangalore.revanna@stericsson.com,
- *           Daniel Martensson / daniel.martensson@stericsson.com
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt
-
-#include <linux/spinlock.h>
-#include <linux/sched.h>
-#include <linux/list.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/io.h>
-
-#include <net/caif/caif_device.h>
-#include <net/caif/caif_shm.h>
-
-#define NR_TX_BUF              6
-#define NR_RX_BUF              6
-#define TX_BUF_SZ              0x2000
-#define RX_BUF_SZ              0x2000
-
-#define CAIF_NEEDED_HEADROOM   32
-
-#define CAIF_FLOW_ON           1
-#define CAIF_FLOW_OFF          0
-
-#define LOW_WATERMARK          3
-#define HIGH_WATERMARK         4
-
-/* Maximum number of CAIF buffers per shared memory buffer. */
-#define SHM_MAX_FRMS_PER_BUF   10
-
-/*
- * Size in bytes of the descriptor area
- * (With end of descriptor signalling)
- */
-#define SHM_CAIF_DESC_SIZE     ((SHM_MAX_FRMS_PER_BUF + 1) * \
-                                       sizeof(struct shm_pck_desc))
-
-/*
- * Offset to the first CAIF frame within a shared memory buffer.
- * Aligned on 32 bytes.
- */
-#define SHM_CAIF_FRM_OFS       (SHM_CAIF_DESC_SIZE + (SHM_CAIF_DESC_SIZE % 32))
-
-/* Number of bytes for CAIF shared memory header. */
-#define SHM_HDR_LEN            1
-
-/* Number of padding bytes for the complete CAIF frame. */
-#define SHM_FRM_PAD_LEN                4
-
-#define CAIF_MAX_MTU           4096
-
-#define SHM_SET_FULL(x)        (((x+1) & 0x0F) << 0)
-#define SHM_GET_FULL(x)        (((x >> 0) & 0x0F) - 1)
-
-#define SHM_SET_EMPTY(x)       (((x+1) & 0x0F) << 4)
-#define SHM_GET_EMPTY(x)       (((x >> 4) & 0x0F) - 1)
-
-#define SHM_FULL_MASK          (0x0F << 0)
-#define SHM_EMPTY_MASK         (0x0F << 4)
-
-struct shm_pck_desc {
-       /*
-        * Offset from start of shared memory area to start of
-        * shared memory CAIF frame.
-        */
-       u32 frm_ofs;
-       u32 frm_len;
-};
-
-struct buf_list {
-       unsigned char *desc_vptr;
-       u32 phy_addr;
-       u32 index;
-       u32 len;
-       u32 frames;
-       u32 frm_ofs;
-       struct list_head list;
-};
-
-struct shm_caif_frm {
-       /* Number of bytes of padding before the CAIF frame. */
-       u8 hdr_ofs;
-};
-
-struct shmdrv_layer {
-       /* caif_dev_common must always be first in the structure*/
-       struct caif_dev_common cfdev;
-
-       u32 shm_tx_addr;
-       u32 shm_rx_addr;
-       u32 shm_base_addr;
-       u32 tx_empty_available;
-       spinlock_t lock;
-
-       struct list_head tx_empty_list;
-       struct list_head tx_pend_list;
-       struct list_head tx_full_list;
-       struct list_head rx_empty_list;
-       struct list_head rx_pend_list;
-       struct list_head rx_full_list;
-
-       struct workqueue_struct *pshm_tx_workqueue;
-       struct workqueue_struct *pshm_rx_workqueue;
-
-       struct work_struct shm_tx_work;
-       struct work_struct shm_rx_work;
-
-       struct sk_buff_head sk_qhead;
-       struct shmdev_layer *pshm_dev;
-};
-
-static int shm_netdev_open(struct net_device *shm_netdev)
-{
-       netif_wake_queue(shm_netdev);
-       return 0;
-}
-
-static int shm_netdev_close(struct net_device *shm_netdev)
-{
-       netif_stop_queue(shm_netdev);
-       return 0;
-}
-
-int caif_shmdrv_rx_cb(u32 mbx_msg, void *priv)
-{
-       struct buf_list *pbuf;
-       struct shmdrv_layer *pshm_drv;
-       struct list_head *pos;
-       u32 avail_emptybuff = 0;
-       unsigned long flags = 0;
-
-       pshm_drv = priv;
-
-       /* Check for received buffers. */
-       if (mbx_msg & SHM_FULL_MASK) {
-               int idx;
-
-               spin_lock_irqsave(&pshm_drv->lock, flags);
-
-               /* Check whether we have any outstanding buffers. */
-               if (list_empty(&pshm_drv->rx_empty_list)) {
-
-                       /* Release spin lock. */
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
-                       /* We print even in IRQ context... */
-                       pr_warn("No empty Rx buffers to fill: "
-                                       "mbx_msg:%x\n", mbx_msg);
-
-                       /* Bail out. */
-                       goto err_sync;
-               }
-
-               pbuf =
-                       list_entry(pshm_drv->rx_empty_list.next,
-                                       struct buf_list, list);
-               idx = pbuf->index;
-
-               /* Check buffer synchronization. */
-               if (idx != SHM_GET_FULL(mbx_msg)) {
-
-                       /* We print even in IRQ context... */
-                       pr_warn(
-                       "phyif_shm_mbx_msg_cb: RX full out of sync:"
-                       " idx:%d, msg:%x SHM_GET_FULL(mbx_msg):%x\n",
-                               idx, mbx_msg, SHM_GET_FULL(mbx_msg));
-
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
-                       /* Bail out. */
-                       goto err_sync;
-               }
-
-               list_del_init(&pbuf->list);
-               list_add_tail(&pbuf->list, &pshm_drv->rx_full_list);
-
-               spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
-               /* Schedule RX work queue. */
-               if (!work_pending(&pshm_drv->shm_rx_work))
-                       queue_work(pshm_drv->pshm_rx_workqueue,
-                                               &pshm_drv->shm_rx_work);
-       }
-
-       /* Check for emptied buffers. */
-       if (mbx_msg & SHM_EMPTY_MASK) {
-               int idx;
-
-               spin_lock_irqsave(&pshm_drv->lock, flags);
-
-               /* Check whether we have any outstanding buffers. */
-               if (list_empty(&pshm_drv->tx_full_list)) {
-
-                       /* We print even in IRQ context... */
-                       pr_warn("No TX to empty: msg:%x\n", mbx_msg);
-
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
-                       /* Bail out. */
-                       goto err_sync;
-               }
-
-               pbuf =
-                       list_entry(pshm_drv->tx_full_list.next,
-                                       struct buf_list, list);
-               idx = pbuf->index;
-
-               /* Check buffer synchronization. */
-               if (idx != SHM_GET_EMPTY(mbx_msg)) {
-
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
-                       /* We print even in IRQ context... */
-                       pr_warn("TX empty "
-                               "out of sync:idx:%d, msg:%x\n", idx, mbx_msg);
-
-                       /* Bail out. */
-                       goto err_sync;
-               }
-               list_del_init(&pbuf->list);
-
-               /* Reset buffer parameters. */
-               pbuf->frames = 0;
-               pbuf->frm_ofs = SHM_CAIF_FRM_OFS;
-
-               list_add_tail(&pbuf->list, &pshm_drv->tx_empty_list);
-
-               /* Check the available no. of buffers in the empty list */
-               list_for_each(pos, &pshm_drv->tx_empty_list)
-                       avail_emptybuff++;
-
-               /* Check whether we have to wake up the transmitter. */
-               if ((avail_emptybuff > HIGH_WATERMARK) &&
-                                       (!pshm_drv->tx_empty_available)) {
-                       pshm_drv->tx_empty_available = 1;
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-                       pshm_drv->cfdev.flowctrl
-                                       (pshm_drv->pshm_dev->pshm_netdev,
-                                                               CAIF_FLOW_ON);
-
-
-                       /* Schedule the work queue. if required */
-                       if (!work_pending(&pshm_drv->shm_tx_work))
-                               queue_work(pshm_drv->pshm_tx_workqueue,
-                                                       &pshm_drv->shm_tx_work);
-               } else
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-       }
-
-       return 0;
-
-err_sync:
-       return -EIO;
-}
-
-static void shm_rx_work_func(struct work_struct *rx_work)
-{
-       struct shmdrv_layer *pshm_drv;
-       struct buf_list *pbuf;
-       unsigned long flags = 0;
-       struct sk_buff *skb;
-       char *p;
-       int ret;
-
-       pshm_drv = container_of(rx_work, struct shmdrv_layer, shm_rx_work);
-
-       while (1) {
-
-               struct shm_pck_desc *pck_desc;
-
-               spin_lock_irqsave(&pshm_drv->lock, flags);
-
-               /* Check for received buffers. */
-               if (list_empty(&pshm_drv->rx_full_list)) {
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-                       break;
-               }
-
-               pbuf =
-                       list_entry(pshm_drv->rx_full_list.next, struct buf_list,
-                                       list);
-               list_del_init(&pbuf->list);
-               spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
-               /* Retrieve pointer to start of the packet descriptor area. */
-               pck_desc = (struct shm_pck_desc *) pbuf->desc_vptr;
-
-               /*
-                * Check whether descriptor contains a CAIF shared memory
-                * frame.
-                */
-               while (pck_desc->frm_ofs) {
-                       unsigned int frm_buf_ofs;
-                       unsigned int frm_pck_ofs;
-                       unsigned int frm_pck_len;
-                       /*
-                        * Check whether offset is within buffer limits
-                        * (lower).
-                        */
-                       if (pck_desc->frm_ofs <
-                               (pbuf->phy_addr - pshm_drv->shm_base_addr))
-                               break;
-                       /*
-                        * Check whether offset is within buffer limits
-                        * (higher).
-                        */
-                       if (pck_desc->frm_ofs >
-                               ((pbuf->phy_addr - pshm_drv->shm_base_addr) +
-                                       pbuf->len))
-                               break;
-
-                       /* Calculate offset from start of buffer. */
-                       frm_buf_ofs =
-                               pck_desc->frm_ofs - (pbuf->phy_addr -
-                                               pshm_drv->shm_base_addr);
-
-                       /*
-                        * Calculate offset and length of CAIF packet while
-                        * taking care of the shared memory header.
-                        */
-                       frm_pck_ofs =
-                               frm_buf_ofs + SHM_HDR_LEN +
-                               (*(pbuf->desc_vptr + frm_buf_ofs));
-                       frm_pck_len =
-                               (pck_desc->frm_len - SHM_HDR_LEN -
-                               (*(pbuf->desc_vptr + frm_buf_ofs)));
-
-                       /* Check whether CAIF packet is within buffer limits */
-                       if ((frm_pck_ofs + pck_desc->frm_len) > pbuf->len)
-                               break;
-
-                       /* Get a suitable CAIF packet and copy in data. */
-                       skb = netdev_alloc_skb(pshm_drv->pshm_dev->pshm_netdev,
-                                                       frm_pck_len + 1);
-
-                       if (skb == NULL) {
-                               pr_info("OOM: Try next frame in descriptor\n");
-                               break;
-                       }
-
-                       p = skb_put(skb, frm_pck_len);
-                       memcpy(p, pbuf->desc_vptr + frm_pck_ofs, frm_pck_len);
-
-                       skb->protocol = htons(ETH_P_CAIF);
-                       skb_reset_mac_header(skb);
-                       skb->dev = pshm_drv->pshm_dev->pshm_netdev;
-
-                       /* Push received packet up the stack. */
-                       ret = netif_rx_ni(skb);
-
-                       if (!ret) {
-                               pshm_drv->pshm_dev->pshm_netdev->stats.
-                                                               rx_packets++;
-                               pshm_drv->pshm_dev->pshm_netdev->stats.
-                                               rx_bytes += pck_desc->frm_len;
-                       } else
-                               ++pshm_drv->pshm_dev->pshm_netdev->stats.
-                                                               rx_dropped;
-                       /* Move to next packet descriptor. */
-                       pck_desc++;
-               }
-
-               spin_lock_irqsave(&pshm_drv->lock, flags);
-               list_add_tail(&pbuf->list, &pshm_drv->rx_pend_list);
-
-               spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
-       }
-
-       /* Schedule the work queue. if required */
-       if (!work_pending(&pshm_drv->shm_tx_work))
-               queue_work(pshm_drv->pshm_tx_workqueue, &pshm_drv->shm_tx_work);
-
-}
-
-static void shm_tx_work_func(struct work_struct *tx_work)
-{
-       u32 mbox_msg;
-       unsigned int frmlen, avail_emptybuff, append = 0;
-       unsigned long flags = 0;
-       struct buf_list *pbuf = NULL;
-       struct shmdrv_layer *pshm_drv;
-       struct shm_caif_frm *frm;
-       struct sk_buff *skb;
-       struct shm_pck_desc *pck_desc;
-       struct list_head *pos;
-
-       pshm_drv = container_of(tx_work, struct shmdrv_layer, shm_tx_work);
-
-       do {
-               /* Initialize mailbox message. */
-               mbox_msg = 0x00;
-               avail_emptybuff = 0;
-
-               spin_lock_irqsave(&pshm_drv->lock, flags);
-
-               /* Check for pending receive buffers. */
-               if (!list_empty(&pshm_drv->rx_pend_list)) {
-
-                       pbuf = list_entry(pshm_drv->rx_pend_list.next,
-                                               struct buf_list, list);
-
-                       list_del_init(&pbuf->list);
-                       list_add_tail(&pbuf->list, &pshm_drv->rx_empty_list);
-                       /*
-                        * Value index is never changed,
-                        * so read access should be safe.
-                        */
-                       mbox_msg |= SHM_SET_EMPTY(pbuf->index);
-               }
-
-               skb = skb_peek(&pshm_drv->sk_qhead);
-
-               if (skb == NULL)
-                       goto send_msg;
-               /* Check the available no. of buffers in the empty list */
-               list_for_each(pos, &pshm_drv->tx_empty_list)
-                       avail_emptybuff++;
-
-               if ((avail_emptybuff < LOW_WATERMARK) &&
-                                       pshm_drv->tx_empty_available) {
-                       /* Update blocking condition. */
-                       pshm_drv->tx_empty_available = 0;
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-                       pshm_drv->cfdev.flowctrl
-                                       (pshm_drv->pshm_dev->pshm_netdev,
-                                       CAIF_FLOW_OFF);
-                       spin_lock_irqsave(&pshm_drv->lock, flags);
-               }
-               /*
-                * We simply return back to the caller if we do not have space
-                * either in Tx pending list or Tx empty list. In this case,
-                * we hold the received skb in the skb list, waiting to
-                * be transmitted once Tx buffers become available
-                */
-               if (list_empty(&pshm_drv->tx_empty_list))
-                       goto send_msg;
-
-               /* Get the first free Tx buffer. */
-               pbuf = list_entry(pshm_drv->tx_empty_list.next,
-                                               struct buf_list, list);
-               do {
-                       if (append) {
-                               skb = skb_peek(&pshm_drv->sk_qhead);
-                               if (skb == NULL)
-                                       break;
-                       }
-
-                       frm = (struct shm_caif_frm *)
-                                       (pbuf->desc_vptr + pbuf->frm_ofs);
-
-                       frm->hdr_ofs = 0;
-                       frmlen = 0;
-                       frmlen += SHM_HDR_LEN + frm->hdr_ofs + skb->len;
-
-                       /* Add tail padding if needed. */
-                       if (frmlen % SHM_FRM_PAD_LEN)
-                               frmlen += SHM_FRM_PAD_LEN -
-                                               (frmlen % SHM_FRM_PAD_LEN);
-
-                       /*
-                        * Verify that packet, header and additional padding
-                        * can fit within the buffer frame area.
-                        */
-                       if (frmlen >= (pbuf->len - pbuf->frm_ofs))
-                               break;
-
-                       if (!append) {
-                               list_del_init(&pbuf->list);
-                               append = 1;
-                       }
-
-                       skb = skb_dequeue(&pshm_drv->sk_qhead);
-                       if (skb == NULL)
-                               break;
-                       /* Copy in CAIF frame. */
-                       skb_copy_bits(skb, 0, pbuf->desc_vptr +
-                                       pbuf->frm_ofs + SHM_HDR_LEN +
-                                               frm->hdr_ofs, skb->len);
-
-                       pshm_drv->pshm_dev->pshm_netdev->stats.tx_packets++;
-                       pshm_drv->pshm_dev->pshm_netdev->stats.tx_bytes +=
-                                                                       frmlen;
-                       dev_kfree_skb_irq(skb);
-
-                       /* Fill in the shared memory packet descriptor area. */
-                       pck_desc = (struct shm_pck_desc *) (pbuf->desc_vptr);
-                       /* Forward to current frame. */
-                       pck_desc += pbuf->frames;
-                       pck_desc->frm_ofs = (pbuf->phy_addr -
-                                               pshm_drv->shm_base_addr) +
-                                                               pbuf->frm_ofs;
-                       pck_desc->frm_len = frmlen;
-                       /* Terminate packet descriptor area. */
-                       pck_desc++;
-                       pck_desc->frm_ofs = 0;
-                       /* Update buffer parameters. */
-                       pbuf->frames++;
-                       pbuf->frm_ofs += frmlen + (frmlen % 32);
-
-               } while (pbuf->frames < SHM_MAX_FRMS_PER_BUF);
-
-               /* Assign buffer as full. */
-               list_add_tail(&pbuf->list, &pshm_drv->tx_full_list);
-               append = 0;
-               mbox_msg |= SHM_SET_FULL(pbuf->index);
-send_msg:
-               spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
-               if (mbox_msg)
-                       pshm_drv->pshm_dev->pshmdev_mbxsend
-                                       (pshm_drv->pshm_dev->shm_id, mbox_msg);
-       } while (mbox_msg);
-}
-
-static int shm_netdev_tx(struct sk_buff *skb, struct net_device *shm_netdev)
-{
-       struct shmdrv_layer *pshm_drv;
-
-       pshm_drv = netdev_priv(shm_netdev);
-
-       skb_queue_tail(&pshm_drv->sk_qhead, skb);
-
-       /* Schedule Tx work queue. for deferred processing of skbs*/
-       if (!work_pending(&pshm_drv->shm_tx_work))
-               queue_work(pshm_drv->pshm_tx_workqueue, &pshm_drv->shm_tx_work);
-
-       return 0;
-}
-
-static const struct net_device_ops netdev_ops = {
-       .ndo_open = shm_netdev_open,
-       .ndo_stop = shm_netdev_close,
-       .ndo_start_xmit = shm_netdev_tx,
-};
-
-static void shm_netdev_setup(struct net_device *pshm_netdev)
-{
-       struct shmdrv_layer *pshm_drv;
-       pshm_netdev->netdev_ops = &netdev_ops;
-
-       pshm_netdev->mtu = CAIF_MAX_MTU;
-       pshm_netdev->type = ARPHRD_CAIF;
-       pshm_netdev->hard_header_len = CAIF_NEEDED_HEADROOM;
-       pshm_netdev->tx_queue_len = 0;
-       pshm_netdev->destructor = free_netdev;
-
-       pshm_drv = netdev_priv(pshm_netdev);
-
-       /* Initialize structures in a clean state. */
-       memset(pshm_drv, 0, sizeof(struct shmdrv_layer));
-
-       pshm_drv->cfdev.link_select = CAIF_LINK_LOW_LATENCY;
-}
-
-int caif_shmcore_probe(struct shmdev_layer *pshm_dev)
-{
-       int result, j;
-       struct shmdrv_layer *pshm_drv = NULL;
-
-       pshm_dev->pshm_netdev = alloc_netdev(sizeof(struct shmdrv_layer),
-                                               "cfshm%d", shm_netdev_setup);
-       if (!pshm_dev->pshm_netdev)
-               return -ENOMEM;
-
-       pshm_drv = netdev_priv(pshm_dev->pshm_netdev);
-       pshm_drv->pshm_dev = pshm_dev;
-
-       /*
-        * Initialization starts with the verification of the
-        * availability of MBX driver by calling its setup function.
-        * MBX driver must be available by this time for proper
-        * functioning of SHM driver.
-        */
-       if ((pshm_dev->pshmdev_mbxsetup
-                               (caif_shmdrv_rx_cb, pshm_dev, pshm_drv)) != 0) {
-               pr_warn("Could not config. SHM Mailbox,"
-                               " Bailing out.....\n");
-               free_netdev(pshm_dev->pshm_netdev);
-               return -ENODEV;
-       }
-
-       skb_queue_head_init(&pshm_drv->sk_qhead);
-
-       pr_info("SHM DEVICE[%d] PROBED BY DRIVER, NEW SHM DRIVER"
-                       " INSTANCE AT pshm_drv =0x%p\n",
-                       pshm_drv->pshm_dev->shm_id, pshm_drv);
-
-       if (pshm_dev->shm_total_sz <
-                       (NR_TX_BUF * TX_BUF_SZ + NR_RX_BUF * RX_BUF_SZ)) {
-
-               pr_warn("ERROR, Amount of available"
-                               " Phys. SHM cannot accommodate current SHM "
-                               "driver configuration, Bailing out ...\n");
-               free_netdev(pshm_dev->pshm_netdev);
-               return -ENOMEM;
-       }
-
-       pshm_drv->shm_base_addr = pshm_dev->shm_base_addr;
-       pshm_drv->shm_tx_addr = pshm_drv->shm_base_addr;
-
-       if (pshm_dev->shm_loopback)
-               pshm_drv->shm_rx_addr = pshm_drv->shm_tx_addr;
-       else
-               pshm_drv->shm_rx_addr = pshm_dev->shm_base_addr +
-                                               (NR_TX_BUF * TX_BUF_SZ);
-
-       spin_lock_init(&pshm_drv->lock);
-       INIT_LIST_HEAD(&pshm_drv->tx_empty_list);
-       INIT_LIST_HEAD(&pshm_drv->tx_pend_list);
-       INIT_LIST_HEAD(&pshm_drv->tx_full_list);
-
-       INIT_LIST_HEAD(&pshm_drv->rx_empty_list);
-       INIT_LIST_HEAD(&pshm_drv->rx_pend_list);
-       INIT_LIST_HEAD(&pshm_drv->rx_full_list);
-
-       INIT_WORK(&pshm_drv->shm_tx_work, shm_tx_work_func);
-       INIT_WORK(&pshm_drv->shm_rx_work, shm_rx_work_func);
-
-       pshm_drv->pshm_tx_workqueue =
-                               create_singlethread_workqueue("shm_tx_work");
-       pshm_drv->pshm_rx_workqueue =
-                               create_singlethread_workqueue("shm_rx_work");
-
-       for (j = 0; j < NR_TX_BUF; j++) {
-               struct buf_list *tx_buf =
-                               kmalloc(sizeof(struct buf_list), GFP_KERNEL);
-
-               if (tx_buf == NULL) {
-                       free_netdev(pshm_dev->pshm_netdev);
-                       return -ENOMEM;
-               }
-               tx_buf->index = j;
-               tx_buf->phy_addr = pshm_drv->shm_tx_addr + (TX_BUF_SZ * j);
-               tx_buf->len = TX_BUF_SZ;
-               tx_buf->frames = 0;
-               tx_buf->frm_ofs = SHM_CAIF_FRM_OFS;
-
-               if (pshm_dev->shm_loopback)
-                       tx_buf->desc_vptr = (unsigned char *)tx_buf->phy_addr;
-               else
-                       /*
-                        * FIXME: the result of ioremap is not a pointer - arnd
-                        */
-                       tx_buf->desc_vptr =
-                                       ioremap(tx_buf->phy_addr, TX_BUF_SZ);
-
-               list_add_tail(&tx_buf->list, &pshm_drv->tx_empty_list);
-       }
-
-       for (j = 0; j < NR_RX_BUF; j++) {
-               struct buf_list *rx_buf =
-                               kmalloc(sizeof(struct buf_list), GFP_KERNEL);
-
-               if (rx_buf == NULL) {
-                       free_netdev(pshm_dev->pshm_netdev);
-                       return -ENOMEM;
-               }
-               rx_buf->index = j;
-               rx_buf->phy_addr = pshm_drv->shm_rx_addr + (RX_BUF_SZ * j);
-               rx_buf->len = RX_BUF_SZ;
-
-               if (pshm_dev->shm_loopback)
-                       rx_buf->desc_vptr = (unsigned char *)rx_buf->phy_addr;
-               else
-                       rx_buf->desc_vptr =
-                                       ioremap(rx_buf->phy_addr, RX_BUF_SZ);
-               list_add_tail(&rx_buf->list, &pshm_drv->rx_empty_list);
-       }
-
-       pshm_drv->tx_empty_available = 1;
-       result = register_netdev(pshm_dev->pshm_netdev);
-       if (result)
-               pr_warn("ERROR[%d], SHM could not, "
-                       "register with NW FRMWK Bailing out ...\n", result);
-
-       return result;
-}
-
-void caif_shmcore_remove(struct net_device *pshm_netdev)
-{
-       struct buf_list *pbuf;
-       struct shmdrv_layer *pshm_drv = NULL;
-
-       pshm_drv = netdev_priv(pshm_netdev);
-
-       while (!(list_empty(&pshm_drv->tx_pend_list))) {
-               pbuf =
-                       list_entry(pshm_drv->tx_pend_list.next,
-                                       struct buf_list, list);
-
-               list_del(&pbuf->list);
-               kfree(pbuf);
-       }
-
-       while (!(list_empty(&pshm_drv->tx_full_list))) {
-               pbuf =
-                       list_entry(pshm_drv->tx_full_list.next,
-                                       struct buf_list, list);
-               list_del(&pbuf->list);
-               kfree(pbuf);
-       }
-
-       while (!(list_empty(&pshm_drv->tx_empty_list))) {
-               pbuf =
-                       list_entry(pshm_drv->tx_empty_list.next,
-                                       struct buf_list, list);
-               list_del(&pbuf->list);
-               kfree(pbuf);
-       }
-
-       while (!(list_empty(&pshm_drv->rx_full_list))) {
-               pbuf =
-                       list_entry(pshm_drv->tx_full_list.next,
-                               struct buf_list, list);
-               list_del(&pbuf->list);
-               kfree(pbuf);
-       }
-
-       while (!(list_empty(&pshm_drv->rx_pend_list))) {
-               pbuf =
-                       list_entry(pshm_drv->tx_pend_list.next,
-                               struct buf_list, list);
-               list_del(&pbuf->list);
-               kfree(pbuf);
-       }
-
-       while (!(list_empty(&pshm_drv->rx_empty_list))) {
-               pbuf =
-                       list_entry(pshm_drv->rx_empty_list.next,
-                               struct buf_list, list);
-               list_del(&pbuf->list);
-               kfree(pbuf);
-       }
-
-       /* Destroy work queues. */
-       destroy_workqueue(pshm_drv->pshm_tx_workqueue);
-       destroy_workqueue(pshm_drv->pshm_rx_workqueue);
-
-       unregister_netdev(pshm_netdev);
-}
index 9862b2e07644462b59fe81c137c7ad6d835bd2d8..e456b70933c230abd7703ca3f6abed853679e5ef 100644 (file)
@@ -65,7 +65,7 @@ config CAN_LEDS
 
 config CAN_AT91
        tristate "Atmel AT91 onchip CAN controller"
-       depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9X5
+       depends on ARM
        ---help---
          This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
          and AT91SAM9X5 processors.
index 44f363792b59d5b04b7f47585ca8db0bbb4fc1bb..db52f4414def171a4b76fffad88231589ddcc5c5 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/rtnetlink.h>
 #include <linux/skbuff.h>
@@ -155,19 +156,20 @@ struct at91_priv {
        canid_t mb0_id;
 };
 
-static const struct at91_devtype_data at91_devtype_data[] = {
-       [AT91_DEVTYPE_SAM9263] = {
-               .rx_first = 1,
-               .rx_split = 8,
-               .rx_last = 11,
-               .tx_shift = 2,
-       },
-       [AT91_DEVTYPE_SAM9X5] = {
-               .rx_first = 0,
-               .rx_split = 4,
-               .rx_last = 5,
-               .tx_shift = 1,
-       },
+static const struct at91_devtype_data at91_at91sam9263_data = {
+       .rx_first = 1,
+       .rx_split = 8,
+       .rx_last = 11,
+       .tx_shift = 2,
+       .type = AT91_DEVTYPE_SAM9263,
+};
+
+static const struct at91_devtype_data at91_at91sam9x5_data = {
+       .rx_first = 0,
+       .rx_split = 4,
+       .rx_last = 5,
+       .tx_shift = 1,
+       .type = AT91_DEVTYPE_SAM9X5,
 };
 
 static const struct can_bittiming_const at91_bittiming_const = {
@@ -1249,10 +1251,42 @@ static struct attribute_group at91_sysfs_attr_group = {
        .attrs = at91_sysfs_attrs,
 };
 
+#if defined(CONFIG_OF)
+static const struct of_device_id at91_can_dt_ids[] = {
+       {
+               .compatible = "atmel,at91sam9x5-can",
+               .data = &at91_at91sam9x5_data,
+       }, {
+               .compatible = "atmel,at91sam9263-can",
+               .data = &at91_at91sam9263_data,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, at91_can_dt_ids);
+#else
+#define at91_can_dt_ids NULL
+#endif
+
+static const struct at91_devtype_data *at91_can_get_driver_data(struct platform_device *pdev)
+{
+       if (pdev->dev.of_node) {
+               const struct of_device_id *match;
+
+               match = of_match_node(at91_can_dt_ids, pdev->dev.of_node);
+               if (!match) {
+                       dev_err(&pdev->dev, "no matching node found in dtb\n");
+                       return NULL;
+               }
+               return (const struct at91_devtype_data *)match->data;
+       }
+       return (const struct at91_devtype_data *)
+               platform_get_device_id(pdev)->driver_data;
+}
+
 static int at91_can_probe(struct platform_device *pdev)
 {
        const struct at91_devtype_data *devtype_data;
-       enum at91_devtype devtype;
        struct net_device *dev;
        struct at91_priv *priv;
        struct resource *res;
@@ -1260,8 +1294,12 @@ static int at91_can_probe(struct platform_device *pdev)
        void __iomem *addr;
        int err, irq;
 
-       devtype = pdev->id_entry->driver_data;
-       devtype_data = &at91_devtype_data[devtype];
+       devtype_data = at91_can_get_driver_data(pdev);
+       if (!devtype_data) {
+               dev_err(&pdev->dev, "no driver data\n");
+               err = -ENODEV;
+               goto exit;
+       }
 
        clk = clk_get(&pdev->dev, "can_clk");
        if (IS_ERR(clk)) {
@@ -1310,7 +1348,6 @@ static int at91_can_probe(struct platform_device *pdev)
        priv->dev = dev;
        priv->reg_base = addr;
        priv->devtype_data = *devtype_data;
-       priv->devtype_data.type = devtype;
        priv->clk = clk;
        priv->pdata = pdev->dev.platform_data;
        priv->mb0_id = 0x7ff;
@@ -1373,10 +1410,10 @@ static int at91_can_remove(struct platform_device *pdev)
 static const struct platform_device_id at91_can_id_table[] = {
        {
                .name = "at91_can",
-               .driver_data = AT91_DEVTYPE_SAM9263,
+               .driver_data = (kernel_ulong_t)&at91_at91sam9x5_data,
        }, {
                .name = "at91sam9x5_can",
-               .driver_data = AT91_DEVTYPE_SAM9X5,
+               .driver_data = (kernel_ulong_t)&at91_at91sam9263_data,
        }, {
                /* sentinel */
        }
@@ -1389,6 +1426,7 @@ static struct platform_driver at91_can_driver = {
        .driver = {
                .name = KBUILD_MODNAME,
                .owner = THIS_MODULE,
+               .of_match_table = at91_can_dt_ids,
        },
        .id_table = at91_can_id_table,
 };
index 6a0532176b69a1c47c5992c1c4e335b991892c2d..d4a15e82bfc0954860999e5831a043ff53b07d9a 100644 (file)
@@ -412,7 +412,7 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status)
        return 0;
 }
 
-irqreturn_t bfin_can_interrupt(int irq, void *dev_id)
+static irqreturn_t bfin_can_interrupt(int irq, void *dev_id)
 {
        struct net_device *dev = dev_id;
        struct bfin_can_priv *priv = netdev_priv(dev);
@@ -504,7 +504,7 @@ static int bfin_can_close(struct net_device *dev)
        return 0;
 }
 
-struct net_device *alloc_bfin_candev(void)
+static struct net_device *alloc_bfin_candev(void)
 {
        struct net_device *dev;
        struct bfin_can_priv *priv;
index 9aa0c64c33c81c9f296ed5f08b0d3666ee989816..8cda23bf0614a1ebe383660300e3ec0cd34d20e2 100644 (file)
@@ -269,7 +269,7 @@ struct mcp251x_priv {
 #define MCP251X_IS(_model) \
 static inline int mcp251x_is_##_model(struct spi_device *spi) \
 { \
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); \
+       struct mcp251x_priv *priv = spi_get_drvdata(spi); \
        return priv->model == CAN_MCP251X_MCP##_model; \
 }
 
@@ -305,7 +305,7 @@ static void mcp251x_clean(struct net_device *net)
  */
 static int mcp251x_spi_trans(struct spi_device *spi, int len)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
        struct spi_transfer t = {
                .tx_buf = priv->spi_tx_buf,
                .rx_buf = priv->spi_rx_buf,
@@ -333,7 +333,7 @@ static int mcp251x_spi_trans(struct spi_device *spi, int len)
 
 static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
        u8 val = 0;
 
        priv->spi_tx_buf[0] = INSTRUCTION_READ;
@@ -348,7 +348,7 @@ static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
 static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg,
                uint8_t *v1, uint8_t *v2)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
 
        priv->spi_tx_buf[0] = INSTRUCTION_READ;
        priv->spi_tx_buf[1] = reg;
@@ -361,7 +361,7 @@ static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg,
 
 static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
 
        priv->spi_tx_buf[0] = INSTRUCTION_WRITE;
        priv->spi_tx_buf[1] = reg;
@@ -373,7 +373,7 @@ static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
 static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
                               u8 mask, uint8_t val)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
 
        priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;
        priv->spi_tx_buf[1] = reg;
@@ -386,7 +386,7 @@ static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
 static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
                                int len, int tx_buf_idx)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
 
        if (mcp251x_is_2510(spi)) {
                int i;
@@ -403,7 +403,7 @@ static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
 static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
                          int tx_buf_idx)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
        u32 sid, eid, exide, rtr;
        u8 buf[SPI_TRANSFER_BUF_LEN];
 
@@ -434,7 +434,7 @@ static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
 static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,
                                int buf_idx)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
 
        if (mcp251x_is_2510(spi)) {
                int i, len;
@@ -454,7 +454,7 @@ static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,
 
 static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
        struct sk_buff *skb;
        struct can_frame *frame;
        u8 buf[SPI_TRANSFER_BUF_LEN];
@@ -550,7 +550,7 @@ static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode)
 
 static int mcp251x_set_normal_mode(struct spi_device *spi)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
        unsigned long timeout;
 
        /* Enable interrupts */
@@ -620,7 +620,7 @@ static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
 
 static int mcp251x_hw_reset(struct spi_device *spi)
 {
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
        int ret;
        unsigned long timeout;
 
@@ -1026,7 +1026,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
                CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY;
        priv->model = spi_get_device_id(spi)->driver_data;
        priv->net = net;
-       dev_set_drvdata(&spi->dev, priv);
+       spi_set_drvdata(spi, priv);
 
        priv->spi = spi;
        mutex_init(&priv->mcp_lock);
@@ -1124,7 +1124,7 @@ error_out:
 static int mcp251x_can_remove(struct spi_device *spi)
 {
        struct mcp251x_platform_data *pdata = spi->dev.platform_data;
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
        struct net_device *net = priv->net;
 
        unregister_candev(net);
@@ -1144,11 +1144,13 @@ static int mcp251x_can_remove(struct spi_device *spi)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+
+static int mcp251x_can_suspend(struct device *dev)
 {
+       struct spi_device *spi = to_spi_device(dev);
        struct mcp251x_platform_data *pdata = spi->dev.platform_data;
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
        struct net_device *net = priv->net;
 
        priv->force_quit = 1;
@@ -1176,10 +1178,11 @@ static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
        return 0;
 }
 
-static int mcp251x_can_resume(struct spi_device *spi)
+static int mcp251x_can_resume(struct device *dev)
 {
+       struct spi_device *spi = to_spi_device(dev);
        struct mcp251x_platform_data *pdata = spi->dev.platform_data;
-       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+       struct mcp251x_priv *priv = spi_get_drvdata(spi);
 
        if (priv->after_suspend & AFTER_SUSPEND_POWER) {
                pdata->power_enable(1);
@@ -1197,11 +1200,11 @@ static int mcp251x_can_resume(struct spi_device *spi)
        enable_irq(spi->irq);
        return 0;
 }
-#else
-#define mcp251x_can_suspend NULL
-#define mcp251x_can_resume NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(mcp251x_can_pm_ops, mcp251x_can_suspend,
+       mcp251x_can_resume);
+
 static const struct spi_device_id mcp251x_id_table[] = {
        { "mcp2510",    CAN_MCP251X_MCP2510 },
        { "mcp2515",    CAN_MCP251X_MCP2515 },
@@ -1213,29 +1216,15 @@ MODULE_DEVICE_TABLE(spi, mcp251x_id_table);
 static struct spi_driver mcp251x_can_driver = {
        .driver = {
                .name = DEVICE_NAME,
-               .bus = &spi_bus_type,
                .owner = THIS_MODULE,
+               .pm = &mcp251x_can_pm_ops,
        },
 
        .id_table = mcp251x_id_table,
        .probe = mcp251x_can_probe,
        .remove = mcp251x_can_remove,
-       .suspend = mcp251x_can_suspend,
-       .resume = mcp251x_can_resume,
 };
-
-static int __init mcp251x_can_init(void)
-{
-       return spi_register_driver(&mcp251x_can_driver);
-}
-
-static void __exit mcp251x_can_exit(void)
-{
-       spi_unregister_driver(&mcp251x_can_driver);
-}
-
-module_init(mcp251x_can_init);
-module_exit(mcp251x_can_exit);
+module_spi_driver(mcp251x_can_driver);
 
 MODULE_AUTHOR("Chris Elston <celston@katalix.com>, "
              "Christian Pellegrin <chripell@evolware.org>");
index 36d298da2af6afb4c4ed5b7780c8f9dc11df0311..3752342a678ac5320e68bf69f747b4f69034758d 100644 (file)
@@ -168,12 +168,12 @@ static inline int ems_pci_check_chan(const struct sja1000_priv *priv)
        unsigned char res;
 
        /* Make sure SJA1000 is in reset mode */
-       priv->write_reg(priv, REG_MOD, 1);
+       priv->write_reg(priv, SJA1000_MOD, 1);
 
-       priv->write_reg(priv, REG_CDR, CDR_PELICAN);
+       priv->write_reg(priv, SJA1000_CDR, CDR_PELICAN);
 
        /* read reset-values */
-       res = priv->read_reg(priv, REG_CDR);
+       res = priv->read_reg(priv, SJA1000_CDR);
 
        if (res == CDR_PELICAN)
                return 1;
index 5c2f3fbbf5ae8fc4028ecf0fdc50ac03e92fd21b..a3aa6817b51580d820940fdca83c9c33fc7046cb 100644 (file)
@@ -126,11 +126,11 @@ static irqreturn_t ems_pcmcia_interrupt(int irq, void *dev_id)
 static inline int ems_pcmcia_check_chan(struct sja1000_priv *priv)
 {
        /* Make sure SJA1000 is in reset mode */
-       ems_pcmcia_write_reg(priv, REG_MOD, 1);
-       ems_pcmcia_write_reg(priv, REG_CDR, CDR_PELICAN);
+       ems_pcmcia_write_reg(priv, SJA1000_MOD, 1);
+       ems_pcmcia_write_reg(priv, SJA1000_CDR, CDR_PELICAN);
 
        /* read reset-values */
-       if (ems_pcmcia_read_reg(priv, REG_CDR) == CDR_PELICAN)
+       if (ems_pcmcia_read_reg(priv, SJA1000_CDR) == CDR_PELICAN)
                return 1;
 
        return 0;
index 37b0381f532e6458e13b7a424330047aec368e0c..217585b97cd3e0851439f8b388c040058b668100 100644 (file)
@@ -159,9 +159,9 @@ static int number_of_sja1000_chip(void __iomem *base_addr)
        for (i = 0; i < MAX_NO_OF_CHANNELS; i++) {
                /* reset chip */
                iowrite8(MOD_RM, base_addr +
-                        (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+                        (i * KVASER_PCI_PORT_BYTES) + SJA1000_MOD);
                status = ioread8(base_addr +
-                                (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+                                (i * KVASER_PCI_PORT_BYTES) + SJA1000_MOD);
                /* check reset bit */
                if (!(status & MOD_RM))
                        break;
index d1e7f1006ddd60b1531703d2602f9ca405e9b786..6b6f0ad75090c4ea463ae49b8e2041dbdaca55df 100644 (file)
@@ -402,7 +402,7 @@ static void peak_pciec_write_reg(const struct sja1000_priv *priv,
        int c = (priv->reg_base - card->reg_base) / PEAK_PCI_CHAN_SIZE;
 
        /* sja1000 register changes control the leds state */
-       if (port == REG_MOD)
+       if (port == SJA1000_MOD)
                switch (val) {
                case MOD_RM:
                        /* Reset Mode: set led on */
index 1a7020ba37f5416435f6e8e1475772ec33b5d0db..977901a0214abc2cb9029407dc693ec92fc1d783 100644 (file)
@@ -196,7 +196,7 @@ static void pcan_write_canreg(const struct sja1000_priv *priv, int port, u8 v)
        int c = (priv->reg_base - card->ioport_addr) / PCC_CHAN_SIZE;
 
        /* sja1000 register changes control the leds state */
-       if (port == REG_MOD)
+       if (port == SJA1000_MOD)
                switch (v) {
                case MOD_RM:
                        /* Reset Mode: set led on */
@@ -509,11 +509,11 @@ static void pcan_free_channels(struct pcan_pccard *card)
 static inline int pcan_channel_present(struct sja1000_priv *priv)
 {
        /* make sure SJA1000 is in reset mode */
-       pcan_write_canreg(priv, REG_MOD, 1);
-       pcan_write_canreg(priv, REG_CDR, CDR_PELICAN);
+       pcan_write_canreg(priv, SJA1000_MOD, 1);
+       pcan_write_canreg(priv, SJA1000_CDR, CDR_PELICAN);
 
        /* read reset-values */
-       if (pcan_read_canreg(priv, REG_CDR) == CDR_PELICAN)
+       if (pcan_read_canreg(priv, SJA1000_CDR) == CDR_PELICAN)
                return 1;
 
        return 0;
index 3c18d7d000edaabe3b8ec93f1d2841d3300df087..c52c1e96bf90741455eea01add4129fcfc590bad 100644 (file)
@@ -348,20 +348,20 @@ static inline int plx_pci_check_sja1000(const struct sja1000_priv *priv)
         */
        if ((priv->read_reg(priv, REG_CR) & REG_CR_BASICCAN_INITIAL_MASK) ==
            REG_CR_BASICCAN_INITIAL &&
-           (priv->read_reg(priv, SJA1000_REG_SR) == REG_SR_BASICCAN_INITIAL) &&
-           (priv->read_reg(priv, REG_IR) == REG_IR_BASICCAN_INITIAL))
+           (priv->read_reg(priv, SJA1000_SR) == REG_SR_BASICCAN_INITIAL) &&
+           (priv->read_reg(priv, SJA1000_IR) == REG_IR_BASICCAN_INITIAL))
                flag = 1;
 
        /* Bring the SJA1000 into the PeliCAN mode*/
-       priv->write_reg(priv, REG_CDR, CDR_PELICAN);
+       priv->write_reg(priv, SJA1000_CDR, CDR_PELICAN);
 
        /*
         * Check registers after reset in the PeliCAN mode.
         * See states on p. 23 of the Datasheet.
         */
-       if (priv->read_reg(priv, REG_MOD) == REG_MOD_PELICAN_INITIAL &&
-           priv->read_reg(priv, SJA1000_REG_SR) == REG_SR_PELICAN_INITIAL &&
-           priv->read_reg(priv, REG_IR) == REG_IR_PELICAN_INITIAL)
+       if (priv->read_reg(priv, SJA1000_MOD) == REG_MOD_PELICAN_INITIAL &&
+           priv->read_reg(priv, SJA1000_SR) == REG_SR_PELICAN_INITIAL &&
+           priv->read_reg(priv, SJA1000_IR) == REG_IR_PELICAN_INITIAL)
                return flag;
 
        return 0;
index e4df307eaa9081b9e39a81731714e0bd3969ea3b..7164a999f50ff37e176f9995f21cd04451f17043 100644 (file)
@@ -91,14 +91,14 @@ static void sja1000_write_cmdreg(struct sja1000_priv *priv, u8 val)
         * the write_reg() operation - especially on SMP systems.
         */
        spin_lock_irqsave(&priv->cmdreg_lock, flags);
-       priv->write_reg(priv, REG_CMR, val);
-       priv->read_reg(priv, SJA1000_REG_SR);
+       priv->write_reg(priv, SJA1000_CMR, val);
+       priv->read_reg(priv, SJA1000_SR);
        spin_unlock_irqrestore(&priv->cmdreg_lock, flags);
 }
 
 static int sja1000_is_absent(struct sja1000_priv *priv)
 {
-       return (priv->read_reg(priv, REG_MOD) == 0xFF);
+       return (priv->read_reg(priv, SJA1000_MOD) == 0xFF);
 }
 
 static int sja1000_probe_chip(struct net_device *dev)
@@ -116,11 +116,11 @@ static int sja1000_probe_chip(struct net_device *dev)
 static void set_reset_mode(struct net_device *dev)
 {
        struct sja1000_priv *priv = netdev_priv(dev);
-       unsigned char status = priv->read_reg(priv, REG_MOD);
+       unsigned char status = priv->read_reg(priv, SJA1000_MOD);
        int i;
 
        /* disable interrupts */
-       priv->write_reg(priv, REG_IER, IRQ_OFF);
+       priv->write_reg(priv, SJA1000_IER, IRQ_OFF);
 
        for (i = 0; i < 100; i++) {
                /* check reset bit */
@@ -129,9 +129,10 @@ static void set_reset_mode(struct net_device *dev)
                        return;
                }
 
-               priv->write_reg(priv, REG_MOD, MOD_RM); /* reset chip */
+               /* reset chip */
+               priv->write_reg(priv, SJA1000_MOD, MOD_RM);
                udelay(10);
-               status = priv->read_reg(priv, REG_MOD);
+               status = priv->read_reg(priv, SJA1000_MOD);
        }
 
        netdev_err(dev, "setting SJA1000 into reset mode failed!\n");
@@ -140,7 +141,7 @@ static void set_reset_mode(struct net_device *dev)
 static void set_normal_mode(struct net_device *dev)
 {
        struct sja1000_priv *priv = netdev_priv(dev);
-       unsigned char status = priv->read_reg(priv, REG_MOD);
+       unsigned char status = priv->read_reg(priv, SJA1000_MOD);
        int i;
 
        for (i = 0; i < 100; i++) {
@@ -149,22 +150,22 @@ static void set_normal_mode(struct net_device *dev)
                        priv->can.state = CAN_STATE_ERROR_ACTIVE;
                        /* enable interrupts */
                        if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
-                               priv->write_reg(priv, REG_IER, IRQ_ALL);
+                               priv->write_reg(priv, SJA1000_IER, IRQ_ALL);
                        else
-                               priv->write_reg(priv, REG_IER,
+                               priv->write_reg(priv, SJA1000_IER,
                                                IRQ_ALL & ~IRQ_BEI);
                        return;
                }
 
                /* set chip to normal mode */
                if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
-                       priv->write_reg(priv, REG_MOD, MOD_LOM);
+                       priv->write_reg(priv, SJA1000_MOD, MOD_LOM);
                else
-                       priv->write_reg(priv, REG_MOD, 0x00);
+                       priv->write_reg(priv, SJA1000_MOD, 0x00);
 
                udelay(10);
 
-               status = priv->read_reg(priv, REG_MOD);
+               status = priv->read_reg(priv, SJA1000_MOD);
        }
 
        netdev_err(dev, "setting SJA1000 into normal mode failed!\n");
@@ -179,9 +180,9 @@ static void sja1000_start(struct net_device *dev)
                set_reset_mode(dev);
 
        /* Clear error counters and error code capture */
-       priv->write_reg(priv, REG_TXERR, 0x0);
-       priv->write_reg(priv, REG_RXERR, 0x0);
-       priv->read_reg(priv, REG_ECC);
+       priv->write_reg(priv, SJA1000_TXERR, 0x0);
+       priv->write_reg(priv, SJA1000_RXERR, 0x0);
+       priv->read_reg(priv, SJA1000_ECC);
 
        /* leave reset mode */
        set_normal_mode(dev);
@@ -217,8 +218,8 @@ static int sja1000_set_bittiming(struct net_device *dev)
 
        netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
 
-       priv->write_reg(priv, REG_BTR0, btr0);
-       priv->write_reg(priv, REG_BTR1, btr1);
+       priv->write_reg(priv, SJA1000_BTR0, btr0);
+       priv->write_reg(priv, SJA1000_BTR1, btr1);
 
        return 0;
 }
@@ -228,8 +229,8 @@ static int sja1000_get_berr_counter(const struct net_device *dev,
 {
        struct sja1000_priv *priv = netdev_priv(dev);
 
-       bec->txerr = priv->read_reg(priv, REG_TXERR);
-       bec->rxerr = priv->read_reg(priv, REG_RXERR);
+       bec->txerr = priv->read_reg(priv, SJA1000_TXERR);
+       bec->rxerr = priv->read_reg(priv, SJA1000_RXERR);
 
        return 0;
 }
@@ -247,20 +248,20 @@ static void chipset_init(struct net_device *dev)
        struct sja1000_priv *priv = netdev_priv(dev);
 
        /* set clock divider and output control register */
-       priv->write_reg(priv, REG_CDR, priv->cdr | CDR_PELICAN);
+       priv->write_reg(priv, SJA1000_CDR, priv->cdr | CDR_PELICAN);
 
        /* set acceptance filter (accept all) */
-       priv->write_reg(priv, REG_ACCC0, 0x00);
-       priv->write_reg(priv, REG_ACCC1, 0x00);
-       priv->write_reg(priv, REG_ACCC2, 0x00);
-       priv->write_reg(priv, REG_ACCC3, 0x00);
+       priv->write_reg(priv, SJA1000_ACCC0, 0x00);
+       priv->write_reg(priv, SJA1000_ACCC1, 0x00);
+       priv->write_reg(priv, SJA1000_ACCC2, 0x00);
+       priv->write_reg(priv, SJA1000_ACCC3, 0x00);
 
-       priv->write_reg(priv, REG_ACCM0, 0xFF);
-       priv->write_reg(priv, REG_ACCM1, 0xFF);
-       priv->write_reg(priv, REG_ACCM2, 0xFF);
-       priv->write_reg(priv, REG_ACCM3, 0xFF);
+       priv->write_reg(priv, SJA1000_ACCM0, 0xFF);
+       priv->write_reg(priv, SJA1000_ACCM1, 0xFF);
+       priv->write_reg(priv, SJA1000_ACCM2, 0xFF);
+       priv->write_reg(priv, SJA1000_ACCM3, 0xFF);
 
-       priv->write_reg(priv, REG_OCR, priv->ocr | OCR_MODE_NORMAL);
+       priv->write_reg(priv, SJA1000_OCR, priv->ocr | OCR_MODE_NORMAL);
 }
 
 /*
@@ -289,21 +290,21 @@ static netdev_tx_t sja1000_start_xmit(struct sk_buff *skb,
        id = cf->can_id;
 
        if (id & CAN_RTR_FLAG)
-               fi |= FI_RTR;
+               fi |= SJA1000_FI_RTR;
 
        if (id & CAN_EFF_FLAG) {
-               fi |= FI_FF;
-               dreg = EFF_BUF;
-               priv->write_reg(priv, REG_FI, fi);
-               priv->write_reg(priv, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
-               priv->write_reg(priv, REG_ID2, (id & 0x001fe000) >> (5 + 8));
-               priv->write_reg(priv, REG_ID3, (id & 0x00001fe0) >> 5);
-               priv->write_reg(priv, REG_ID4, (id & 0x0000001f) << 3);
+               fi |= SJA1000_FI_FF;
+               dreg = SJA1000_EFF_BUF;
+               priv->write_reg(priv, SJA1000_FI, fi);
+               priv->write_reg(priv, SJA1000_ID1, (id & 0x1fe00000) >> 21);
+               priv->write_reg(priv, SJA1000_ID2, (id & 0x001fe000) >> 13);
+               priv->write_reg(priv, SJA1000_ID3, (id & 0x00001fe0) >> 5);
+               priv->write_reg(priv, SJA1000_ID4, (id & 0x0000001f) << 3);
        } else {
-               dreg = SFF_BUF;
-               priv->write_reg(priv, REG_FI, fi);
-               priv->write_reg(priv, REG_ID1, (id & 0x000007f8) >> 3);
-               priv->write_reg(priv, REG_ID2, (id & 0x00000007) << 5);
+               dreg = SJA1000_SFF_BUF;
+               priv->write_reg(priv, SJA1000_FI, fi);
+               priv->write_reg(priv, SJA1000_ID1, (id & 0x000007f8) >> 3);
+               priv->write_reg(priv, SJA1000_ID2, (id & 0x00000007) << 5);
        }
 
        for (i = 0; i < dlc; i++)
@@ -335,25 +336,25 @@ static void sja1000_rx(struct net_device *dev)
        if (skb == NULL)
                return;
 
-       fi = priv->read_reg(priv, REG_FI);
+       fi = priv->read_reg(priv, SJA1000_FI);
 
-       if (fi & FI_FF) {
+       if (fi & SJA1000_FI_FF) {
                /* extended frame format (EFF) */
-               dreg = EFF_BUF;
-               id = (priv->read_reg(priv, REG_ID1) << (5 + 16))
-                   | (priv->read_reg(priv, REG_ID2) << (5 + 8))
-                   | (priv->read_reg(priv, REG_ID3) << 5)
-                   | (priv->read_reg(priv, REG_ID4) >> 3);
+               dreg = SJA1000_EFF_BUF;
+               id = (priv->read_reg(priv, SJA1000_ID1) << 21)
+                   | (priv->read_reg(priv, SJA1000_ID2) << 13)
+                   | (priv->read_reg(priv, SJA1000_ID3) << 5)
+                   | (priv->read_reg(priv, SJA1000_ID4) >> 3);
                id |= CAN_EFF_FLAG;
        } else {
                /* standard frame format (SFF) */
-               dreg = SFF_BUF;
-               id = (priv->read_reg(priv, REG_ID1) << 3)
-                   | (priv->read_reg(priv, REG_ID2) >> 5);
+               dreg = SJA1000_SFF_BUF;
+               id = (priv->read_reg(priv, SJA1000_ID1) << 3)
+                   | (priv->read_reg(priv, SJA1000_ID2) >> 5);
        }
 
        cf->can_dlc = get_can_dlc(fi & 0x0F);
-       if (fi & FI_RTR) {
+       if (fi & SJA1000_FI_RTR) {
                id |= CAN_RTR_FLAG;
        } else {
                for (i = 0; i < cf->can_dlc; i++)
@@ -414,7 +415,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
                priv->can.can_stats.bus_error++;
                stats->rx_errors++;
 
-               ecc = priv->read_reg(priv, REG_ECC);
+               ecc = priv->read_reg(priv, SJA1000_ECC);
 
                cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
 
@@ -448,7 +449,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
        if (isrc & IRQ_ALI) {
                /* arbitration lost interrupt */
                netdev_dbg(dev, "arbitration lost interrupt\n");
-               alc = priv->read_reg(priv, REG_ALC);
+               alc = priv->read_reg(priv, SJA1000_ALC);
                priv->can.can_stats.arbitration_lost++;
                stats->tx_errors++;
                cf->can_id |= CAN_ERR_LOSTARB;
@@ -457,8 +458,8 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
 
        if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING ||
                                         state == CAN_STATE_ERROR_PASSIVE)) {
-               uint8_t rxerr = priv->read_reg(priv, REG_RXERR);
-               uint8_t txerr = priv->read_reg(priv, REG_TXERR);
+               uint8_t rxerr = priv->read_reg(priv, SJA1000_RXERR);
+               uint8_t txerr = priv->read_reg(priv, SJA1000_TXERR);
                cf->can_id |= CAN_ERR_CRTL;
                if (state == CAN_STATE_ERROR_WARNING) {
                        priv->can.can_stats.error_warning++;
@@ -494,15 +495,16 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
        int n = 0;
 
        /* Shared interrupts and IRQ off? */
-       if (priv->read_reg(priv, REG_IER) == IRQ_OFF)
+       if (priv->read_reg(priv, SJA1000_IER) == IRQ_OFF)
                return IRQ_NONE;
 
        if (priv->pre_irq)
                priv->pre_irq(priv);
 
-       while ((isrc = priv->read_reg(priv, REG_IR)) && (n < SJA1000_MAX_IRQ)) {
+       while ((isrc = priv->read_reg(priv, SJA1000_IR)) &&
+              (n < SJA1000_MAX_IRQ)) {
                n++;
-               status = priv->read_reg(priv, SJA1000_REG_SR);
+               status = priv->read_reg(priv, SJA1000_SR);
                /* check for absent controller due to hw unplug */
                if (status == 0xFF && sja1000_is_absent(priv))
                        return IRQ_NONE;
@@ -519,7 +521,7 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
                        } else {
                                /* transmission complete */
                                stats->tx_bytes +=
-                                       priv->read_reg(priv, REG_FI) & 0xf;
+                                       priv->read_reg(priv, SJA1000_FI) & 0xf;
                                stats->tx_packets++;
                                can_get_echo_skb(dev, 0);
                        }
@@ -530,7 +532,7 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
                        /* receive interrupt */
                        while (status & SR_RBS) {
                                sja1000_rx(dev);
-                               status = priv->read_reg(priv, SJA1000_REG_SR);
+                               status = priv->read_reg(priv, SJA1000_SR);
                                /* check for absent controller */
                                if (status == 0xFF && sja1000_is_absent(priv))
                                        return IRQ_NONE;
index aa48e053da27ce26ed394e8a4584ef9805828372..9d46398f815480231330ad4519974f14516dd289 100644 (file)
 #define SJA1000_MAX_IRQ 20     /* max. number of interrupts handled in ISR */
 
 /* SJA1000 registers - manual section 6.4 (Pelican Mode) */
-#define REG_MOD                0x00
-#define REG_CMR                0x01
-#define SJA1000_REG_SR         0x02
-#define REG_IR         0x03
-#define REG_IER                0x04
-#define REG_ALC                0x0B
-#define REG_ECC                0x0C
-#define REG_EWL                0x0D
-#define REG_RXERR      0x0E
-#define REG_TXERR      0x0F
-#define REG_ACCC0      0x10
-#define REG_ACCC1      0x11
-#define REG_ACCC2      0x12
-#define REG_ACCC3      0x13
-#define REG_ACCM0      0x14
-#define REG_ACCM1      0x15
-#define REG_ACCM2      0x16
-#define REG_ACCM3      0x17
-#define REG_RMC                0x1D
-#define REG_RBSA       0x1E
+#define SJA1000_MOD            0x00
+#define SJA1000_CMR            0x01
+#define SJA1000_SR             0x02
+#define SJA1000_IR             0x03
+#define SJA1000_IER            0x04
+#define SJA1000_ALC            0x0B
+#define SJA1000_ECC            0x0C
+#define SJA1000_EWL            0x0D
+#define SJA1000_RXERR          0x0E
+#define SJA1000_TXERR          0x0F
+#define SJA1000_ACCC0          0x10
+#define SJA1000_ACCC1          0x11
+#define SJA1000_ACCC2          0x12
+#define SJA1000_ACCC3          0x13
+#define SJA1000_ACCM0          0x14
+#define SJA1000_ACCM1          0x15
+#define SJA1000_ACCM2          0x16
+#define SJA1000_ACCM3          0x17
+#define SJA1000_RMC            0x1D
+#define SJA1000_RBSA           0x1E
 
 /* Common registers - manual section 6.5 */
-#define REG_BTR0       0x06
-#define REG_BTR1       0x07
-#define REG_OCR                0x08
-#define REG_CDR                0x1F
+#define SJA1000_BTR0           0x06
+#define SJA1000_BTR1           0x07
+#define SJA1000_OCR            0x08
+#define SJA1000_CDR            0x1F
 
-#define REG_FI         0x10
-#define SFF_BUF                0x13
-#define EFF_BUF                0x15
+#define SJA1000_FI             0x10
+#define SJA1000_SFF_BUF                0x13
+#define SJA1000_EFF_BUF                0x15
 
-#define FI_FF          0x80
-#define FI_RTR         0x40
+#define SJA1000_FI_FF          0x80
+#define SJA1000_FI_RTR         0x40
 
-#define REG_ID1                0x11
-#define REG_ID2                0x12
-#define REG_ID3                0x13
-#define REG_ID4                0x14
+#define SJA1000_ID1            0x11
+#define SJA1000_ID2            0x12
+#define SJA1000_ID3            0x13
+#define SJA1000_ID4            0x14
 
-#define CAN_RAM                0x20
+#define SJA1000_CAN_RAM                0x20
 
 /* mode register */
 #define MOD_RM         0x01
index 27aaaf99e73e62ed9949e6b76838b78b682ac5d2..144942f6372b68edd1bedc91175b8feab07cbe6e 100644 (file)
@@ -1690,7 +1690,7 @@ typhoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile __le32 * read
                        skb_checksum_none_assert(new_skb);
 
                if (rx->rxStatus & TYPHOON_RX_VLAN)
-                       __vlan_hwaccel_put_tag(new_skb,
+                       __vlan_hwaccel_put_tag(new_skb, htons(ETH_P_8021Q),
                                               ntohl(rx->vlanTag) & 0xffff);
                netif_receive_skb(new_skb);
 
@@ -2445,9 +2445,9 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
         * settings -- so we only allow the user to toggle the TX processing.
         */
        dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
-               NETIF_F_HW_VLAN_TX;
+               NETIF_F_HW_VLAN_CTAG_TX;
        dev->features = dev->hw_features |
-               NETIF_F_HW_VLAN_RX | NETIF_F_RXCSUM;
+               NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXCSUM;
 
        if(register_netdev(dev) < 0) {
                err_msg = "unable to register netdev";
index 549b77500579e823b60d34633555d482dff1c7b6..8b04bfc20cfba3b9c17984471dfccb6163799bdb 100644 (file)
@@ -594,7 +594,8 @@ static const struct ethtool_ops ethtool_ops;
 
 
 #ifdef VLAN_SUPPORT
-static int netdev_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+static int netdev_vlan_rx_add_vid(struct net_device *dev,
+                                 __be16 proto, u16 vid)
 {
        struct netdev_private *np = netdev_priv(dev);
 
@@ -608,7 +609,8 @@ static int netdev_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
        return 0;
 }
 
-static int netdev_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+static int netdev_vlan_rx_kill_vid(struct net_device *dev,
+                                  __be16 proto, u16 vid)
 {
        struct netdev_private *np = netdev_priv(dev);
 
@@ -702,7 +704,7 @@ static int starfire_init_one(struct pci_dev *pdev,
 #endif /* ZEROCOPY */
 
 #ifdef VLAN_SUPPORT
-       dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
 #endif /* VLAN_RX_KILL_VID */
 #ifdef ADDR_64BITS
        dev->features |= NETIF_F_HIGHDMA;
@@ -1496,7 +1498,7 @@ static int __netdev_rx(struct net_device *dev, int *quota)
                                printk(KERN_DEBUG "  netdev_rx() vlanid = %d\n",
                                       vlid);
                        }
-                       __vlan_hwaccel_put_tag(skb, vlid);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlid);
                }
 #endif /* VLAN_SUPPORT */
                netif_receive_skb(skb);
index a175d0be1ae1a23e1c2f0fc920efd039a65b02ba..ee705771bd2cb43658fd55edffa5811a179b6c80 100644 (file)
@@ -188,10 +188,9 @@ static int desc_list_init(struct net_device *dev)
 
                /* allocate a new skb for next time receive */
                new_skb = netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN);
-               if (!new_skb) {
-                       pr_notice("init: low on mem - packet dropped\n");
+               if (!new_skb)
                        goto init_error;
-               }
+
                skb_reserve(new_skb, NET_IP_ALIGN);
                /* Invidate the data cache of skb->data range when it is write back
                 * cache. It will prevent overwritting the new data from DMA
@@ -1236,7 +1235,6 @@ static void bfin_mac_rx(struct net_device *dev)
 
        new_skb = netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN);
        if (!new_skb) {
-               netdev_notice(dev, "rx: low on mem - packet dropped\n");
                dev->stats.rx_dropped++;
                goto out;
        }
index 0be2195e50340c24c5c2f5403b2f32b12c403c8e..269295403fc48959a35be1c65430e416fc385d0b 100644 (file)
@@ -1464,35 +1464,23 @@ static int greth_of_probe(struct platform_device *ofdev)
        }
 
        /* Allocate TX descriptor ring in coherent memory */
-       greth->tx_bd_base = (struct greth_bd *) dma_alloc_coherent(greth->dev,
-                                                                  1024,
-                                                                  &greth->tx_bd_base_phys,
-                                                                  GFP_KERNEL);
-
+       greth->tx_bd_base = dma_alloc_coherent(greth->dev, 1024,
+                                              &greth->tx_bd_base_phys,
+                                              GFP_KERNEL | __GFP_ZERO);
        if (!greth->tx_bd_base) {
-               if (netif_msg_probe(greth))
-                       dev_err(&dev->dev, "could not allocate descriptor memory.\n");
                err = -ENOMEM;
                goto error3;
        }
 
-       memset(greth->tx_bd_base, 0, 1024);
-
        /* Allocate RX descriptor ring in coherent memory */
-       greth->rx_bd_base = (struct greth_bd *) dma_alloc_coherent(greth->dev,
-                                                                  1024,
-                                                                  &greth->rx_bd_base_phys,
-                                                                  GFP_KERNEL);
-
+       greth->rx_bd_base = dma_alloc_coherent(greth->dev, 1024,
+                                              &greth->rx_bd_base_phys,
+                                              GFP_KERNEL | __GFP_ZERO);
        if (!greth->rx_bd_base) {
-               if (netif_msg_probe(greth))
-                       dev_err(greth->dev, "could not allocate descriptor memory.\n");
                err = -ENOMEM;
                goto error4;
        }
 
-       memset(greth->rx_bd_base, 0, 1024);
-
        /* Get MAC address from: module param, OF property or ID prom */
        for (i = 0; i < 6; i++) {
                if (macaddr[i] != 0)
index c0bc41a784ca253b012f327807e9d42a1ff0197b..b7894f8af9d1969e31410b0ffb3784c774a88275 100644 (file)
@@ -472,7 +472,7 @@ static int acenic_probe_one(struct pci_dev *pdev,
        ap->name = pci_name(pdev);
 
        dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
-       dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 
        dev->watchdog_timeo = 5*HZ;
 
@@ -2019,7 +2019,7 @@ static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm)
 
                /* send it up */
                if ((bd_flags & BD_FLG_VLAN_TAG))
-                       __vlan_hwaccel_put_tag(skb, retdesc->vlan);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), retdesc->vlan);
                netif_rx(skb);
 
                dev->stats.rx_packets++;
index 6e722dc37db7aa69ec639f47540ae5a9d0e1572d..65926a956575acfc42a49428a9653c539199ec20 100644 (file)
@@ -318,8 +318,6 @@ static int lance_rx (struct net_device *dev)
                        struct sk_buff *skb = netdev_alloc_skb(dev, len + 2);
 
                         if (!skb) {
-                                printk ("%s: Memory squeeze, deferring packet.\n",
-                                        dev->name);
                                 dev->stats.rx_dropped++;
                                 rd->mblength = 0;
                                 rd->rmd1_bits = LE_R1_OWN;
index 3789affbc0e5cbff032f5cf225725fd4a80a013d..0866e7627433c1148037b9ade8064351c2819722 100644 (file)
@@ -293,7 +293,6 @@ static int lance_rx(struct net_device *dev)
                        struct sk_buff *skb = netdev_alloc_skb(dev, len + 2);
 
                        if (!skb) {
-                               netdev_warn(dev, "Memory squeeze, deferring packet\n");
                                dev->stats.rx_dropped++;
                                rd->mblength = 0;
                                rd->rmd1_bits = LE_R1_OWN;
index 60e2b701afe726f05b041f0694ed6b454ddd05bd..9793767996a2a8e836259adde6fa0a342cd0f4d0 100644 (file)
@@ -528,7 +528,6 @@ am79c961_rx(struct net_device *dev, struct dev_priv *priv)
                        dev->stats.rx_packets++;
                } else {
                        am_writeword (dev, hdraddr + 2, RMD_OWN);
-                       printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name);
                        dev->stats.rx_dropped++;
                        break;
                }
index 42d4e6ad58a5f81098d7d7829528ecb057434e3d..8e6b665a672686350f872565e4e071fad7c6caa9 100644 (file)
@@ -793,7 +793,7 @@ static int amd8111e_rx_poll(struct napi_struct *napi, int budget)
 #if AMD8111E_VLAN_TAG_USED
                        if (vtag == TT_VLAN_TAGGED){
                                u16 vlan_tag = le16_to_cpu(lp->rx_ring[rx_index].tag_ctrl_info);
-                               __vlan_hwaccel_put_tag(skb, vlan_tag);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
                        }
 #endif
                        netif_receive_skb(skb);
@@ -1869,7 +1869,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev,
        SET_NETDEV_DEV(dev, &pdev->dev);
 
 #if AMD8111E_VLAN_TAG_USED
-       dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX ;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX ;
 #endif
 
        lp = netdev_priv(dev);
@@ -1907,7 +1907,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev,
        netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32);
 
 #if AMD8111E_VLAN_TAG_USED
-       dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 #endif
        /* Probe the external PHY */
        amd8111e_probe_ext_phy(dev);
index 98f4522fd17b7e69d3c524568d4b8c991ca80533..c178eb4c81668ad3a1b033f11c9070b9e63c934d 100644 (file)
@@ -193,7 +193,6 @@ static int ariadne_rx(struct net_device *dev)
 
                        skb = netdev_alloc_skb(dev, pkt_len + 2);
                        if (skb == NULL) {
-                               netdev_warn(dev, "Memory squeeze, deferring packet\n");
                                for (i = 0; i < RX_RING_SIZE; i++)
                                        if (lowb(priv->rx_ring[(entry + i) % RX_RING_SIZE]->RMD1) & RF_OWN)
                                                break;
index 84219df72f5122bf9bcd3d4ce103bca6d197fbe4..e8d0ef508f48f79baa7024219248c22b09cf95a9 100644 (file)
@@ -996,8 +996,6 @@ static int lance_rx( struct net_device *dev )
                        else {
                                skb = netdev_alloc_skb(dev, pkt_len + 2);
                                if (skb == NULL) {
-                                       DPRINTK( 1, ( "%s: Memory squeeze, deferring packet.\n",
-                                                                 dev->name ));
                                        for( i = 0; i < RX_RING_SIZE; i++ )
                                                if (MEM->rx_head[(entry+i) & RX_RING_MOD_MASK].flag &
                                                        RMD1_OWN_CHIP)
@@ -1149,9 +1147,7 @@ static struct net_device *atarilance_dev;
 static int __init atarilance_module_init(void)
 {
        atarilance_dev = atarilance_probe(-1);
-       if (IS_ERR(atarilance_dev))
-               return PTR_ERR(atarilance_dev);
-       return 0;
+       return PTR_RET(atarilance_dev);
 }
 
 static void __exit atarilance_module_exit(void)
index de774d419144c1fb86ea558b087a627f9f04a6dc..688aede742c7d0068d061f377582253f52280d12 100644 (file)
@@ -727,7 +727,6 @@ static int au1000_rx(struct net_device *dev)
                        frmlen -= 4; /* Remove FCS */
                        skb = netdev_alloc_skb(dev, frmlen + 2);
                        if (skb == NULL) {
-                               netdev_err(dev, "Memory squeeze, dropping packet.\n");
                                dev->stats.rx_dropped++;
                                continue;
                        }
index baca0bd1b39361b7d5ce7ff90a82cc43064fce7f..3d86ffeb4e15919b0b3efb21205b4195127ba79d 100644 (file)
@@ -607,8 +607,6 @@ static int lance_rx(struct net_device *dev)
                        skb = netdev_alloc_skb(dev, len + 2);
 
                        if (skb == 0) {
-                               printk("%s: Memory squeeze, deferring packet.\n",
-                                      dev->name);
                                dev->stats.rx_dropped++;
                                *rds_ptr(rd, mblength, lp->type) = 0;
                                *rds_ptr(rd, rmd1, lp->type) =
index 9af3c307862c1aead9d545aa51588385849325c0..a51497c9d2afb2fceda2f61d96d27c914eaa4700 100644 (file)
@@ -188,9 +188,7 @@ static struct net_device *dev_mvme147_lance;
 int __init init_module(void)
 {
        dev_mvme147_lance = mvme147lance_probe(-1);
-       if (IS_ERR(dev_mvme147_lance))
-               return PTR_ERR(dev_mvme147_lance);
-       return 0;
+       return PTR_RET(dev_mvme147_lance);
 }
 
 void __exit cleanup_module(void)
index 013b651085369c83e20db7b4c5f868c382c5dccd..26fc0ce0faa3299929895e47b5a4fe5288687630 100644 (file)
@@ -1238,7 +1238,7 @@ MODULE_PARM_DESC(dma, "ni6510 ISA DMA channel (ignored for some cards)");
 int __init init_module(void)
 {
        dev_ni65 = ni65_probe(-1);
-       return IS_ERR(dev_ni65) ? PTR_ERR(dev_ni65) : 0;
+       return PTR_RET(dev_ni65);
 }
 
 void __exit cleanup_module(void)
index 797f847edf13fd4dffc3349e8d4615a10dea3b97..ed21307276430895fadc736b16f8539afe1713cf 100644 (file)
@@ -1166,7 +1166,6 @@ static void pcnet32_rx_entry(struct net_device *dev,
                skb = netdev_alloc_skb(dev, pkt_len + NET_IP_ALIGN);
 
        if (skb == NULL) {
-               netif_err(lp, drv, dev, "Memory squeeze, dropping packet\n");
                dev->stats.rx_dropped++;
                return;
        }
index 74b3891b64836485143db3dca11948f29f95b683..4375abe61da1b4233678dd999784056a3237f3b4 100644 (file)
@@ -812,9 +812,6 @@ static int lance_rx( struct net_device *dev )
                        else {
                                skb = netdev_alloc_skb(dev, pkt_len + 2);
                                if (skb == NULL) {
-                                       DPRINTK( 1, ( "%s: Memory squeeze, deferring packet.\n",
-                                                     dev->name ));
-
                                        dev->stats.rx_dropped++;
                                        head->msg_length = 0;
                                        head->flag |= RMD1_OWN_CHIP;
@@ -943,9 +940,7 @@ static struct net_device *sun3lance_dev;
 int __init init_module(void)
 {
        sun3lance_dev = sun3lance_probe(-1);
-       if (IS_ERR(sun3lance_dev))
-               return PTR_ERR(sun3lance_dev);
-       return 0;
+       return PTR_RET(sun3lance_dev);
 }
 
 void __exit cleanup_module(void)
index 6a40290d3727579f2a3fd0b1009e5ef3d2323e06..f47b780892e97f4a4e5ca186e08ee1679f4eee6c 100644 (file)
@@ -536,8 +536,6 @@ static void lance_rx_dvma(struct net_device *dev)
                        skb = netdev_alloc_skb(dev, len + 2);
 
                        if (skb == NULL) {
-                               printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n",
-                                      dev->name);
                                dev->stats.rx_dropped++;
                                rd->mblength = 0;
                                rd->rmd1_bits = LE_R1_OWN;
@@ -708,8 +706,6 @@ static void lance_rx_pio(struct net_device *dev)
                        skb = netdev_alloc_skb(dev, len + 2);
 
                        if (skb == NULL) {
-                               printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n",
-                                      dev->name);
                                dev->stats.rx_dropped++;
                                sbus_writew(0, &rd->mblength);
                                sbus_writeb(LE_R1_OWN, &rd->rmd1_bits);
@@ -1377,10 +1373,9 @@ static int sparc_lance_probe_one(struct platform_device *op,
                        dma_alloc_coherent(&op->dev,
                                           sizeof(struct lance_init_block),
                                           &lp->init_block_dvma, GFP_ATOMIC);
-               if (!lp->init_block_mem) {
-                       printk(KERN_ERR "SunLance: Cannot allocate consistent DMA memory.\n");
+               if (!lp->init_block_mem)
                        goto fail;
-               }
+
                lp->pio_buffer = 0;
                lp->init_ring = lance_init_ring_dvma;
                lp->rx = lance_rx_dvma;
index a206779c68cf2d48f5a6bcc6f98b99dd2c98426f..4ce8ceb6220584c1e1af51217f91cfa97f4e5783 100644 (file)
@@ -386,20 +386,16 @@ static int mace_open(struct net_device *dev)
        /* Allocate the DMA ring buffers */
 
        mp->tx_ring = dma_alloc_coherent(mp->device,
-                       N_TX_RING * MACE_BUFF_SIZE,
-                       &mp->tx_ring_phys, GFP_KERNEL);
-       if (mp->tx_ring == NULL) {
-               printk(KERN_ERR "%s: unable to allocate DMA tx buffers\n", dev->name);
+                                        N_TX_RING * MACE_BUFF_SIZE,
+                                        &mp->tx_ring_phys, GFP_KERNEL);
+       if (mp->tx_ring == NULL)
                goto out1;
-       }
 
        mp->rx_ring = dma_alloc_coherent(mp->device,
-                       N_RX_RING * MACE_BUFF_SIZE,
-                       &mp->rx_ring_phys, GFP_KERNEL);
-       if (mp->rx_ring == NULL) {
-               printk(KERN_ERR "%s: unable to allocate DMA rx buffers\n", dev->name);
+                                        N_RX_RING * MACE_BUFF_SIZE,
+                                        &mp->rx_ring_phys, GFP_KERNEL);
+       if (mp->rx_ring == NULL)
                goto out2;
-       }
 
        mace_dma_off(dev);
 
index 1f07fc633ab9482bdf5df0bfe3305d919b6407c4..0ba900762b138d87a3547a0a6d810bcfb151429d 100644 (file)
@@ -417,7 +417,7 @@ static void atl1c_set_multi(struct net_device *netdev)
 
 static void __atl1c_vlan_mode(netdev_features_t features, u32 *mac_ctrl_data)
 {
-       if (features & NETIF_F_HW_VLAN_RX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_RX) {
                /* enable VLAN tag insert/strip */
                *mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
        } else {
@@ -494,10 +494,10 @@ static netdev_features_t atl1c_fix_features(struct net_device *netdev,
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        if (netdev->mtu > MAX_TSO_FRAME_SIZE)
                features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
@@ -510,7 +510,7 @@ static int atl1c_set_features(struct net_device *netdev,
 {
        netdev_features_t changed = netdev->features ^ features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                atl1c_vlan_mode(netdev, features);
 
        return 0;
@@ -1809,7 +1809,7 @@ rrs_checked:
 
                        AT_TAG_TO_VLAN(rrs->vlan_tag, vlan);
                        vlan = le16_to_cpu(vlan);
-                       __vlan_hwaccel_put_tag(skb, vlan);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
                }
                netif_receive_skb(skb);
 
@@ -2475,13 +2475,13 @@ static int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
        atl1c_set_ethtool_ops(netdev);
 
        /* TODO: add when ready */
-       netdev->hw_features =   NETIF_F_SG         |
-                               NETIF_F_HW_CSUM    |
-                               NETIF_F_HW_VLAN_RX |
-                               NETIF_F_TSO        |
+       netdev->hw_features =   NETIF_F_SG              |
+                               NETIF_F_HW_CSUM         |
+                               NETIF_F_HW_VLAN_CTAG_RX |
+                               NETIF_F_TSO             |
                                NETIF_F_TSO6;
-       netdev->features =      netdev->hw_features |
-                               NETIF_F_HW_VLAN_TX;
+       netdev->features =      netdev->hw_features     |
+                               NETIF_F_HW_VLAN_CTAG_TX;
        return 0;
 }
 
index ac25f05ff68f03b9eda25867d0ace6707efc2d41..0688bb82b442e9cd55d5ce6da9d1a9f064295403 100644 (file)
@@ -315,7 +315,7 @@ static void atl1e_set_multi(struct net_device *netdev)
 
 static void __atl1e_vlan_mode(netdev_features_t features, u32 *mac_ctrl_data)
 {
-       if (features & NETIF_F_HW_VLAN_RX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_RX) {
                /* enable VLAN tag insert/strip */
                *mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
        } else {
@@ -378,10 +378,10 @@ static netdev_features_t atl1e_fix_features(struct net_device *netdev,
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -391,7 +391,7 @@ static int atl1e_set_features(struct net_device *netdev,
 {
        netdev_features_t changed = netdev->features ^ features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                atl1e_vlan_mode(netdev, features);
 
        return 0;
@@ -1420,11 +1420,9 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que,
                        packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) &
                                        RRS_PKT_SIZE_MASK) - 4; /* CRC */
                        skb = netdev_alloc_skb_ip_align(netdev, packet_size);
-                       if (skb == NULL) {
-                               netdev_warn(netdev,
-                                           "Memory squeeze, deferring packet\n");
+                       if (skb == NULL)
                                goto skip_pkt;
-                       }
+
                        memcpy(skb->data, (u8 *)(prrs + 1), packet_size);
                        skb_put(skb, packet_size);
                        skb->protocol = eth_type_trans(skb, netdev);
@@ -1437,7 +1435,7 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que,
                                netdev_dbg(netdev,
                                           "RXD VLAN TAG<RRD>=0x%04x\n",
                                           prrs->vtag);
-                               __vlan_hwaccel_put_tag(skb, vlan_tag);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
                        }
                        netif_receive_skb(skb);
 
@@ -2200,9 +2198,9 @@ static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
        atl1e_set_ethtool_ops(netdev);
 
        netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO |
-                             NETIF_F_HW_VLAN_RX;
+                             NETIF_F_HW_VLAN_CTAG_RX;
        netdev->features = netdev->hw_features | NETIF_F_LLTX |
-                          NETIF_F_HW_VLAN_TX;
+                          NETIF_F_HW_VLAN_CTAG_TX;
 
        return 0;
 }
index 5b0d9931c720f9beb3f21b721527d01c98781960..fa0915f3999b24c8d3d53d0efddad6ad7efb98d6 100644 (file)
@@ -2024,7 +2024,7 @@ rrd_ok:
                                        ((rrd->vlan_tag & 7) << 13) |
                                        ((rrd->vlan_tag & 8) << 9);
 
-                       __vlan_hwaccel_put_tag(skb, vlan_tag);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
                }
                netif_receive_skb(skb);
 
@@ -2774,7 +2774,7 @@ static int atl1_close(struct net_device *netdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int atl1_suspend(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -2876,23 +2876,18 @@ static int atl1_resume(struct device *dev)
 
        return 0;
 }
+#endif
 
 static SIMPLE_DEV_PM_OPS(atl1_pm_ops, atl1_suspend, atl1_resume);
-#define ATL1_PM_OPS    (&atl1_pm_ops)
-
-#else
-
-static int atl1_suspend(struct device *dev) { return 0; }
-
-#define ATL1_PM_OPS    NULL
-#endif
 
 static void atl1_shutdown(struct pci_dev *pdev)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct atl1_adapter *adapter = netdev_priv(netdev);
 
+#ifdef CONFIG_PM_SLEEP
        atl1_suspend(&pdev->dev);
+#endif
        pci_wake_from_d3(pdev, adapter->wol);
        pci_set_power_state(pdev, PCI_D3hot);
 }
@@ -3023,10 +3018,10 @@ static int atl1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        netdev->features = NETIF_F_HW_CSUM;
        netdev->features |= NETIF_F_SG;
-       netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
+       netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
 
        netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO |
-                             NETIF_F_HW_VLAN_RX;
+                             NETIF_F_HW_VLAN_CTAG_RX;
 
        /* is this valid? see atl1_setup_mac_ctrl() */
        netdev->features |= NETIF_F_RXCSUM;
@@ -3147,7 +3142,7 @@ static struct pci_driver atl1_driver = {
        .probe = atl1_probe,
        .remove = atl1_remove,
        .shutdown = atl1_shutdown,
-       .driver.pm = ATL1_PM_OPS,
+       .driver.pm = &atl1_pm_ops,
 };
 
 /**
index 1278b47022e0d2cf2b406e7adbefa7168c25274b..265ce1b752ed0b169a927f6dad148e694ca4c5a2 100644 (file)
@@ -363,7 +363,7 @@ static inline void atl2_irq_disable(struct atl2_adapter *adapter)
 
 static void __atl2_vlan_mode(netdev_features_t features, u32 *ctrl)
 {
-       if (features & NETIF_F_HW_VLAN_RX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_RX) {
                /* enable VLAN tag insert/strip */
                *ctrl |= MAC_CTRL_RMV_VLAN;
        } else {
@@ -399,10 +399,10 @@ static netdev_features_t atl2_fix_features(struct net_device *netdev,
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -412,7 +412,7 @@ static int atl2_set_features(struct net_device *netdev,
 {
        netdev_features_t changed = netdev->features ^ features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                atl2_vlan_mode(netdev, features);
 
        return 0;
@@ -437,9 +437,6 @@ static void atl2_intr_rx(struct atl2_adapter *adapter)
                        /* alloc new buffer */
                        skb = netdev_alloc_skb_ip_align(netdev, rx_size);
                        if (NULL == skb) {
-                               printk(KERN_WARNING
-                                       "%s: Mem squeeze, deferring packet.\n",
-                                       netdev->name);
                                /*
                                 * Check that some rx space is free. If not,
                                 * free one and mark stats->rx_dropped++.
@@ -455,7 +452,7 @@ static void atl2_intr_rx(struct atl2_adapter *adapter)
                                        ((rxd->status.vtag&7) << 13) |
                                        ((rxd->status.vtag&8) << 9);
 
-                               __vlan_hwaccel_put_tag(skb, vlan_tag);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
                        }
                        netif_rx(skb);
                        netdev->stats.rx_bytes += rx_size;
@@ -890,7 +887,7 @@ static netdev_tx_t atl2_xmit_frame(struct sk_buff *skb,
                        skb->len-copy_len);
                offset = ((u32)(skb->len-copy_len + 3) & ~3);
        }
-#ifdef NETIF_F_HW_VLAN_TX
+#ifdef NETIF_F_HW_VLAN_CTAG_TX
        if (vlan_tx_tag_present(skb)) {
                u16 vlan_tag = vlan_tx_tag_get(skb);
                vlan_tag = (vlan_tag << 4) |
@@ -1416,8 +1413,8 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        err = -EIO;
 
-       netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_RX;
-       netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
+       netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX;
+       netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
 
        /* Init PHY as early as possible due to power saving issue  */
        atl2_phy_init(&adapter->hw);
index f82eb16994640388e8fa96b90333c79d1abcc6f8..46a622cceee4d472e7edca861be3bd37dafa384e 100644 (file)
@@ -220,7 +220,7 @@ static void atlx_link_chg_task(struct work_struct *work)
 
 static void __atlx_vlan_mode(netdev_features_t features, u32 *ctrl)
 {
-       if (features & NETIF_F_HW_VLAN_RX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_RX) {
                /* enable VLAN tag insert/strip */
                *ctrl |= MAC_CTRL_RMV_VLAN;
        } else {
@@ -257,10 +257,10 @@ static netdev_features_t atlx_fix_features(struct net_device *netdev,
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -270,7 +270,7 @@ static int atlx_set_features(struct net_device *netdev,
 {
        netdev_features_t changed = netdev->features ^ features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                atlx_vlan_mode(netdev, features);
 
        return 0;
index 7d81e059e811356b4621ec89df7e52eb56cbf15d..0b3e23ec37f769dde56f47f475b262c5c101f35d 100644 (file)
@@ -862,27 +862,25 @@ static int bcm_enet_open(struct net_device *dev)
 
        /* allocate rx dma ring */
        size = priv->rx_ring_size * sizeof(struct bcm_enet_desc);
-       p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma, GFP_KERNEL);
+       p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma,
+                              GFP_KERNEL | __GFP_ZERO);
        if (!p) {
-               dev_err(kdev, "cannot allocate rx ring %u\n", size);
                ret = -ENOMEM;
                goto out_freeirq_tx;
        }
 
-       memset(p, 0, size);
        priv->rx_desc_alloc_size = size;
        priv->rx_desc_cpu = p;
 
        /* allocate tx dma ring */
        size = priv->tx_ring_size * sizeof(struct bcm_enet_desc);
-       p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma, GFP_KERNEL);
+       p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma,
+                              GFP_KERNEL | __GFP_ZERO);
        if (!p) {
-               dev_err(kdev, "cannot allocate tx ring\n");
                ret = -ENOMEM;
                goto out_free_rx_ring;
        }
 
-       memset(p, 0, size);
        priv->tx_desc_alloc_size = size;
        priv->tx_desc_cpu = p;
 
@@ -1619,7 +1617,6 @@ static int bcm_enet_probe(struct platform_device *pdev)
        struct resource *res_mem, *res_irq, *res_irq_rx, *res_irq_tx;
        struct mii_bus *bus;
        const char *clk_name;
-       unsigned int iomem_size;
        int i, ret;
 
        /* stop if shared driver failed, assume driver->probe will be
@@ -1644,17 +1641,12 @@ static int bcm_enet_probe(struct platform_device *pdev)
        if (ret)
                goto out;
 
-       iomem_size = resource_size(res_mem);
-       if (!request_mem_region(res_mem->start, iomem_size, "bcm63xx_enet")) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       priv->base = ioremap(res_mem->start, iomem_size);
+       priv->base = devm_request_and_ioremap(&pdev->dev, res_mem);
        if (priv->base == NULL) {
                ret = -ENOMEM;
-               goto out_release_mem;
+               goto out;
        }
+
        dev->irq = priv->irq = res_irq->start;
        priv->irq_rx = res_irq_rx->start;
        priv->irq_tx = res_irq_tx->start;
@@ -1674,9 +1666,9 @@ static int bcm_enet_probe(struct platform_device *pdev)
        priv->mac_clk = clk_get(&pdev->dev, clk_name);
        if (IS_ERR(priv->mac_clk)) {
                ret = PTR_ERR(priv->mac_clk);
-               goto out_unmap;
+               goto out;
        }
-       clk_enable(priv->mac_clk);
+       clk_prepare_enable(priv->mac_clk);
 
        /* initialize default and fetch platform data */
        priv->rx_ring_size = BCMENET_DEF_RX_DESC;
@@ -1705,7 +1697,7 @@ static int bcm_enet_probe(struct platform_device *pdev)
                        priv->phy_clk = NULL;
                        goto out_put_clk_mac;
                }
-               clk_enable(priv->phy_clk);
+               clk_prepare_enable(priv->phy_clk);
        }
 
        /* do minimal hardware init to be able to probe mii bus */
@@ -1733,7 +1725,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 = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+               bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
+                                       GFP_KERNEL);
                if (!bus->irq) {
                        ret = -ENOMEM;
                        goto out_free_mdio;
@@ -1794,10 +1787,8 @@ static int bcm_enet_probe(struct platform_device *pdev)
        return 0;
 
 out_unregister_mdio:
-       if (priv->mii_bus) {
+       if (priv->mii_bus)
                mdiobus_unregister(priv->mii_bus);
-               kfree(priv->mii_bus->irq);
-       }
 
 out_free_mdio:
        if (priv->mii_bus)
@@ -1807,19 +1798,13 @@ out_uninit_hw:
        /* turn off mdc clock */
        enet_writel(priv, 0, ENET_MIISC_REG);
        if (priv->phy_clk) {
-               clk_disable(priv->phy_clk);
+               clk_disable_unprepare(priv->phy_clk);
                clk_put(priv->phy_clk);
        }
 
 out_put_clk_mac:
-       clk_disable(priv->mac_clk);
+       clk_disable_unprepare(priv->mac_clk);
        clk_put(priv->mac_clk);
-
-out_unmap:
-       iounmap(priv->base);
-
-out_release_mem:
-       release_mem_region(res_mem->start, iomem_size);
 out:
        free_netdev(dev);
        return ret;
@@ -1833,7 +1818,6 @@ static int bcm_enet_remove(struct platform_device *pdev)
 {
        struct bcm_enet_priv *priv;
        struct net_device *dev;
-       struct resource *res;
 
        /* stop netdevice */
        dev = platform_get_drvdata(pdev);
@@ -1845,7 +1829,6 @@ static int bcm_enet_remove(struct platform_device *pdev)
 
        if (priv->has_phy) {
                mdiobus_unregister(priv->mii_bus);
-               kfree(priv->mii_bus->irq);
                mdiobus_free(priv->mii_bus);
        } else {
                struct bcm63xx_enet_platform_data *pd;
@@ -1856,17 +1839,12 @@ static int bcm_enet_remove(struct platform_device *pdev)
                                       bcm_enet_mdio_write_mii);
        }
 
-       /* release device resources */
-       iounmap(priv->base);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, resource_size(res));
-
        /* disable hw block clocks */
        if (priv->phy_clk) {
-               clk_disable(priv->phy_clk);
+               clk_disable_unprepare(priv->phy_clk);
                clk_put(priv->phy_clk);
        }
-       clk_disable(priv->mac_clk);
+       clk_disable_unprepare(priv->mac_clk);
        clk_put(priv->mac_clk);
 
        platform_set_drvdata(pdev, NULL);
@@ -1889,31 +1867,20 @@ struct platform_driver bcm63xx_enet_driver = {
 static int bcm_enet_shared_probe(struct platform_device *pdev)
 {
        struct resource *res;
-       unsigned int iomem_size;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
                return -ENODEV;
 
-       iomem_size = resource_size(res);
-       if (!request_mem_region(res->start, iomem_size, "bcm63xx_enet_dma"))
-               return -EBUSY;
-
-       bcm_enet_shared_base = ioremap(res->start, iomem_size);
-       if (!bcm_enet_shared_base) {
-               release_mem_region(res->start, iomem_size);
+       bcm_enet_shared_base = devm_request_and_ioremap(&pdev->dev, res);
+       if (!bcm_enet_shared_base)
                return -ENOMEM;
-       }
+
        return 0;
 }
 
 static int bcm_enet_shared_remove(struct platform_device *pdev)
 {
-       struct resource *res;
-
-       iounmap(bcm_enet_shared_base);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, resource_size(res));
        return 0;
 }
 
index da5f4397f87c2744e13d4cb3bf0c99fb85d4666a..eec0af45b85996b2cdb02a2509563e944f93a742 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
 #include <linux/mii.h>
+#include <linux/phy.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
 #include <bcm47xx_nvram.h>
@@ -244,10 +245,8 @@ static int bgmac_dma_rx_skb_for_slot(struct bgmac *bgmac,
 
        /* Alloc skb */
        slot->skb = netdev_alloc_skb(bgmac->net_dev, BGMAC_RX_BUF_SIZE);
-       if (!slot->skb) {
-               bgmac_err(bgmac, "Allocation of skb failed!\n");
+       if (!slot->skb)
                return -ENOMEM;
-       }
 
        /* Poison - if everything goes fine, hardware will overwrite it */
        rx = (struct bgmac_rx_header *)slot->skb->data;
@@ -1312,6 +1311,73 @@ static const struct ethtool_ops bgmac_ethtool_ops = {
        .get_drvinfo            = bgmac_get_drvinfo,
 };
 
+/**************************************************
+ * MII
+ **************************************************/
+
+static int bgmac_mii_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+       return bgmac_phy_read(bus->priv, mii_id, regnum);
+}
+
+static int bgmac_mii_write(struct mii_bus *bus, int mii_id, int regnum,
+                          u16 value)
+{
+       return bgmac_phy_write(bus->priv, mii_id, regnum, value);
+}
+
+static int bgmac_mii_register(struct bgmac *bgmac)
+{
+       struct mii_bus *mii_bus;
+       int i, err = 0;
+
+       mii_bus = mdiobus_alloc();
+       if (!mii_bus)
+               return -ENOMEM;
+
+       mii_bus->name = "bgmac mii bus";
+       sprintf(mii_bus->id, "%s-%d-%d", "bgmac", bgmac->core->bus->num,
+               bgmac->core->core_unit);
+       mii_bus->priv = bgmac;
+       mii_bus->read = bgmac_mii_read;
+       mii_bus->write = bgmac_mii_write;
+       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;
+       }
+
+       bgmac->mii_bus = mii_bus;
+
+       return err;
+
+err_free_irq:
+       kfree(mii_bus->irq);
+err_free_bus:
+       mdiobus_free(mii_bus);
+       return err;
+}
+
+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);
+}
+
 /**************************************************
  * BCMA bus ops
  **************************************************/
@@ -1404,11 +1470,18 @@ static int bgmac_probe(struct bcma_device *core)
        if (core->bus->sprom.boardflags_lo & BGMAC_BFL_ENETADM)
                bgmac_warn(bgmac, "Support for ADMtek ethernet switch not implemented\n");
 
+       err = bgmac_mii_register(bgmac);
+       if (err) {
+               bgmac_err(bgmac, "Cannot register MDIO\n");
+               err = -ENOTSUPP;
+               goto err_dma_free;
+       }
+
        err = register_netdev(bgmac->net_dev);
        if (err) {
                bgmac_err(bgmac, "Cannot register net device\n");
                err = -ENOTSUPP;
-               goto err_dma_free;
+               goto err_mii_unregister;
        }
 
        netif_carrier_off(net_dev);
@@ -1417,6 +1490,8 @@ static int bgmac_probe(struct bcma_device *core)
 
        return 0;
 
+err_mii_unregister:
+       bgmac_mii_unregister(bgmac);
 err_dma_free:
        bgmac_dma_free(bgmac);
 
@@ -1433,6 +1508,7 @@ static void bgmac_remove(struct bcma_device *core)
 
        netif_napi_del(&bgmac->napi);
        unregister_netdev(bgmac->net_dev);
+       bgmac_mii_unregister(bgmac);
        bgmac_dma_free(bgmac);
        bcma_set_drvdata(core, NULL);
        free_netdev(bgmac->net_dev);
index 4ede614c81f8d5955d81bb4a06a636fdc8cb65da..98d4b5fcc070b4c0d10fbf09d9510dd6570492d3 100644 (file)
@@ -399,6 +399,7 @@ struct bgmac {
        struct bcma_device *cmn; /* Reference to CMN core for BCM4706 */
        struct net_device *net_dev;
        struct napi_struct napi;
+       struct mii_bus *mii_bus;
 
        /* DMA */
        struct bgmac_dma_ring tx_ring[BGMAC_MAX_TX_RINGS];
index 2f0ba8f2fd6c08f85547c11eb9744c831caea23c..5d204492c603a84fd94f03faf5fc1179abf61208 100644 (file)
@@ -416,7 +416,7 @@ static int bnx2_unregister_cnic(struct net_device *dev)
        return 0;
 }
 
-struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev)
+static struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev)
 {
        struct bnx2 *bp = netdev_priv(dev);
        struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
@@ -854,12 +854,11 @@ bnx2_alloc_mem(struct bnx2 *bp)
                                sizeof(struct statistics_block);
 
        status_blk = dma_alloc_coherent(&bp->pdev->dev, bp->status_stats_size,
-                                       &bp->status_blk_mapping, GFP_KERNEL);
+                                       &bp->status_blk_mapping,
+                                       GFP_KERNEL | __GFP_ZERO);
        if (status_blk == NULL)
                goto alloc_mem_err;
 
-       memset(status_blk, 0, bp->status_stats_size);
-
        bnapi = &bp->bnx2_napi[0];
        bnapi->status_blk.msi = status_blk;
        bnapi->hw_tx_cons_ptr =
@@ -3212,7 +3211,7 @@ bnx2_rx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget)
                }
                if ((status & L2_FHDR_STATUS_L2_VLAN_TAG) &&
                    !(bp->rx_mode & BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG))
-                       __vlan_hwaccel_put_tag(skb, rx_hdr->l2_fhdr_vlan_tag);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rx_hdr->l2_fhdr_vlan_tag);
 
                skb->protocol = eth_type_trans(skb, bp->dev);
 
@@ -3554,7 +3553,7 @@ bnx2_set_rx_mode(struct net_device *dev)
        rx_mode = bp->rx_mode & ~(BNX2_EMAC_RX_MODE_PROMISCUOUS |
                                  BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG);
        sort_mode = 1 | BNX2_RPM_SORT_USER0_BC_EN;
-       if (!(dev->features & NETIF_F_HW_VLAN_RX) &&
+       if (!(dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
             (bp->flags & BNX2_FLAG_CAN_KEEP_VLAN))
                rx_mode |= BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG;
        if (dev->flags & IFF_PROMISC) {
@@ -7696,7 +7695,7 @@ bnx2_fix_features(struct net_device *dev, netdev_features_t features)
        struct bnx2 *bp = netdev_priv(dev);
 
        if (!(bp->flags & BNX2_FLAG_CAN_KEEP_VLAN))
-               features |= NETIF_F_HW_VLAN_RX;
+               features |= NETIF_F_HW_VLAN_CTAG_RX;
 
        return features;
 }
@@ -7707,12 +7706,12 @@ bnx2_set_features(struct net_device *dev, netdev_features_t features)
        struct bnx2 *bp = netdev_priv(dev);
 
        /* TSO with VLAN tag won't work with current firmware */
-       if (features & NETIF_F_HW_VLAN_TX)
+       if (features & NETIF_F_HW_VLAN_CTAG_TX)
                dev->vlan_features |= (dev->hw_features & NETIF_F_ALL_TSO);
        else
                dev->vlan_features &= ~NETIF_F_ALL_TSO;
 
-       if ((!!(features & NETIF_F_HW_VLAN_RX) !=
+       if ((!!(features & NETIF_F_HW_VLAN_CTAG_RX) !=
            !!(bp->rx_mode & BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG)) &&
            netif_running(dev)) {
                bnx2_netif_stop(bp, false);
@@ -8552,7 +8551,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev->hw_features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
 
        dev->vlan_features = dev->hw_features;
-       dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+       dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
        dev->features |= dev->hw_features;
        dev->priv_flags |= IFF_UNICAST_FLT;
 
index e4605a965084d18d65de70ab46c8ccbc7debe907..3dba2a70a00e41f6ab86de036b4a3a7d859fd105 100644 (file)
@@ -26,8 +26,8 @@
  * (you will need to reboot afterwards) */
 /* #define BNX2X_STOP_ON_ERROR */
 
-#define DRV_MODULE_VERSION      "1.78.02-0"
-#define DRV_MODULE_RELDATE      "2013/01/14"
+#define DRV_MODULE_VERSION      "1.78.17-0"
+#define DRV_MODULE_RELDATE      "2013/04/11"
 #define BNX2X_BC_VER            0x040200
 
 #if defined(CONFIG_DCB)
@@ -492,7 +492,6 @@ enum bnx2x_tpa_mode_t {
 struct bnx2x_fastpath {
        struct bnx2x            *bp; /* parent */
 
-#define BNX2X_NAPI_WEIGHT       128
        struct napi_struct      napi;
        union host_hc_status_block      status_blk;
        /* chip independed shortcuts into sb structure */
@@ -613,9 +612,10 @@ struct bnx2x_fastpath {
  * START_BD            - describes packed
  * START_BD(splitted)  - includes unpaged data segment for GSO
  * PARSING_BD          - for TSO and CSUM data
+ * PARSING_BD2         - for encapsulation data
  * Frag BDs            - decribes pages for frags
  */
-#define BDS_PER_TX_PKT         3
+#define BDS_PER_TX_PKT         4
 #define MAX_BDS_PER_TX_PKT     (MAX_SKB_FRAGS + BDS_PER_TX_PKT)
 /* max BDs per tx packet including next pages */
 #define MAX_DESC_PER_TX_PKT    (MAX_BDS_PER_TX_PKT + \
@@ -730,18 +730,24 @@ struct bnx2x_fastpath {
 #define SKB_CS(skb)            (*(u16 *)(skb_transport_header(skb) + \
                                          skb->csum_offset))
 
-#define pbd_tcp_flags(skb)     (ntohl(tcp_flag_word(tcp_hdr(skb)))>>16 & 0xff)
+#define pbd_tcp_flags(tcp_hdr) (ntohl(tcp_flag_word(tcp_hdr))>>16 & 0xff)
 
-#define XMIT_PLAIN                     0
-#define XMIT_CSUM_V4                   0x1
-#define XMIT_CSUM_V6                   0x2
-#define XMIT_CSUM_TCP                  0x4
-#define XMIT_GSO_V4                    0x8
-#define XMIT_GSO_V6                    0x10
+#define XMIT_PLAIN             0
+#define XMIT_CSUM_V4           (1 << 0)
+#define XMIT_CSUM_V6           (1 << 1)
+#define XMIT_CSUM_TCP          (1 << 2)
+#define XMIT_GSO_V4            (1 << 3)
+#define XMIT_GSO_V6            (1 << 4)
+#define XMIT_CSUM_ENC_V4       (1 << 5)
+#define XMIT_CSUM_ENC_V6       (1 << 6)
+#define XMIT_GSO_ENC_V4                (1 << 7)
+#define XMIT_GSO_ENC_V6                (1 << 8)
 
-#define XMIT_CSUM                      (XMIT_CSUM_V4 | XMIT_CSUM_V6)
-#define XMIT_GSO                       (XMIT_GSO_V4 | XMIT_GSO_V6)
+#define XMIT_CSUM_ENC          (XMIT_CSUM_ENC_V4 | XMIT_CSUM_ENC_V6)
+#define XMIT_GSO_ENC           (XMIT_GSO_ENC_V4 | XMIT_GSO_ENC_V6)
 
+#define XMIT_CSUM              (XMIT_CSUM_V4 | XMIT_CSUM_V6 | XMIT_CSUM_ENC)
+#define XMIT_GSO               (XMIT_GSO_V4 | XMIT_GSO_V6 | XMIT_GSO_ENC)
 
 /* stuff added to make the code fit 80Col */
 #define CQE_TYPE(cqe_fp_flags)  ((cqe_fp_flags) & ETH_FAST_PATH_RX_CQE_TYPE)
@@ -844,6 +850,9 @@ struct bnx2x_common {
 #define CHIP_IS_57840_VF(bp)           (CHIP_NUM(bp) == CHIP_NUM_57840_VF)
 #define CHIP_IS_E1H(bp)                        (CHIP_IS_57711(bp) || \
                                         CHIP_IS_57711E(bp))
+#define CHIP_IS_57811xx(bp)            (CHIP_IS_57811(bp) || \
+                                        CHIP_IS_57811_MF(bp) || \
+                                        CHIP_IS_57811_VF(bp))
 #define CHIP_IS_E2(bp)                 (CHIP_IS_57712(bp) || \
                                         CHIP_IS_57712_MF(bp) || \
                                         CHIP_IS_57712_VF(bp))
@@ -853,9 +862,7 @@ struct bnx2x_common {
                                         CHIP_IS_57810(bp) || \
                                         CHIP_IS_57810_MF(bp) || \
                                         CHIP_IS_57810_VF(bp) || \
-                                        CHIP_IS_57811(bp) || \
-                                        CHIP_IS_57811_MF(bp) || \
-                                        CHIP_IS_57811_VF(bp) || \
+                                        CHIP_IS_57811xx(bp) || \
                                         CHIP_IS_57840(bp) || \
                                         CHIP_IS_57840_MF(bp) || \
                                         CHIP_IS_57840_VF(bp))
@@ -1215,14 +1222,16 @@ enum {
        BNX2X_SP_RTNL_ENABLE_SRIOV,
        BNX2X_SP_RTNL_VFPF_MCAST,
        BNX2X_SP_RTNL_VFPF_STORM_RX_MODE,
+       BNX2X_SP_RTNL_HYPERVISOR_VLAN,
 };
 
 
 struct bnx2x_prev_path_list {
+       struct list_head list;
        u8 bus;
        u8 slot;
        u8 path;
-       struct list_head list;
+       u8 aer;
        u8 undi;
 };
 
@@ -1269,6 +1278,8 @@ struct bnx2x {
 #define BP_FW_MB_IDX(bp)               BP_FW_MB_IDX_VN(bp, BP_VN(bp))
 
 #ifdef CONFIG_BNX2X_SRIOV
+       /* protects vf2pf mailbox from simultaneous access */
+       struct mutex            vf2pf_mutex;
        /* vf pf channel mailbox contains request and response buffers */
        struct bnx2x_vf_mbx_msg *vf2pf_mbox;
        dma_addr_t              vf2pf_mbox_mapping;
@@ -1281,6 +1292,8 @@ struct bnx2x {
        dma_addr_t              pf2vf_bulletin_mapping;
 
        struct pf_vf_bulletin_content   old_bulletin;
+
+       u16 requested_nr_virtfn;
 #endif /* CONFIG_BNX2X_SRIOV */
 
        struct net_device       *dev;
@@ -1944,12 +1957,9 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
 void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id,
                            bool is_pf);
 
-#define BNX2X_ILT_ZALLOC(x, y, size) \
-       do { \
-               x = dma_alloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \
-               if (x) \
-                       memset(x, 0, size); \
-       } while (0)
+#define BNX2X_ILT_ZALLOC(x, y, size)                           \
+       x = dma_alloc_coherent(&bp->pdev->dev, size, y,         \
+                              GFP_KERNEL | __GFP_ZERO)
 
 #define BNX2X_ILT_FREE(x, y, size) \
        do { \
@@ -2286,7 +2296,7 @@ static const u32 dmae_reg_go_c[] = {
        DMAE_REG_GO_C12, DMAE_REG_GO_C13, DMAE_REG_GO_C14, DMAE_REG_GO_C15
 };
 
-void bnx2x_set_ethtool_ops(struct net_device *netdev);
+void bnx2x_set_ethtool_ops(struct bnx2x *bp, struct net_device *netdev);
 void bnx2x_notify_link_changed(struct bnx2x *bp);
 
 #define BNX2X_MF_SD_PROTOCOL(bp) \
index 57619dd4a92b0ef08ff0b990ae26db355bb732f3..d72bd8c40aa15ec40c8be248cabaea49453ccfb7 100644 (file)
@@ -451,7 +451,8 @@ static void bnx2x_tpa_start(struct bnx2x_fastpath *fp, u16 queue,
  * Compute number of aggregated segments, and gso_type.
  */
 static void bnx2x_set_gro_params(struct sk_buff *skb, u16 parsing_flags,
-                                u16 len_on_bd, unsigned int pkt_len)
+                                u16 len_on_bd, unsigned int pkt_len,
+                                u16 num_of_coalesced_segs)
 {
        /* TPA aggregation won't have either IP options or TCP options
         * other than timestamp or IPv6 extension headers.
@@ -480,8 +481,7 @@ static void bnx2x_set_gro_params(struct sk_buff *skb, u16 parsing_flags,
        /* tcp_gro_complete() will copy NAPI_GRO_CB(skb)->count
         * to skb_shinfo(skb)->gso_segs
         */
-       NAPI_GRO_CB(skb)->count = DIV_ROUND_UP(pkt_len - hdrs_len,
-                                              skb_shinfo(skb)->gso_size);
+       NAPI_GRO_CB(skb)->count = num_of_coalesced_segs;
 }
 
 static int bnx2x_alloc_rx_sge(struct bnx2x *bp,
@@ -537,7 +537,8 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp,
        /* This is needed in order to enable forwarding support */
        if (frag_size)
                bnx2x_set_gro_params(skb, tpa_info->parsing_flags, len_on_bd,
-                                    le16_to_cpu(cqe->pkt_len));
+                                    le16_to_cpu(cqe->pkt_len),
+                                    le16_to_cpu(cqe->num_of_coalesced_segs));
 
 #ifdef BNX2X_STOP_ON_ERROR
        if (pages > min_t(u32, 8, MAX_SKB_FRAGS) * SGE_PAGES) {
@@ -718,7 +719,7 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp,
                if (!bnx2x_fill_frag_skb(bp, fp, tpa_info, pages,
                                         skb, cqe, cqe_idx)) {
                        if (tpa_info->parsing_flags & PARSING_FLAGS_VLAN)
-                               __vlan_hwaccel_put_tag(skb, tpa_info->vlan_tag);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tpa_info->vlan_tag);
                        bnx2x_gro_receive(bp, fp, skb);
                } else {
                        DP(NETIF_MSG_RX_STATUS,
@@ -993,7 +994,7 @@ reuse_rx:
 
                if (le16_to_cpu(cqe_fp->pars_flags.flags) &
                    PARSING_FLAGS_VLAN)
-                       __vlan_hwaccel_put_tag(skb,
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
                                               le16_to_cpu(cqe_fp->vlan_tag));
                napi_gro_receive(&fp->napi, skb);
 
@@ -2009,7 +2010,7 @@ static int bnx2x_init_hw(struct bnx2x *bp, u32 load_code)
  * Cleans the object that have internal lists without sending
  * ramrods. Should be run when interrutps are disabled.
  */
-static void bnx2x_squeeze_objects(struct bnx2x *bp)
+void bnx2x_squeeze_objects(struct bnx2x *bp)
 {
        int rc;
        unsigned long ramrod_flags = 0, vlan_mac_flags = 0;
@@ -2777,7 +2778,7 @@ load_error0:
 #endif /* ! BNX2X_STOP_ON_ERROR */
 }
 
-static int bnx2x_drain_tx_queues(struct bnx2x *bp)
+int bnx2x_drain_tx_queues(struct bnx2x *bp)
 {
        u8 rc = 0, cos, i;
 
@@ -3089,11 +3090,11 @@ int bnx2x_poll(struct napi_struct *napi, int budget)
  * to ease the pain of our fellow microcode engineers
  * we use one mapping for both BDs
  */
-static noinline u16 bnx2x_tx_split(struct bnx2x *bp,
-                                  struct bnx2x_fp_txdata *txdata,
-                                  struct sw_tx_bd *tx_buf,
-                                  struct eth_tx_start_bd **tx_bd, u16 hlen,
-                                  u16 bd_prod, int nbd)
+static u16 bnx2x_tx_split(struct bnx2x *bp,
+                         struct bnx2x_fp_txdata *txdata,
+                         struct sw_tx_bd *tx_buf,
+                         struct eth_tx_start_bd **tx_bd, u16 hlen,
+                         u16 bd_prod)
 {
        struct eth_tx_start_bd *h_tx_bd = *tx_bd;
        struct eth_tx_bd *d_tx_bd;
@@ -3101,11 +3102,10 @@ static noinline u16 bnx2x_tx_split(struct bnx2x *bp,
        int old_len = le16_to_cpu(h_tx_bd->nbytes);
 
        /* first fix first BD */
-       h_tx_bd->nbd = cpu_to_le16(nbd);
        h_tx_bd->nbytes = cpu_to_le16(hlen);
 
-       DP(NETIF_MSG_TX_QUEUED, "TSO split header size is %d (%x:%x) nbd %d\n",
-          h_tx_bd->nbytes, h_tx_bd->addr_hi, h_tx_bd->addr_lo, h_tx_bd->nbd);
+       DP(NETIF_MSG_TX_QUEUED, "TSO split header size is %d (%x:%x)\n",
+          h_tx_bd->nbytes, h_tx_bd->addr_hi, h_tx_bd->addr_lo);
 
        /* now get a new data BD
         * (after the pbd) and fill it */
@@ -3134,7 +3134,7 @@ static noinline u16 bnx2x_tx_split(struct bnx2x *bp,
 
 #define bswab32(b32) ((__force __le32) swab32((__force __u32) (b32)))
 #define bswab16(b16) ((__force __le16) swab16((__force __u16) (b16)))
-static inline __le16 bnx2x_csum_fix(unsigned char *t_header, u16 csum, s8 fix)
+static __le16 bnx2x_csum_fix(unsigned char *t_header, u16 csum, s8 fix)
 {
        __sum16 tsum = (__force __sum16) csum;
 
@@ -3149,30 +3149,47 @@ static inline __le16 bnx2x_csum_fix(unsigned char *t_header, u16 csum, s8 fix)
        return bswab16(tsum);
 }
 
-static inline u32 bnx2x_xmit_type(struct bnx2x *bp, struct sk_buff *skb)
+static u32 bnx2x_xmit_type(struct bnx2x *bp, struct sk_buff *skb)
 {
        u32 rc;
+       __u8 prot = 0;
+       __be16 protocol;
 
        if (skb->ip_summed != CHECKSUM_PARTIAL)
-               rc = XMIT_PLAIN;
+               return XMIT_PLAIN;
 
-       else {
-               if (vlan_get_protocol(skb) == htons(ETH_P_IPV6)) {
-                       rc = XMIT_CSUM_V6;
-                       if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
-                               rc |= XMIT_CSUM_TCP;
+       protocol = vlan_get_protocol(skb);
+       if (protocol == htons(ETH_P_IPV6)) {
+               rc = XMIT_CSUM_V6;
+               prot = ipv6_hdr(skb)->nexthdr;
+       } else {
+               rc = XMIT_CSUM_V4;
+               prot = ip_hdr(skb)->protocol;
+       }
 
+       if (!CHIP_IS_E1x(bp) && skb->encapsulation) {
+               if (inner_ip_hdr(skb)->version == 6) {
+                       rc |= XMIT_CSUM_ENC_V6;
+                       if (inner_ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
+                               rc |= XMIT_CSUM_TCP;
                } else {
-                       rc = XMIT_CSUM_V4;
-                       if (ip_hdr(skb)->protocol == IPPROTO_TCP)
+                       rc |= XMIT_CSUM_ENC_V4;
+                       if (inner_ip_hdr(skb)->protocol == IPPROTO_TCP)
                                rc |= XMIT_CSUM_TCP;
                }
        }
+       if (prot == IPPROTO_TCP)
+               rc |= XMIT_CSUM_TCP;
 
-       if (skb_is_gso_v6(skb))
-               rc |= XMIT_GSO_V6 | XMIT_CSUM_TCP | XMIT_CSUM_V6;
-       else if (skb_is_gso(skb))
-               rc |= XMIT_GSO_V4 | XMIT_CSUM_V4 | XMIT_CSUM_TCP;
+       if (skb_is_gso_v6(skb)) {
+               rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP | XMIT_CSUM_V6);
+               if (rc & XMIT_CSUM_ENC)
+                       rc |= XMIT_GSO_ENC_V6;
+       } else if (skb_is_gso(skb)) {
+               rc |= (XMIT_GSO_V4 | XMIT_CSUM_V4 | XMIT_CSUM_TCP);
+               if (rc & XMIT_CSUM_ENC)
+                       rc |= XMIT_GSO_ENC_V4;
+       }
 
        return rc;
 }
@@ -3257,14 +3274,23 @@ exit_lbl:
 }
 #endif
 
-static inline void bnx2x_set_pbd_gso_e2(struct sk_buff *skb, u32 *parsing_data,
-                                       u32 xmit_type)
+static void bnx2x_set_pbd_gso_e2(struct sk_buff *skb, u32 *parsing_data,
+                                u32 xmit_type)
 {
+       struct ipv6hdr *ipv6;
+
        *parsing_data |= (skb_shinfo(skb)->gso_size <<
                              ETH_TX_PARSE_BD_E2_LSO_MSS_SHIFT) &
                              ETH_TX_PARSE_BD_E2_LSO_MSS;
-       if ((xmit_type & XMIT_GSO_V6) &&
-           (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPV6))
+
+       if (xmit_type & XMIT_GSO_ENC_V6)
+               ipv6 = inner_ipv6_hdr(skb);
+       else if (xmit_type & XMIT_GSO_V6)
+               ipv6 = ipv6_hdr(skb);
+       else
+               ipv6 = NULL;
+
+       if (ipv6 && ipv6->nexthdr == NEXTHDR_IPV6)
                *parsing_data |= ETH_TX_PARSE_BD_E2_IPV6_WITH_EXT_HDR;
 }
 
@@ -3275,13 +3301,13 @@ static inline void bnx2x_set_pbd_gso_e2(struct sk_buff *skb, u32 *parsing_data,
  * @pbd:       parse BD
  * @xmit_type: xmit flags
  */
-static inline void bnx2x_set_pbd_gso(struct sk_buff *skb,
-                                    struct eth_tx_parse_bd_e1x *pbd,
-                                    u32 xmit_type)
+static void bnx2x_set_pbd_gso(struct sk_buff *skb,
+                             struct eth_tx_parse_bd_e1x *pbd,
+                             u32 xmit_type)
 {
        pbd->lso_mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
        pbd->tcp_send_seq = bswab32(tcp_hdr(skb)->seq);
-       pbd->tcp_flags = pbd_tcp_flags(skb);
+       pbd->tcp_flags = pbd_tcp_flags(tcp_hdr(skb));
 
        if (xmit_type & XMIT_GSO_V4) {
                pbd->ip_id = bswab16(ip_hdr(skb)->id);
@@ -3300,6 +3326,40 @@ static inline void bnx2x_set_pbd_gso(struct sk_buff *skb,
                cpu_to_le16(ETH_TX_PARSE_BD_E1X_PSEUDO_CS_WITHOUT_LEN);
 }
 
+/**
+ * bnx2x_set_pbd_csum_enc - update PBD with checksum and return header length
+ *
+ * @bp:                        driver handle
+ * @skb:               packet skb
+ * @parsing_data:      data to be updated
+ * @xmit_type:         xmit flags
+ *
+ * 57712/578xx related, when skb has encapsulation
+ */
+static u8 bnx2x_set_pbd_csum_enc(struct bnx2x *bp, struct sk_buff *skb,
+                                u32 *parsing_data, u32 xmit_type)
+{
+       *parsing_data |=
+               ((((u8 *)skb_inner_transport_header(skb) - skb->data) >> 1) <<
+               ETH_TX_PARSE_BD_E2_L4_HDR_START_OFFSET_W_SHIFT) &
+               ETH_TX_PARSE_BD_E2_L4_HDR_START_OFFSET_W;
+
+       if (xmit_type & XMIT_CSUM_TCP) {
+               *parsing_data |= ((inner_tcp_hdrlen(skb) / 4) <<
+                       ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW_SHIFT) &
+                       ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW;
+
+               return skb_inner_transport_header(skb) +
+                       inner_tcp_hdrlen(skb) - skb->data;
+       }
+
+       /* We support checksum offload for TCP and UDP only.
+        * No need to pass the UDP header length - it's a constant.
+        */
+       return skb_inner_transport_header(skb) +
+               sizeof(struct udphdr) - skb->data;
+}
+
 /**
  * bnx2x_set_pbd_csum_e2 - update PBD with checksum and return header length
  *
@@ -3308,15 +3368,15 @@ static inline void bnx2x_set_pbd_gso(struct sk_buff *skb,
  * @parsing_data:      data to be updated
  * @xmit_type:         xmit flags
  *
- * 57712 related
+ * 57712/578xx related
  */
-static inline  u8 bnx2x_set_pbd_csum_e2(struct bnx2x *bp, struct sk_buff *skb,
-                                       u32 *parsing_data, u32 xmit_type)
+static u8 bnx2x_set_pbd_csum_e2(struct bnx2x *bp, struct sk_buff *skb,
+                               u32 *parsing_data, u32 xmit_type)
 {
        *parsing_data |=
                ((((u8 *)skb_transport_header(skb) - skb->data) >> 1) <<
-               ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W_SHIFT) &
-               ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W;
+               ETH_TX_PARSE_BD_E2_L4_HDR_START_OFFSET_W_SHIFT) &
+               ETH_TX_PARSE_BD_E2_L4_HDR_START_OFFSET_W;
 
        if (xmit_type & XMIT_CSUM_TCP) {
                *parsing_data |= ((tcp_hdrlen(skb) / 4) <<
@@ -3331,17 +3391,15 @@ static inline  u8 bnx2x_set_pbd_csum_e2(struct bnx2x *bp, struct sk_buff *skb,
        return skb_transport_header(skb) + sizeof(struct udphdr) - skb->data;
 }
 
-static inline void bnx2x_set_sbd_csum(struct bnx2x *bp, struct sk_buff *skb,
-       struct eth_tx_start_bd *tx_start_bd, u32 xmit_type)
+/* set FW indication according to inner or outer protocols if tunneled */
+static void bnx2x_set_sbd_csum(struct bnx2x *bp, struct sk_buff *skb,
+                              struct eth_tx_start_bd *tx_start_bd,
+                              u32 xmit_type)
 {
        tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_L4_CSUM;
 
-       if (xmit_type & XMIT_CSUM_V4)
-               tx_start_bd->bd_flags.as_bitfield |=
-                                       ETH_TX_BD_FLAGS_IP_CSUM;
-       else
-               tx_start_bd->bd_flags.as_bitfield |=
-                                       ETH_TX_BD_FLAGS_IPV6;
+       if (xmit_type & (XMIT_CSUM_ENC_V6 | XMIT_CSUM_V6))
+               tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_IPV6;
 
        if (!(xmit_type & XMIT_CSUM_TCP))
                tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_IS_UDP;
@@ -3355,9 +3413,9 @@ static inline void bnx2x_set_sbd_csum(struct bnx2x *bp, struct sk_buff *skb,
  * @pbd:       parse BD to be updated
  * @xmit_type: xmit flags
  */
-static inline u8 bnx2x_set_pbd_csum(struct bnx2x *bp, struct sk_buff *skb,
-       struct eth_tx_parse_bd_e1x *pbd,
-       u32 xmit_type)
+static u8 bnx2x_set_pbd_csum(struct bnx2x *bp, struct sk_buff *skb,
+                            struct eth_tx_parse_bd_e1x *pbd,
+                            u32 xmit_type)
 {
        u8 hlen = (skb_network_header(skb) - skb->data) >> 1;
 
@@ -3403,6 +3461,70 @@ static inline u8 bnx2x_set_pbd_csum(struct bnx2x *bp, struct sk_buff *skb,
        return hlen;
 }
 
+static void bnx2x_update_pbds_gso_enc(struct sk_buff *skb,
+                                     struct eth_tx_parse_bd_e2 *pbd_e2,
+                                     struct eth_tx_parse_2nd_bd *pbd2,
+                                     u16 *global_data,
+                                     u32 xmit_type)
+{
+       u16 hlen_w = 0;
+       u8 outerip_off, outerip_len = 0;
+       /* from outer IP to transport */
+       hlen_w = (skb_inner_transport_header(skb) -
+                 skb_network_header(skb)) >> 1;
+
+       /* transport len */
+       if (xmit_type & XMIT_CSUM_TCP)
+               hlen_w += inner_tcp_hdrlen(skb) >> 1;
+       else
+               hlen_w += sizeof(struct udphdr) >> 1;
+
+       pbd2->fw_ip_hdr_to_payload_w = hlen_w;
+
+       if (xmit_type & XMIT_CSUM_ENC_V4) {
+               struct iphdr *iph = ip_hdr(skb);
+               pbd2->fw_ip_csum_wo_len_flags_frag =
+                       bswab16(csum_fold((~iph->check) -
+                                         iph->tot_len - iph->frag_off));
+       } else {
+               pbd2->fw_ip_hdr_to_payload_w =
+                       hlen_w - ((sizeof(struct ipv6hdr)) >> 1);
+       }
+
+       pbd2->tcp_send_seq = bswab32(inner_tcp_hdr(skb)->seq);
+
+       pbd2->tcp_flags = pbd_tcp_flags(inner_tcp_hdr(skb));
+
+       if (xmit_type & XMIT_GSO_V4) {
+               pbd2->hw_ip_id = bswab16(inner_ip_hdr(skb)->id);
+
+               pbd_e2->data.tunnel_data.pseudo_csum =
+                       bswab16(~csum_tcpudp_magic(
+                                       inner_ip_hdr(skb)->saddr,
+                                       inner_ip_hdr(skb)->daddr,
+                                       0, IPPROTO_TCP, 0));
+
+               outerip_len = ip_hdr(skb)->ihl << 1;
+       } else {
+               pbd_e2->data.tunnel_data.pseudo_csum =
+                       bswab16(~csum_ipv6_magic(
+                                       &inner_ipv6_hdr(skb)->saddr,
+                                       &inner_ipv6_hdr(skb)->daddr,
+                                       0, IPPROTO_TCP, 0));
+       }
+
+       outerip_off = (skb_network_header(skb) - skb->data) >> 1;
+
+       *global_data |=
+               outerip_off |
+               (!!(xmit_type & XMIT_CSUM_V6) <<
+                       ETH_TX_PARSE_2ND_BD_IP_HDR_TYPE_OUTER_SHIFT) |
+               (outerip_len <<
+                       ETH_TX_PARSE_2ND_BD_IP_HDR_LEN_OUTER_W_SHIFT) |
+               ((skb->protocol == cpu_to_be16(ETH_P_8021Q)) <<
+                       ETH_TX_PARSE_2ND_BD_LLC_SNAP_EN_SHIFT);
+}
+
 /* called with netif_tx_lock
  * bnx2x_tx_int() runs without netif_tx_lock unless it needs to call
  * netif_wake_queue()
@@ -3418,6 +3540,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct eth_tx_bd *tx_data_bd, *total_pkt_bd = NULL;
        struct eth_tx_parse_bd_e1x *pbd_e1x = NULL;
        struct eth_tx_parse_bd_e2 *pbd_e2 = NULL;
+       struct eth_tx_parse_2nd_bd *pbd2 = NULL;
        u32 pbd_e2_parsing_data = 0;
        u16 pkt_prod, bd_prod;
        int nbd, txq_index;
@@ -3485,7 +3608,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        mac_type = MULTICAST_ADDRESS;
        }
 
-#if (MAX_SKB_FRAGS >= MAX_FETCH_BD - 3)
+#if (MAX_SKB_FRAGS >= MAX_FETCH_BD - BDS_PER_TX_PKT)
        /* First, check if we need to linearize the skb (due to FW
           restrictions). No need to check fragmentation if page size > 8K
           (there will be no violation to FW restrictions) */
@@ -3533,12 +3656,9 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
        first_bd = tx_start_bd;
 
        tx_start_bd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD;
-       SET_FLAG(tx_start_bd->general_data,
-                ETH_TX_START_BD_PARSE_NBDS,
-                0);
 
-       /* header nbd */
-       SET_FLAG(tx_start_bd->general_data, ETH_TX_START_BD_HDR_NBDS, 1);
+       /* header nbd: indirectly zero other flags! */
+       tx_start_bd->general_data = 1 << ETH_TX_START_BD_HDR_NBDS_SHIFT;
 
        /* remember the first BD of the packet */
        tx_buf->first_bd = txdata->tx_bd_prod;
@@ -3558,19 +3678,16 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
                /* when transmitting in a vf, start bd must hold the ethertype
                 * for fw to enforce it
                 */
-#ifndef BNX2X_STOP_ON_ERROR
-               if (IS_VF(bp)) {
-#endif
+               if (IS_VF(bp))
                        tx_start_bd->vlan_or_ethertype =
                                cpu_to_le16(ntohs(eth->h_proto));
-#ifndef BNX2X_STOP_ON_ERROR
-               } else {
+               else
                        /* used by FW for packet accounting */
                        tx_start_bd->vlan_or_ethertype = cpu_to_le16(pkt_prod);
-               }
-#endif
        }
 
+       nbd = 2; /* start_bd + pbd + frags (updated when pages are mapped) */
+
        /* turn on parsing and get a BD */
        bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
 
@@ -3580,23 +3697,58 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (!CHIP_IS_E1x(bp)) {
                pbd_e2 = &txdata->tx_desc_ring[bd_prod].parse_bd_e2;
                memset(pbd_e2, 0, sizeof(struct eth_tx_parse_bd_e2));
-               /* Set PBD in checksum offload case */
-               if (xmit_type & XMIT_CSUM)
+
+               if (xmit_type & XMIT_CSUM_ENC) {
+                       u16 global_data = 0;
+
+                       /* Set PBD in enc checksum offload case */
+                       hlen = bnx2x_set_pbd_csum_enc(bp, skb,
+                                                     &pbd_e2_parsing_data,
+                                                     xmit_type);
+
+                       /* turn on 2nd parsing and get a BD */
+                       bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
+
+                       pbd2 = &txdata->tx_desc_ring[bd_prod].parse_2nd_bd;
+
+                       memset(pbd2, 0, sizeof(*pbd2));
+
+                       pbd_e2->data.tunnel_data.ip_hdr_start_inner_w =
+                               (skb_inner_network_header(skb) -
+                                skb->data) >> 1;
+
+                       if (xmit_type & XMIT_GSO_ENC)
+                               bnx2x_update_pbds_gso_enc(skb, pbd_e2, pbd2,
+                                                         &global_data,
+                                                         xmit_type);
+
+                       pbd2->global_data = cpu_to_le16(global_data);
+
+                       /* add addition parse BD indication to start BD */
+                       SET_FLAG(tx_start_bd->general_data,
+                                ETH_TX_START_BD_PARSE_NBDS, 1);
+                       /* set encapsulation flag in start BD */
+                       SET_FLAG(tx_start_bd->general_data,
+                                ETH_TX_START_BD_TUNNEL_EXIST, 1);
+                       nbd++;
+               } else if (xmit_type & XMIT_CSUM) {
+                       /* Set PBD in checksum offload case w/o encapsulation */
                        hlen = bnx2x_set_pbd_csum_e2(bp, skb,
                                                     &pbd_e2_parsing_data,
                                                     xmit_type);
+               }
 
-               if (IS_MF_SI(bp) || IS_VF(bp)) {
-                       /* fill in the MAC addresses in the PBD - for local
-                        * switching
-                        */
-                       bnx2x_set_fw_mac_addr(&pbd_e2->src_mac_addr_hi,
-                                             &pbd_e2->src_mac_addr_mid,
-                                             &pbd_e2->src_mac_addr_lo,
+               /* Add the macs to the parsing BD this is a vf */
+               if (IS_VF(bp)) {
+                       /* override GRE parameters in BD */
+                       bnx2x_set_fw_mac_addr(&pbd_e2->data.mac_addr.src_hi,
+                                             &pbd_e2->data.mac_addr.src_mid,
+                                             &pbd_e2->data.mac_addr.src_lo,
                                              eth->h_source);
-                       bnx2x_set_fw_mac_addr(&pbd_e2->dst_mac_addr_hi,
-                                             &pbd_e2->dst_mac_addr_mid,
-                                             &pbd_e2->dst_mac_addr_lo,
+
+                       bnx2x_set_fw_mac_addr(&pbd_e2->data.mac_addr.dst_hi,
+                                             &pbd_e2->data.mac_addr.dst_mid,
+                                             &pbd_e2->data.mac_addr.dst_lo,
                                              eth->h_dest);
                }
 
@@ -3618,14 +3770,13 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
        /* Setup the data pointer of the first BD of the packet */
        tx_start_bd->addr_hi = cpu_to_le32(U64_HI(mapping));
        tx_start_bd->addr_lo = cpu_to_le32(U64_LO(mapping));
-       nbd = 2; /* start_bd + pbd + frags (updated when pages are mapped) */
        tx_start_bd->nbytes = cpu_to_le16(skb_headlen(skb));
        pkt_size = tx_start_bd->nbytes;
 
        DP(NETIF_MSG_TX_QUEUED,
-          "first bd @%p  addr (%x:%x)  nbd %d  nbytes %d  flags %x  vlan %x\n",
+          "first bd @%p  addr (%x:%x)  nbytes %d  flags %x  vlan %x\n",
           tx_start_bd, tx_start_bd->addr_hi, tx_start_bd->addr_lo,
-          le16_to_cpu(tx_start_bd->nbd), le16_to_cpu(tx_start_bd->nbytes),
+          le16_to_cpu(tx_start_bd->nbytes),
           tx_start_bd->bd_flags.as_bitfield,
           le16_to_cpu(tx_start_bd->vlan_or_ethertype));
 
@@ -3638,10 +3789,12 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
                tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_SW_LSO;
 
-               if (unlikely(skb_headlen(skb) > hlen))
+               if (unlikely(skb_headlen(skb) > hlen)) {
+                       nbd++;
                        bd_prod = bnx2x_tx_split(bp, txdata, tx_buf,
                                                 &tx_start_bd, hlen,
-                                                bd_prod, ++nbd);
+                                                bd_prod);
+               }
                if (!CHIP_IS_E1x(bp))
                        bnx2x_set_pbd_gso_e2(skb, &pbd_e2_parsing_data,
                                             xmit_type);
@@ -3731,9 +3884,13 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (pbd_e2)
                DP(NETIF_MSG_TX_QUEUED,
                   "PBD (E2) @%p  dst %x %x %x src %x %x %x parsing_data %x\n",
-                  pbd_e2, pbd_e2->dst_mac_addr_hi, pbd_e2->dst_mac_addr_mid,
-                  pbd_e2->dst_mac_addr_lo, pbd_e2->src_mac_addr_hi,
-                  pbd_e2->src_mac_addr_mid, pbd_e2->src_mac_addr_lo,
+                  pbd_e2,
+                  pbd_e2->data.mac_addr.dst_hi,
+                  pbd_e2->data.mac_addr.dst_mid,
+                  pbd_e2->data.mac_addr.dst_lo,
+                  pbd_e2->data.mac_addr.src_hi,
+                  pbd_e2->data.mac_addr.src_mid,
+                  pbd_e2->data.mac_addr.src_lo,
                   pbd_e2->parsing_data);
        DP(NETIF_MSG_TX_QUEUED, "doorbell: nbd %d  bd %u\n", nbd, bd_prod);
 
index aee7671ff4c10bd55eda3a4b93a82e083ffaeb86..54e1b149acb3f5f1777547d88aeff7470a0d8668 100644 (file)
@@ -50,13 +50,13 @@ extern int int_mode;
                } \
        } while (0)
 
-#define BNX2X_PCI_ALLOC(x, y, size) \
-       do { \
-               x = dma_alloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \
-               if (x == NULL) \
-                       goto alloc_mem_err; \
-               memset((void *)x, 0, size); \
-       } while (0)
+#define BNX2X_PCI_ALLOC(x, y, size)                            \
+do {                                                           \
+       x = dma_alloc_coherent(&bp->pdev->dev, size, y,         \
+                              GFP_KERNEL | __GFP_ZERO);        \
+       if (x == NULL)                                          \
+               goto alloc_mem_err;                             \
+} while (0)
 
 #define BNX2X_ALLOC(x, size) \
        do { \
@@ -496,7 +496,10 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev);
 /* setup_tc callback */
 int bnx2x_setup_tc(struct net_device *dev, u8 num_tc);
 
+int bnx2x_get_vf_config(struct net_device *dev, int vf,
+                       struct ifla_vf_info *ivi);
 int bnx2x_set_vf_mac(struct net_device *dev, int queue, u8 *mac);
+int bnx2x_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos);
 
 /* select_queue callback */
 u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb);
@@ -834,7 +837,7 @@ static inline void bnx2x_add_all_napi_cnic(struct bnx2x *bp)
        /* Add NAPI objects */
        for_each_rx_queue_cnic(bp, i)
                netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
-                              bnx2x_poll, BNX2X_NAPI_WEIGHT);
+                              bnx2x_poll, NAPI_POLL_WEIGHT);
 }
 
 static inline void bnx2x_add_all_napi(struct bnx2x *bp)
@@ -844,7 +847,7 @@ static inline void bnx2x_add_all_napi(struct bnx2x *bp)
        /* Add NAPI objects */
        for_each_eth_queue(bp, i)
                netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
-                              bnx2x_poll, BNX2X_NAPI_WEIGHT);
+                              bnx2x_poll, NAPI_POLL_WEIGHT);
 }
 
 static inline void bnx2x_del_all_napi_cnic(struct bnx2x *bp)
@@ -970,6 +973,9 @@ static inline int bnx2x_func_start(struct bnx2x *bp)
        else /* CHIP_IS_E1X */
                start_params->network_cos_mode = FW_WRR;
 
+       start_params->gre_tunnel_mode = IPGRE_TUNNEL;
+       start_params->gre_tunnel_rss = GRE_INNER_HEADERS_RSS;
+
        return bnx2x_func_state_change(bp, &func_params);
 }
 
@@ -1396,4 +1402,8 @@ static inline bool bnx2x_is_valid_ether_addr(struct bnx2x *bp, u8 *addr)
  *
  */
 void bnx2x_fill_fw_str(struct bnx2x *bp, char *buf, size_t buf_len);
+
+int bnx2x_drain_tx_queues(struct bnx2x *bp);
+void bnx2x_squeeze_objects(struct bnx2x *bp);
+
 #endif /* BNX2X_CMN_H */
index edfa67adf2f975d6b5d6dc382e4cf6d5617a2b3b..88e9b47a3eb6a2255c687fb4b43f54e164b58686 100644 (file)
@@ -1364,11 +1364,27 @@ static int bnx2x_nvram_read(struct bnx2x *bp, u32 offset, u8 *ret_buf,
        return rc;
 }
 
+static int bnx2x_nvram_read32(struct bnx2x *bp, u32 offset, u32 *buf,
+                             int buf_size)
+{
+       int rc;
+
+       rc = bnx2x_nvram_read(bp, offset, (u8 *)buf, buf_size);
+
+       if (!rc) {
+               __be32 *be = (__be32 *)buf;
+
+               while ((buf_size -= 4) >= 0)
+                       *buf++ = be32_to_cpu(*be++);
+       }
+
+       return rc;
+}
+
 static int bnx2x_get_eeprom(struct net_device *dev,
                            struct ethtool_eeprom *eeprom, u8 *eebuf)
 {
        struct bnx2x *bp = netdev_priv(dev);
-       int rc;
 
        if (!netif_running(dev)) {
                DP(BNX2X_MSG_ETHTOOL  | BNX2X_MSG_NVM,
@@ -1383,9 +1399,7 @@ static int bnx2x_get_eeprom(struct net_device *dev,
 
        /* parameters already validated in ethtool_get_eeprom */
 
-       rc = bnx2x_nvram_read(bp, eeprom->offset, eebuf, eeprom->len);
-
-       return rc;
+       return bnx2x_nvram_read(bp, eeprom->offset, eebuf, eeprom->len);
 }
 
 static int bnx2x_get_module_eeprom(struct net_device *dev,
@@ -1393,10 +1407,9 @@ static int bnx2x_get_module_eeprom(struct net_device *dev,
                                   u8 *data)
 {
        struct bnx2x *bp = netdev_priv(dev);
-       int rc = 0, phy_idx;
+       int rc = -EINVAL, phy_idx;
        u8 *user_data = data;
-       int remaining_len = ee->len, xfer_size;
-       unsigned int page_off = ee->offset;
+       unsigned int start_addr = ee->offset, xfer_size = 0;
 
        if (!netif_running(dev)) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
@@ -1405,21 +1418,52 @@ static int bnx2x_get_module_eeprom(struct net_device *dev,
        }
 
        phy_idx = bnx2x_get_cur_phy_idx(bp);
-       bnx2x_acquire_phy_lock(bp);
-       while (!rc && remaining_len > 0) {
-               xfer_size = (remaining_len > SFP_EEPROM_PAGE_SIZE) ?
-                       SFP_EEPROM_PAGE_SIZE : remaining_len;
+
+       /* Read A0 section */
+       if (start_addr < ETH_MODULE_SFF_8079_LEN) {
+               /* Limit transfer size to the A0 section boundary */
+               if (start_addr + ee->len > ETH_MODULE_SFF_8079_LEN)
+                       xfer_size = ETH_MODULE_SFF_8079_LEN - start_addr;
+               else
+                       xfer_size = ee->len;
+               bnx2x_acquire_phy_lock(bp);
                rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],
                                                  &bp->link_params,
-                                                 page_off,
+                                                 I2C_DEV_ADDR_A0,
+                                                 start_addr,
                                                  xfer_size,
                                                  user_data);
-               remaining_len -= xfer_size;
+               bnx2x_release_phy_lock(bp);
+               if (rc) {
+                       DP(BNX2X_MSG_ETHTOOL, "Failed reading A0 section\n");
+
+                       return -EINVAL;
+               }
                user_data += xfer_size;
-               page_off += xfer_size;
+               start_addr += xfer_size;
        }
 
-       bnx2x_release_phy_lock(bp);
+       /* Read A2 section */
+       if ((start_addr >= ETH_MODULE_SFF_8079_LEN) &&
+           (start_addr < ETH_MODULE_SFF_8472_LEN)) {
+               xfer_size = ee->len - xfer_size;
+               /* Limit transfer size to the A2 section boundary */
+               if (start_addr + xfer_size > ETH_MODULE_SFF_8472_LEN)
+                       xfer_size = ETH_MODULE_SFF_8472_LEN - start_addr;
+               start_addr -= ETH_MODULE_SFF_8079_LEN;
+               bnx2x_acquire_phy_lock(bp);
+               rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],
+                                                 &bp->link_params,
+                                                 I2C_DEV_ADDR_A2,
+                                                 start_addr,
+                                                 xfer_size,
+                                                 user_data);
+               bnx2x_release_phy_lock(bp);
+               if (rc) {
+                       DP(BNX2X_MSG_ETHTOOL, "Failed reading A2 section\n");
+                       return -EINVAL;
+               }
+       }
        return rc;
 }
 
@@ -1427,24 +1471,50 @@ static int bnx2x_get_module_info(struct net_device *dev,
                                 struct ethtool_modinfo *modinfo)
 {
        struct bnx2x *bp = netdev_priv(dev);
-       int phy_idx;
+       int phy_idx, rc;
+       u8 sff8472_comp, diag_type;
+
        if (!netif_running(dev)) {
-               DP(BNX2X_MSG_ETHTOOL  | BNX2X_MSG_NVM,
+               DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "cannot access eeprom when the interface is down\n");
                return -EAGAIN;
        }
-
        phy_idx = bnx2x_get_cur_phy_idx(bp);
-       switch (bp->link_params.phy[phy_idx].media_type) {
-       case ETH_PHY_SFPP_10G_FIBER:
-       case ETH_PHY_SFP_1G_FIBER:
-       case ETH_PHY_DA_TWINAX:
+       bnx2x_acquire_phy_lock(bp);
+       rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],
+                                         &bp->link_params,
+                                         I2C_DEV_ADDR_A0,
+                                         SFP_EEPROM_SFF_8472_COMP_ADDR,
+                                         SFP_EEPROM_SFF_8472_COMP_SIZE,
+                                         &sff8472_comp);
+       bnx2x_release_phy_lock(bp);
+       if (rc) {
+               DP(BNX2X_MSG_ETHTOOL, "Failed reading SFF-8472 comp field\n");
+               return -EINVAL;
+       }
+
+       bnx2x_acquire_phy_lock(bp);
+       rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],
+                                         &bp->link_params,
+                                         I2C_DEV_ADDR_A0,
+                                         SFP_EEPROM_DIAG_TYPE_ADDR,
+                                         SFP_EEPROM_DIAG_TYPE_SIZE,
+                                         &diag_type);
+       bnx2x_release_phy_lock(bp);
+       if (rc) {
+               DP(BNX2X_MSG_ETHTOOL, "Failed reading Diag Type field\n");
+               return -EINVAL;
+       }
+
+       if (!sff8472_comp ||
+           (diag_type & SFP_EEPROM_DIAG_ADDR_CHANGE_REQ)) {
                modinfo->type = ETH_MODULE_SFF_8079;
                modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
-               return 0;
-       default:
-               return -EOPNOTSUPP;
+       } else {
+               modinfo->type = ETH_MODULE_SFF_8472;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
        }
+       return 0;
 }
 
 static int bnx2x_nvram_write_dword(struct bnx2x *bp, u32 offset, u32 val,
@@ -1496,9 +1566,8 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf,
                              int buf_size)
 {
        int rc;
-       u32 cmd_flags;
-       u32 align_offset;
-       __be32 val;
+       u32 cmd_flags, align_offset, val;
+       __be32 val_be;
 
        if (offset + buf_size > bp->common.flash_size) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
@@ -1517,16 +1586,16 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf,
 
        cmd_flags = (MCPR_NVM_COMMAND_FIRST | MCPR_NVM_COMMAND_LAST);
        align_offset = (offset & ~0x03);
-       rc = bnx2x_nvram_read_dword(bp, align_offset, &val, cmd_flags);
+       rc = bnx2x_nvram_read_dword(bp, align_offset, &val_be, cmd_flags);
 
        if (rc == 0) {
-               val &= ~(0xff << BYTE_OFFSET(offset));
-               val |= (*data_buf << BYTE_OFFSET(offset));
-
                /* nvram data is returned as an array of bytes
                 * convert it back to cpu order
                 */
-               val = be32_to_cpu(val);
+               val = be32_to_cpu(val_be);
+
+               val &= ~le32_to_cpu(0xff << BYTE_OFFSET(offset));
+               val |= le32_to_cpu(*data_buf << BYTE_OFFSET(offset));
 
                rc = bnx2x_nvram_write_dword(bp, align_offset, val,
                                             cmd_flags);
@@ -2526,14 +2595,168 @@ static int bnx2x_test_ext_loopback(struct bnx2x *bp)
        return rc;
 }
 
+struct code_entry {
+       u32 sram_start_addr;
+       u32 code_attribute;
+#define CODE_IMAGE_TYPE_MASK                   0xf0800003
+#define CODE_IMAGE_VNTAG_PROFILES_DATA         0xd0000003
+#define CODE_IMAGE_LENGTH_MASK                 0x007ffffc
+#define CODE_IMAGE_TYPE_EXTENDED_DIR           0xe0000000
+       u32 nvm_start_addr;
+};
+
+#define CODE_ENTRY_MAX                 16
+#define CODE_ENTRY_EXTENDED_DIR_IDX    15
+#define MAX_IMAGES_IN_EXTENDED_DIR     64
+#define NVRAM_DIR_OFFSET               0x14
+
+#define EXTENDED_DIR_EXISTS(code)                                        \
+       ((code & CODE_IMAGE_TYPE_MASK) == CODE_IMAGE_TYPE_EXTENDED_DIR && \
+        (code & CODE_IMAGE_LENGTH_MASK) != 0)
+
 #define CRC32_RESIDUAL                 0xdebb20e3
+#define CRC_BUFF_SIZE                  256
+
+static int bnx2x_nvram_crc(struct bnx2x *bp,
+                          int offset,
+                          int size,
+                          u8 *buff)
+{
+       u32 crc = ~0;
+       int rc = 0, done = 0;
+
+       DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
+          "NVRAM CRC from 0x%08x to 0x%08x\n", offset, offset + size);
+
+       while (done < size) {
+               int count = min_t(int, size - done, CRC_BUFF_SIZE);
+
+               rc = bnx2x_nvram_read(bp, offset + done, buff, count);
+
+               if (rc)
+                       return rc;
+
+               crc = crc32_le(crc, buff, count);
+               done += count;
+       }
+
+       if (crc != CRC32_RESIDUAL)
+               rc = -EINVAL;
+
+       return rc;
+}
+
+static int bnx2x_test_nvram_dir(struct bnx2x *bp,
+                               struct code_entry *entry,
+                               u8 *buff)
+{
+       size_t size = entry->code_attribute & CODE_IMAGE_LENGTH_MASK;
+       u32 type = entry->code_attribute & CODE_IMAGE_TYPE_MASK;
+       int rc;
+
+       /* Zero-length images and AFEX profiles do not have CRC */
+       if (size == 0 || type == CODE_IMAGE_VNTAG_PROFILES_DATA)
+               return 0;
+
+       rc = bnx2x_nvram_crc(bp, entry->nvm_start_addr, size, buff);
+       if (rc)
+               DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
+                  "image %x has failed crc test (rc %d)\n", type, rc);
+
+       return rc;
+}
+
+static int bnx2x_test_dir_entry(struct bnx2x *bp, u32 addr, u8 *buff)
+{
+       int rc;
+       struct code_entry entry;
+
+       rc = bnx2x_nvram_read32(bp, addr, (u32 *)&entry, sizeof(entry));
+       if (rc)
+               return rc;
+
+       return bnx2x_test_nvram_dir(bp, &entry, buff);
+}
+
+static int bnx2x_test_nvram_ext_dirs(struct bnx2x *bp, u8 *buff)
+{
+       u32 rc, cnt, dir_offset = NVRAM_DIR_OFFSET;
+       struct code_entry entry;
+       int i;
+
+       rc = bnx2x_nvram_read32(bp,
+                               dir_offset +
+                               sizeof(entry) * CODE_ENTRY_EXTENDED_DIR_IDX,
+                               (u32 *)&entry, sizeof(entry));
+       if (rc)
+               return rc;
+
+       if (!EXTENDED_DIR_EXISTS(entry.code_attribute))
+               return 0;
+
+       rc = bnx2x_nvram_read32(bp, entry.nvm_start_addr,
+                               &cnt, sizeof(u32));
+       if (rc)
+               return rc;
+
+       dir_offset = entry.nvm_start_addr + 8;
+
+       for (i = 0; i < cnt && i < MAX_IMAGES_IN_EXTENDED_DIR; i++) {
+               rc = bnx2x_test_dir_entry(bp, dir_offset +
+                                             sizeof(struct code_entry) * i,
+                                         buff);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
+
+static int bnx2x_test_nvram_dirs(struct bnx2x *bp, u8 *buff)
+{
+       u32 rc, dir_offset = NVRAM_DIR_OFFSET;
+       int i;
+
+       DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "NVRAM DIRS CRC test-set\n");
+
+       for (i = 0; i < CODE_ENTRY_EXTENDED_DIR_IDX; i++) {
+               rc = bnx2x_test_dir_entry(bp, dir_offset +
+                                             sizeof(struct code_entry) * i,
+                                         buff);
+               if (rc)
+                       return rc;
+       }
+
+       return bnx2x_test_nvram_ext_dirs(bp, buff);
+}
+
+struct crc_pair {
+       int offset;
+       int size;
+};
+
+static int bnx2x_test_nvram_tbl(struct bnx2x *bp,
+                               const struct crc_pair *nvram_tbl, u8 *buf)
+{
+       int i;
+
+       for (i = 0; nvram_tbl[i].size; i++) {
+               int rc = bnx2x_nvram_crc(bp, nvram_tbl[i].offset,
+                                        nvram_tbl[i].size, buf);
+               if (rc) {
+                       DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
+                          "nvram_tbl[%d] has failed crc test (rc %d)\n",
+                          i, rc);
+                       return rc;
+               }
+       }
+
+       return 0;
+}
 
 static int bnx2x_test_nvram(struct bnx2x *bp)
 {
-       static const struct {
-               int offset;
-               int size;
-       } nvram_tbl[] = {
+       const struct crc_pair nvram_tbl[] = {
                {     0,  0x14 }, /* bootstrap */
                {  0x14,  0xec }, /* dir */
                { 0x100, 0x350 }, /* manuf_info */
@@ -2542,30 +2765,33 @@ static int bnx2x_test_nvram(struct bnx2x *bp)
                { 0x708,  0x70 }, /* manuf_key_info */
                {     0,     0 }
        };
-       __be32 *buf;
-       u8 *data;
-       int i, rc;
-       u32 magic, crc;
+       const struct crc_pair nvram_tbl2[] = {
+               { 0x7e8, 0x350 }, /* manuf_info2 */
+               { 0xb38,  0xf0 }, /* feature_info */
+               {     0,     0 }
+       };
+
+       u8 *buf;
+       int rc;
+       u32 magic;
 
        if (BP_NOMCP(bp))
                return 0;
 
-       buf = kmalloc(0x350, GFP_KERNEL);
+       buf = kmalloc(CRC_BUFF_SIZE, GFP_KERNEL);
        if (!buf) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "kmalloc failed\n");
                rc = -ENOMEM;
                goto test_nvram_exit;
        }
-       data = (u8 *)buf;
 
-       rc = bnx2x_nvram_read(bp, 0, data, 4);
+       rc = bnx2x_nvram_read32(bp, 0, &magic, sizeof(magic));
        if (rc) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "magic value read (rc %d)\n", rc);
                goto test_nvram_exit;
        }
 
-       magic = be32_to_cpu(buf[0]);
        if (magic != 0x669955aa) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "wrong magic value (0x%08x)\n", magic);
@@ -2573,25 +2799,26 @@ static int bnx2x_test_nvram(struct bnx2x *bp)
                goto test_nvram_exit;
        }
 
-       for (i = 0; nvram_tbl[i].size; i++) {
+       DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "Port 0 CRC test-set\n");
+       rc = bnx2x_test_nvram_tbl(bp, nvram_tbl, buf);
+       if (rc)
+               goto test_nvram_exit;
 
-               rc = bnx2x_nvram_read(bp, nvram_tbl[i].offset, data,
-                                     nvram_tbl[i].size);
-               if (rc) {
-                       DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
-                          "nvram_tbl[%d] read data (rc %d)\n", i, rc);
-                       goto test_nvram_exit;
-               }
+       if (!CHIP_IS_E1x(bp) && !CHIP_IS_57811xx(bp)) {
+               u32 hide = SHMEM_RD(bp, dev_info.shared_hw_config.config2) &
+                          SHARED_HW_CFG_HIDE_PORT1;
 
-               crc = ether_crc_le(nvram_tbl[i].size, data);
-               if (crc != CRC32_RESIDUAL) {
+               if (!hide) {
                        DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
-                          "nvram_tbl[%d] wrong crc value (0x%08x)\n", i, crc);
-                       rc = -ENODEV;
-                       goto test_nvram_exit;
+                          "Port 1 CRC test-set\n");
+                       rc = bnx2x_test_nvram_tbl(bp, nvram_tbl2, buf);
+                       if (rc)
+                               goto test_nvram_exit;
                }
        }
 
+       rc = bnx2x_test_nvram_dirs(bp, buf);
+
 test_nvram_exit:
        kfree(buf);
        return rc;
@@ -2637,9 +2864,16 @@ static void bnx2x_self_test(struct net_device *dev,
 
        memset(buf, 0, sizeof(u64) * BNX2X_NUM_TESTS(bp));
 
+       if (bnx2x_test_nvram(bp) != 0) {
+               if (!IS_MF(bp))
+                       buf[4] = 1;
+               else
+                       buf[0] = 1;
+               etest->flags |= ETH_TEST_FL_FAILED;
+       }
+
        if (!netif_running(dev)) {
-               DP(BNX2X_MSG_ETHTOOL,
-                  "Can't perform self-test when interface is down\n");
+               DP(BNX2X_MSG_ETHTOOL, "Interface is down\n");
                return;
        }
 
@@ -2701,13 +2935,7 @@ static void bnx2x_self_test(struct net_device *dev,
                /* wait until link state is restored */
                bnx2x_wait_for_link(bp, link_up, is_serdes);
        }
-       if (bnx2x_test_nvram(bp) != 0) {
-               if (!IS_MF(bp))
-                       buf[4] = 1;
-               else
-                       buf[0] = 1;
-               etest->flags |= ETH_TEST_FL_FAILED;
-       }
+
        if (bnx2x_test_intr(bp) != 0) {
                if (!IS_MF(bp))
                        buf[5] = 1;
@@ -3232,7 +3460,32 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
        .get_ts_info            = ethtool_op_get_ts_info,
 };
 
-void bnx2x_set_ethtool_ops(struct net_device *netdev)
+static const struct ethtool_ops bnx2x_vf_ethtool_ops = {
+       .get_settings           = bnx2x_get_settings,
+       .set_settings           = bnx2x_set_settings,
+       .get_drvinfo            = bnx2x_get_drvinfo,
+       .get_msglevel           = bnx2x_get_msglevel,
+       .set_msglevel           = bnx2x_set_msglevel,
+       .get_link               = bnx2x_get_link,
+       .get_coalesce           = bnx2x_get_coalesce,
+       .get_ringparam          = bnx2x_get_ringparam,
+       .set_ringparam          = bnx2x_set_ringparam,
+       .get_sset_count         = bnx2x_get_sset_count,
+       .get_strings            = bnx2x_get_strings,
+       .get_ethtool_stats      = bnx2x_get_ethtool_stats,
+       .get_rxnfc              = bnx2x_get_rxnfc,
+       .set_rxnfc              = bnx2x_set_rxnfc,
+       .get_rxfh_indir_size    = bnx2x_get_rxfh_indir_size,
+       .get_rxfh_indir         = bnx2x_get_rxfh_indir,
+       .set_rxfh_indir         = bnx2x_set_rxfh_indir,
+       .get_channels           = bnx2x_get_channels,
+       .set_channels           = bnx2x_set_channels,
+};
+
+void bnx2x_set_ethtool_ops(struct bnx2x *bp, struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &bnx2x_ethtool_ops);
+       if (IS_PF(bp))
+               SET_ETHTOOL_OPS(netdev, &bnx2x_ethtool_ops);
+       else /* vf */
+               SET_ETHTOOL_OPS(netdev, &bnx2x_vf_ethtool_ops);
 }
index e5f808377c910cea0f578c3f3cc33cf4c91a5877..40f22c6794cd666f747099354335cbb11692fefa 100644 (file)
        * IRO[138].m2) + ((sbId) * IRO[138].m3))
 #define CSTORM_IGU_MODE_OFFSET (IRO[157].base)
 #define CSTORM_ISCSI_CQ_SIZE_OFFSET(pfId) \
-       (IRO[316].base + ((pfId) * IRO[316].m1))
-#define CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \
        (IRO[317].base + ((pfId) * IRO[317].m1))
+#define CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \
+       (IRO[318].base + ((pfId) * IRO[318].m1))
 #define CSTORM_ISCSI_EQ_CONS_OFFSET(pfId, iscsiEqId) \
-       (IRO[309].base + ((pfId) * IRO[309].m1) + ((iscsiEqId) * IRO[309].m2))
+       (IRO[310].base + ((pfId) * IRO[310].m1) + ((iscsiEqId) * IRO[310].m2))
 #define CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(pfId, iscsiEqId) \
-       (IRO[311].base + ((pfId) * IRO[311].m1) + ((iscsiEqId) * IRO[311].m2))
+       (IRO[312].base + ((pfId) * IRO[312].m1) + ((iscsiEqId) * IRO[312].m2))
 #define CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(pfId, iscsiEqId) \
-       (IRO[310].base + ((pfId) * IRO[310].m1) + ((iscsiEqId) * IRO[310].m2))
+       (IRO[311].base + ((pfId) * IRO[311].m1) + ((iscsiEqId) * IRO[311].m2))
 #define CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_VALID_OFFSET(pfId, iscsiEqId) \
-       (IRO[312].base + ((pfId) * IRO[312].m1) + ((iscsiEqId) * IRO[312].m2))
+       (IRO[313].base + ((pfId) * IRO[313].m1) + ((iscsiEqId) * IRO[313].m2))
 #define CSTORM_ISCSI_EQ_PROD_OFFSET(pfId, iscsiEqId) \
-       (IRO[308].base + ((pfId) * IRO[308].m1) + ((iscsiEqId) * IRO[308].m2))
+       (IRO[309].base + ((pfId) * IRO[309].m1) + ((iscsiEqId) * IRO[309].m2))
 #define CSTORM_ISCSI_EQ_SB_INDEX_OFFSET(pfId, iscsiEqId) \
-       (IRO[314].base + ((pfId) * IRO[314].m1) + ((iscsiEqId) * IRO[314].m2))
+       (IRO[315].base + ((pfId) * IRO[315].m1) + ((iscsiEqId) * IRO[315].m2))
 #define CSTORM_ISCSI_EQ_SB_NUM_OFFSET(pfId, iscsiEqId) \
-       (IRO[313].base + ((pfId) * IRO[313].m1) + ((iscsiEqId) * IRO[313].m2))
+       (IRO[314].base + ((pfId) * IRO[314].m1) + ((iscsiEqId) * IRO[314].m2))
 #define CSTORM_ISCSI_HQ_SIZE_OFFSET(pfId) \
-       (IRO[315].base + ((pfId) * IRO[315].m1))
+       (IRO[316].base + ((pfId) * IRO[316].m1))
 #define CSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
-       (IRO[307].base + ((pfId) * IRO[307].m1))
+       (IRO[308].base + ((pfId) * IRO[308].m1))
 #define CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
-       (IRO[306].base + ((pfId) * IRO[306].m1))
+       (IRO[307].base + ((pfId) * IRO[307].m1))
 #define CSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
-       (IRO[305].base + ((pfId) * IRO[305].m1))
+       (IRO[306].base + ((pfId) * IRO[306].m1))
 #define CSTORM_RECORD_SLOW_PATH_OFFSET(funcId) \
        (IRO[151].base + ((funcId) * IRO[151].m1))
 #define CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(pfId) \
 #define TSTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \
        (IRO[268].base + ((pfId) * IRO[268].m1))
 #define TSTORM_ISCSI_TCP_LOCAL_ADV_WND_OFFSET(pfId) \
-       (IRO[277].base + ((pfId) * IRO[277].m1))
+       (IRO[278].base + ((pfId) * IRO[278].m1))
 #define TSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(pfId) \
        (IRO[264].base + ((pfId) * IRO[264].m1))
 #define TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(pfId) \
 #define USTORM_ASSERT_LIST_INDEX_OFFSET        (IRO[177].base)
 #define USTORM_ASSERT_LIST_OFFSET(assertListEntry) \
        (IRO[176].base + ((assertListEntry) * IRO[176].m1))
-#define USTORM_CQE_PAGE_NEXT_OFFSET(portId, clientId) \
-       (IRO[205].base + ((portId) * IRO[205].m1) + ((clientId) * \
-       IRO[205].m2))
 #define USTORM_ETH_PAUSE_ENABLED_OFFSET(portId) \
        (IRO[183].base + ((portId) * IRO[183].m1))
 #define USTORM_FCOE_EQ_PROD_OFFSET(pfId) \
-       (IRO[318].base + ((pfId) * IRO[318].m1))
+       (IRO[319].base + ((pfId) * IRO[319].m1))
 #define USTORM_FUNC_EN_OFFSET(funcId) \
        (IRO[178].base + ((funcId) * IRO[178].m1))
 #define USTORM_ISCSI_CQ_SIZE_OFFSET(pfId) \
-       (IRO[282].base + ((pfId) * IRO[282].m1))
-#define USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \
        (IRO[283].base + ((pfId) * IRO[283].m1))
+#define USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(pfId) \
+       (IRO[284].base + ((pfId) * IRO[284].m1))
 #define USTORM_ISCSI_ERROR_BITMAP_OFFSET(pfId) \
-       (IRO[287].base + ((pfId) * IRO[287].m1))
+       (IRO[288].base + ((pfId) * IRO[288].m1))
 #define USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(pfId) \
-       (IRO[284].base + ((pfId) * IRO[284].m1))
+       (IRO[285].base + ((pfId) * IRO[285].m1))
 #define USTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
-       (IRO[280].base + ((pfId) * IRO[280].m1))
+       (IRO[281].base + ((pfId) * IRO[281].m1))
 #define USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
-       (IRO[279].base + ((pfId) * IRO[279].m1))
+       (IRO[280].base + ((pfId) * IRO[280].m1))
 #define USTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
-       (IRO[278].base + ((pfId) * IRO[278].m1))
+       (IRO[279].base + ((pfId) * IRO[279].m1))
 #define USTORM_ISCSI_R2TQ_SIZE_OFFSET(pfId) \
-       (IRO[281].base + ((pfId) * IRO[281].m1))
+       (IRO[282].base + ((pfId) * IRO[282].m1))
 #define USTORM_ISCSI_RQ_BUFFER_SIZE_OFFSET(pfId) \
-       (IRO[285].base + ((pfId) * IRO[285].m1))
-#define USTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \
        (IRO[286].base + ((pfId) * IRO[286].m1))
+#define USTORM_ISCSI_RQ_SIZE_OFFSET(pfId) \
+       (IRO[287].base + ((pfId) * IRO[287].m1))
 #define USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(pfId) \
        (IRO[182].base + ((pfId) * IRO[182].m1))
 #define USTORM_RECORD_SLOW_PATH_OFFSET(funcId) \
 #define XSTORM_FUNC_EN_OFFSET(funcId) \
        (IRO[47].base + ((funcId) * IRO[47].m1))
 #define XSTORM_ISCSI_HQ_SIZE_OFFSET(pfId) \
-       (IRO[295].base + ((pfId) * IRO[295].m1))
+       (IRO[296].base + ((pfId) * IRO[296].m1))
 #define XSTORM_ISCSI_LOCAL_MAC_ADDR0_OFFSET(pfId) \
-       (IRO[298].base + ((pfId) * IRO[298].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR1_OFFSET(pfId) \
        (IRO[299].base + ((pfId) * IRO[299].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR2_OFFSET(pfId) \
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR1_OFFSET(pfId) \
        (IRO[300].base + ((pfId) * IRO[300].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR3_OFFSET(pfId) \
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR2_OFFSET(pfId) \
        (IRO[301].base + ((pfId) * IRO[301].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR4_OFFSET(pfId) \
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR3_OFFSET(pfId) \
        (IRO[302].base + ((pfId) * IRO[302].m1))
-#define XSTORM_ISCSI_LOCAL_MAC_ADDR5_OFFSET(pfId) \
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR4_OFFSET(pfId) \
        (IRO[303].base + ((pfId) * IRO[303].m1))
-#define XSTORM_ISCSI_LOCAL_VLAN_OFFSET(pfId) \
+#define XSTORM_ISCSI_LOCAL_MAC_ADDR5_OFFSET(pfId) \
        (IRO[304].base + ((pfId) * IRO[304].m1))
+#define XSTORM_ISCSI_LOCAL_VLAN_OFFSET(pfId) \
+       (IRO[305].base + ((pfId) * IRO[305].m1))
 #define XSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfId) \
-       (IRO[294].base + ((pfId) * IRO[294].m1))
+       (IRO[295].base + ((pfId) * IRO[295].m1))
 #define XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfId) \
-       (IRO[293].base + ((pfId) * IRO[293].m1))
+       (IRO[294].base + ((pfId) * IRO[294].m1))
 #define XSTORM_ISCSI_PAGE_SIZE_OFFSET(pfId) \
-       (IRO[292].base + ((pfId) * IRO[292].m1))
+       (IRO[293].base + ((pfId) * IRO[293].m1))
 #define XSTORM_ISCSI_R2TQ_SIZE_OFFSET(pfId) \
-       (IRO[297].base + ((pfId) * IRO[297].m1))
+       (IRO[298].base + ((pfId) * IRO[298].m1))
 #define XSTORM_ISCSI_SQ_SIZE_OFFSET(pfId) \
-       (IRO[296].base + ((pfId) * IRO[296].m1))
+       (IRO[297].base + ((pfId) * IRO[297].m1))
 #define XSTORM_ISCSI_TCP_VARS_ADV_WND_SCL_OFFSET(pfId) \
-       (IRO[291].base + ((pfId) * IRO[291].m1))
+       (IRO[292].base + ((pfId) * IRO[292].m1))
 #define XSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(pfId) \
-       (IRO[290].base + ((pfId) * IRO[290].m1))
+       (IRO[291].base + ((pfId) * IRO[291].m1))
 #define XSTORM_ISCSI_TCP_VARS_TOS_OFFSET(pfId) \
-       (IRO[289].base + ((pfId) * IRO[289].m1))
+       (IRO[290].base + ((pfId) * IRO[290].m1))
 #define XSTORM_ISCSI_TCP_VARS_TTL_OFFSET(pfId) \
-       (IRO[288].base + ((pfId) * IRO[288].m1))
+       (IRO[289].base + ((pfId) * IRO[289].m1))
 #define XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(pfId) \
        (IRO[44].base + ((pfId) * IRO[44].m1))
 #define XSTORM_RECORD_SLOW_PATH_OFFSET(funcId) \
index 037860ecc3434cffca9ee555846ae21dbe8600ab..12f00a40cdf0e8c280f404d663378729f9cf3975 100644 (file)
@@ -114,6 +114,10 @@ struct license_key {
 #define EPIO_CFG_EPIO30                     0x0000001f
 #define EPIO_CFG_EPIO31                     0x00000020
 
+struct mac_addr {
+       u32 upper;
+       u32 lower;
+};
 
 struct shared_hw_cfg {                  /* NVRAM Offset */
        /* Up to 16 bytes of NULL-terminated string */
@@ -508,7 +512,22 @@ struct port_hw_cfg {                   /* port 0: 0x12c  port 1: 0x2bc */
        #define PORT_HW_CFG_PAUSE_ON_HOST_RING_DISABLED               0x00000000
        #define PORT_HW_CFG_PAUSE_ON_HOST_RING_ENABLED                0x00000001
 
-       u32 reserved0[6];                                   /* 0x178 */
+       /* SFP+ Tx Equalization: NIC recommended and tested value is 0xBEB2
+        * LOM recommended and tested value is 0xBEB2. Using a different
+        * value means using a value not tested by BRCM
+        */
+       u32 sfi_tap_values;                                 /* 0x178 */
+       #define PORT_HW_CFG_TX_EQUALIZATION_MASK                      0x0000FFFF
+       #define PORT_HW_CFG_TX_EQUALIZATION_SHIFT                     0
+
+       /* SFP+ Tx driver broadcast IDRIVER: NIC recommended and tested
+        * value is 0x2. LOM recommended and tested value is 0x2. Using a
+        * different value means using a value not tested by BRCM
+        */
+       #define PORT_HW_CFG_TX_DRV_BROADCAST_MASK                     0x000F0000
+       #define PORT_HW_CFG_TX_DRV_BROADCAST_SHIFT                    16
+
+       u32 reserved0[5];                                   /* 0x17c */
 
        u32 aeu_int_mask;                                   /* 0x190 */
 
@@ -2821,8 +2840,8 @@ struct afex_stats {
 
 #define BCM_5710_FW_MAJOR_VERSION                      7
 #define BCM_5710_FW_MINOR_VERSION                      8
-#define BCM_5710_FW_REVISION_VERSION           2
-#define BCM_5710_FW_ENGINEERING_VERSION                        0
+#define BCM_5710_FW_REVISION_VERSION           17
+#define BCM_5710_FW_ENGINEERING_VERSION                0
 #define BCM_5710_FW_COMPILE_FLAGS                      1
 
 
@@ -3513,11 +3532,14 @@ struct client_init_tx_data {
 #define CLIENT_INIT_TX_DATA_BCAST_ACCEPT_ALL_SHIFT 2
 #define CLIENT_INIT_TX_DATA_ACCEPT_ANY_VLAN (0x1<<3)
 #define CLIENT_INIT_TX_DATA_ACCEPT_ANY_VLAN_SHIFT 3
-#define CLIENT_INIT_TX_DATA_RESERVED1 (0xFFF<<4)
-#define CLIENT_INIT_TX_DATA_RESERVED1_SHIFT 4
+#define CLIENT_INIT_TX_DATA_RESERVED0 (0xFFF<<4)
+#define CLIENT_INIT_TX_DATA_RESERVED0_SHIFT 4
        u8 default_vlan_flg;
        u8 force_default_pri_flg;
-       __le32 reserved3;
+       u8 tunnel_lso_inc_ip_id;
+       u8 refuse_outband_vlan_flg;
+       u8 tunnel_non_lso_pcsum_location;
+       u8 reserved1;
 };
 
 /*
@@ -3551,6 +3573,11 @@ struct client_update_ramrod_data {
        __le16 silent_vlan_mask;
        u8 silent_vlan_removal_flg;
        u8 silent_vlan_change_flg;
+       u8 refuse_outband_vlan_flg;
+       u8 refuse_outband_vlan_change_flg;
+       u8 tx_switching_flg;
+       u8 tx_switching_change_flg;
+       __le32 reserved1;
        __le32 echo;
 };
 
@@ -3620,7 +3647,8 @@ struct eth_classify_header {
  */
 struct eth_classify_mac_cmd {
        struct eth_classify_cmd_header header;
-       __le32 reserved0;
+       __le16 reserved0;
+       __le16 inner_mac;
        __le16 mac_lsb;
        __le16 mac_mid;
        __le16 mac_msb;
@@ -3633,7 +3661,8 @@ struct eth_classify_mac_cmd {
  */
 struct eth_classify_pair_cmd {
        struct eth_classify_cmd_header header;
-       __le32 reserved0;
+       __le16 reserved0;
+       __le16 inner_mac;
        __le16 mac_lsb;
        __le16 mac_mid;
        __le16 mac_msb;
@@ -3855,8 +3884,68 @@ struct eth_halt_ramrod_data {
 
 
 /*
- * Command for setting multicast classification for a client
+ * destination and source mac address.
+ */
+struct eth_mac_addresses {
+#if defined(__BIG_ENDIAN)
+       __le16 dst_mid;
+       __le16 dst_lo;
+#elif defined(__LITTLE_ENDIAN)
+       __le16 dst_lo;
+       __le16 dst_mid;
+#endif
+#if defined(__BIG_ENDIAN)
+       __le16 src_lo;
+       __le16 dst_hi;
+#elif defined(__LITTLE_ENDIAN)
+       __le16 dst_hi;
+       __le16 src_lo;
+#endif
+#if defined(__BIG_ENDIAN)
+       __le16 src_hi;
+       __le16 src_mid;
+#elif defined(__LITTLE_ENDIAN)
+       __le16 src_mid;
+       __le16 src_hi;
+#endif
+};
+
+/* tunneling related data */
+struct eth_tunnel_data {
+#if defined(__BIG_ENDIAN)
+       __le16 dst_mid;
+       __le16 dst_lo;
+#elif defined(__LITTLE_ENDIAN)
+       __le16 dst_lo;
+       __le16 dst_mid;
+#endif
+#if defined(__BIG_ENDIAN)
+       __le16 reserved0;
+       __le16 dst_hi;
+#elif defined(__LITTLE_ENDIAN)
+       __le16 dst_hi;
+       __le16 reserved0;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 reserved1;
+       u8 ip_hdr_start_inner_w;
+       __le16 pseudo_csum;
+#elif defined(__LITTLE_ENDIAN)
+       __le16 pseudo_csum;
+       u8 ip_hdr_start_inner_w;
+       u8 reserved1;
+#endif
+};
+
+/* union for mac addresses and for tunneling data.
+ * considered as tunneling data only if (tunnel_exist == 1).
  */
+union eth_mac_addr_or_tunnel_data {
+       struct eth_mac_addresses mac_addr;
+       struct eth_tunnel_data tunnel_data;
+};
+
+/*Command for setting multicast classification for a client */
 struct eth_multicast_rules_cmd {
        u8 cmd_general_data;
 #define ETH_MULTICAST_RULES_CMD_RX_CMD (0x1<<0)
@@ -3874,7 +3963,6 @@ struct eth_multicast_rules_cmd {
        struct regpair reserved3;
 };
 
-
 /*
  * parameters for multicast classification ramrod
  */
@@ -3883,7 +3971,6 @@ struct eth_multicast_rules_ramrod_data {
        struct eth_multicast_rules_cmd rules[MULTICAST_RULES_COUNT];
 };
 
-
 /*
  * Place holder for ramrods protocol specific data
  */
@@ -3947,11 +4034,14 @@ struct eth_rss_update_ramrod_data {
 #define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_TCP_CAPABILITY_SHIFT 4
 #define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_UDP_CAPABILITY (0x1<<5)
 #define ETH_RSS_UPDATE_RAMROD_DATA_IPV6_UDP_CAPABILITY_SHIFT 5
+#define ETH_RSS_UPDATE_RAMROD_DATA_EN_5_TUPLE_CAPABILITY (0x1<<6)
+#define ETH_RSS_UPDATE_RAMROD_DATA_EN_5_TUPLE_CAPABILITY_SHIFT 6
 #define ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY (0x1<<7)
 #define ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY_SHIFT 7
        u8 rss_result_mask;
        u8 rss_mode;
-       __le32 __reserved2;
+       __le16 udp_4tuple_dst_port_mask;
+       __le16 udp_4tuple_dst_port_value;
        u8 indirection_table[T_ETH_INDIRECTION_TABLE_SIZE];
        __le32 rss_key[T_ETH_RSS_KEY];
        __le32 echo;
@@ -4115,6 +4205,23 @@ enum eth_tpa_update_command {
        MAX_ETH_TPA_UPDATE_COMMAND
 };
 
+/* In case of LSO over IPv4 tunnel, whether to increment
+ * IP ID on external IP header or internal IP header
+ */
+enum eth_tunnel_lso_inc_ip_id {
+       EXT_HEADER,
+       INT_HEADER,
+       MAX_ETH_TUNNEL_LSO_INC_IP_ID
+};
+
+/* In case tunnel exist and L4 checksum offload,
+ * the pseudo checksum location, on packet or on BD.
+ */
+enum eth_tunnel_non_lso_pcsum_location {
+       PCSUM_ON_PKT,
+       PCSUM_ON_BD,
+       MAX_ETH_TUNNEL_NON_LSO_PCSUM_LOCATION
+};
 
 /*
  * Tx regular BD structure
@@ -4166,8 +4273,8 @@ struct eth_tx_start_bd {
 #define ETH_TX_START_BD_FORCE_VLAN_MODE_SHIFT 4
 #define ETH_TX_START_BD_PARSE_NBDS (0x3<<5)
 #define ETH_TX_START_BD_PARSE_NBDS_SHIFT 5
-#define ETH_TX_START_BD_RESREVED (0x1<<7)
-#define ETH_TX_START_BD_RESREVED_SHIFT 7
+#define ETH_TX_START_BD_TUNNEL_EXIST (0x1<<7)
+#define ETH_TX_START_BD_TUNNEL_EXIST_SHIFT 7
 };
 
 /*
@@ -4216,15 +4323,10 @@ struct eth_tx_parse_bd_e1x {
  * Tx parsing BD structure for ETH E2
  */
 struct eth_tx_parse_bd_e2 {
-       __le16 dst_mac_addr_lo;
-       __le16 dst_mac_addr_mid;
-       __le16 dst_mac_addr_hi;
-       __le16 src_mac_addr_lo;
-       __le16 src_mac_addr_mid;
-       __le16 src_mac_addr_hi;
+       union eth_mac_addr_or_tunnel_data data;
        __le32 parsing_data;
-#define ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W (0x7FF<<0)
-#define ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W_SHIFT 0
+#define ETH_TX_PARSE_BD_E2_L4_HDR_START_OFFSET_W (0x7FF<<0)
+#define ETH_TX_PARSE_BD_E2_L4_HDR_START_OFFSET_W_SHIFT 0
 #define ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW (0xF<<11)
 #define ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW_SHIFT 11
 #define ETH_TX_PARSE_BD_E2_IPV6_WITH_EXT_HDR (0x1<<15)
@@ -4236,8 +4338,51 @@ struct eth_tx_parse_bd_e2 {
 };
 
 /*
- * The last BD in the BD memory will hold a pointer to the next BD memory
+ * Tx 2nd parsing BD structure for ETH packet
  */
+struct eth_tx_parse_2nd_bd {
+       __le16 global_data;
+#define ETH_TX_PARSE_2ND_BD_IP_HDR_START_OUTER_W (0xF<<0)
+#define ETH_TX_PARSE_2ND_BD_IP_HDR_START_OUTER_W_SHIFT 0
+#define ETH_TX_PARSE_2ND_BD_IP_HDR_TYPE_OUTER (0x1<<4)
+#define ETH_TX_PARSE_2ND_BD_IP_HDR_TYPE_OUTER_SHIFT 4
+#define ETH_TX_PARSE_2ND_BD_LLC_SNAP_EN (0x1<<5)
+#define ETH_TX_PARSE_2ND_BD_LLC_SNAP_EN_SHIFT 5
+#define ETH_TX_PARSE_2ND_BD_NS_FLG (0x1<<6)
+#define ETH_TX_PARSE_2ND_BD_NS_FLG_SHIFT 6
+#define ETH_TX_PARSE_2ND_BD_TUNNEL_UDP_EXIST (0x1<<7)
+#define ETH_TX_PARSE_2ND_BD_TUNNEL_UDP_EXIST_SHIFT 7
+#define ETH_TX_PARSE_2ND_BD_IP_HDR_LEN_OUTER_W (0x1F<<8)
+#define ETH_TX_PARSE_2ND_BD_IP_HDR_LEN_OUTER_W_SHIFT 8
+#define ETH_TX_PARSE_2ND_BD_RESERVED0 (0x7<<13)
+#define ETH_TX_PARSE_2ND_BD_RESERVED0_SHIFT 13
+       __le16 reserved1;
+       u8 tcp_flags;
+#define ETH_TX_PARSE_2ND_BD_FIN_FLG (0x1<<0)
+#define ETH_TX_PARSE_2ND_BD_FIN_FLG_SHIFT 0
+#define ETH_TX_PARSE_2ND_BD_SYN_FLG (0x1<<1)
+#define ETH_TX_PARSE_2ND_BD_SYN_FLG_SHIFT 1
+#define ETH_TX_PARSE_2ND_BD_RST_FLG (0x1<<2)
+#define ETH_TX_PARSE_2ND_BD_RST_FLG_SHIFT 2
+#define ETH_TX_PARSE_2ND_BD_PSH_FLG (0x1<<3)
+#define ETH_TX_PARSE_2ND_BD_PSH_FLG_SHIFT 3
+#define ETH_TX_PARSE_2ND_BD_ACK_FLG (0x1<<4)
+#define ETH_TX_PARSE_2ND_BD_ACK_FLG_SHIFT 4
+#define ETH_TX_PARSE_2ND_BD_URG_FLG (0x1<<5)
+#define ETH_TX_PARSE_2ND_BD_URG_FLG_SHIFT 5
+#define ETH_TX_PARSE_2ND_BD_ECE_FLG (0x1<<6)
+#define ETH_TX_PARSE_2ND_BD_ECE_FLG_SHIFT 6
+#define ETH_TX_PARSE_2ND_BD_CWR_FLG (0x1<<7)
+#define ETH_TX_PARSE_2ND_BD_CWR_FLG_SHIFT 7
+       u8 reserved2;
+       u8 tunnel_udp_hdr_start_w;
+       u8 fw_ip_hdr_to_payload_w;
+       __le16 fw_ip_csum_wo_len_flags_frag;
+       __le16 hw_ip_id;
+       __le32 tcp_send_seq;
+};
+
+/* The last BD in the BD memory will hold a pointer to the next BD memory */
 struct eth_tx_next_bd {
        __le32 addr_lo;
        __le32 addr_hi;
@@ -4252,6 +4397,7 @@ union eth_tx_bd_types {
        struct eth_tx_bd reg_bd;
        struct eth_tx_parse_bd_e1x parse_bd_e1x;
        struct eth_tx_parse_bd_e2 parse_bd_e2;
+       struct eth_tx_parse_2nd_bd parse_2nd_bd;
        struct eth_tx_next_bd next_bd;
 };
 
@@ -4663,10 +4809,10 @@ enum common_spqe_cmd_id {
        RAMROD_CMD_ID_COMMON_STOP_TRAFFIC,
        RAMROD_CMD_ID_COMMON_START_TRAFFIC,
        RAMROD_CMD_ID_COMMON_AFEX_VIF_LISTS,
+       RAMROD_CMD_ID_COMMON_SET_TIMESYNC,
        MAX_COMMON_SPQE_CMD_ID
 };
 
-
 /*
  * Per-protocol connection types
  */
@@ -4863,7 +5009,7 @@ struct vf_flr_event_data {
  */
 struct malicious_vf_event_data {
        u8 vf_id;
-       u8 reserved0;
+       u8 err_id;
        u16 reserved1;
        u32 reserved2;
        u32 reserved3;
@@ -4969,10 +5115,10 @@ enum event_ring_opcode {
        EVENT_RING_OPCODE_CLASSIFICATION_RULES,
        EVENT_RING_OPCODE_FILTERS_RULES,
        EVENT_RING_OPCODE_MULTICAST_RULES,
+       EVENT_RING_OPCODE_SET_TIMESYNC,
        MAX_EVENT_RING_OPCODE
 };
 
-
 /*
  * Modes for fairness algorithm
  */
@@ -5010,14 +5156,18 @@ struct flow_control_configuration {
  */
 struct function_start_data {
        u8 function_mode;
-       u8 reserved;
+       u8 allow_npar_tx_switching;
        __le16 sd_vlan_tag;
        __le16 vif_id;
        u8 path_id;
        u8 network_cos_mode;
+       u8 dmae_cmd_id;
+       u8 gre_tunnel_mode;
+       u8 gre_tunnel_rss;
+       u8 nvgre_clss_en;
+       __le16 reserved1[2];
 };
 
-
 struct function_update_data {
        u8 vif_id_change_flg;
        u8 afex_default_vlan_change_flg;
@@ -5027,14 +5177,19 @@ struct function_update_data {
        __le16 afex_default_vlan;
        u8 allowed_priorities;
        u8 network_cos_mode;
+       u8 lb_mode_en_change_flg;
        u8 lb_mode_en;
        u8 tx_switch_suspend_change_flg;
        u8 tx_switch_suspend;
        u8 echo;
-       __le16 reserved1;
+       u8 reserved1;
+       u8 update_gre_cfg_flg;
+       u8 gre_tunnel_mode;
+       u8 gre_tunnel_rss;
+       u8 nvgre_clss_en;
+       u32 reserved3;
 };
 
-
 /*
  * FW version stored in the Xstorm RAM
  */
@@ -5061,6 +5216,22 @@ struct fw_version {
 #define __FW_VERSION_RESERVED_SHIFT 4
 };
 
+/* GRE RSS Mode */
+enum gre_rss_mode {
+       GRE_OUTER_HEADERS_RSS,
+       GRE_INNER_HEADERS_RSS,
+       NVGRE_KEY_ENTROPY_RSS,
+       MAX_GRE_RSS_MODE
+};
+
+/* GRE Tunnel Mode */
+enum gre_tunnel_type {
+       NO_GRE_TUNNEL,
+       NVGRE_TUNNEL,
+       L2GRE_TUNNEL,
+       IPGRE_TUNNEL,
+       MAX_GRE_TUNNEL_TYPE
+};
 
 /*
  * Dynamic Host-Coalescing - Driver(host) counters
@@ -5224,6 +5395,26 @@ enum ip_ver {
        MAX_IP_VER
 };
 
+/*
+ * Malicious VF error ID
+ */
+enum malicious_vf_error_id {
+       VF_PF_CHANNEL_NOT_READY,
+       ETH_ILLEGAL_BD_LENGTHS,
+       ETH_PACKET_TOO_SHORT,
+       ETH_PAYLOAD_TOO_BIG,
+       ETH_ILLEGAL_ETH_TYPE,
+       ETH_ILLEGAL_LSO_HDR_LEN,
+       ETH_TOO_MANY_BDS,
+       ETH_ZERO_HDR_NBDS,
+       ETH_START_BD_NOT_SET,
+       ETH_ILLEGAL_PARSE_NBDS,
+       ETH_IPV6_AND_CHECKSUM,
+       ETH_VLAN_FLG_INCORRECT,
+       ETH_ILLEGAL_LSO_MSS,
+       ETH_TUNNEL_NOT_SUPPORTED,
+       MAX_MALICIOUS_VF_ERROR_ID
+};
 
 /*
  * Multi-function modes
@@ -5368,7 +5559,6 @@ struct protocol_common_spe {
        union protocol_common_specific_data data;
 };
 
-
 /*
  * The send queue element
  */
index 0283f343b0d1efd82be8f7fcb8b392823d0111e0..40f58d73de78ae297f38fe7397d3cc35eaf7a28e 100644 (file)
 #include "bnx2x.h"
 #include "bnx2x_cmn.h"
 
+typedef int (*read_sfp_module_eeprom_func_p)(struct bnx2x_phy *phy,
+                                            struct link_params *params,
+                                            u8 dev_addr, u16 addr, u8 byte_cnt,
+                                            u8 *o_buf, u8);
 /********************************************************/
 #define ETH_HLEN                       14
 /* L2 header size + 2*VLANs (8 bytes) + LLC SNAP (8 bytes) */
 #define SFP_EEPROM_CON_TYPE_ADDR               0x2
        #define SFP_EEPROM_CON_TYPE_VAL_LC      0x7
        #define SFP_EEPROM_CON_TYPE_VAL_COPPER  0x21
+       #define SFP_EEPROM_CON_TYPE_VAL_RJ45    0x22
 
 
 #define SFP_EEPROM_COMP_CODE_ADDR              0x3
@@ -3127,11 +3132,6 @@ static int bnx2x_bsc_read(struct link_params *params,
        int rc = 0;
        struct bnx2x *bp = params->bp;
 
-       if ((sl_devid != 0xa0) && (sl_devid != 0xa2)) {
-               DP(NETIF_MSG_LINK, "invalid sl_devid 0x%x\n", sl_devid);
-               return -EINVAL;
-       }
-
        if (xfer_cnt > 16) {
                DP(NETIF_MSG_LINK, "invalid xfer_cnt %d. Max is 16 bytes\n",
                                        xfer_cnt);
@@ -3629,6 +3629,16 @@ static u8 bnx2x_ext_phy_resolve_fc(struct bnx2x_phy *phy,
  * init configuration, and set/clear SGMII flag. Internal
  * phy init is done purely in phy_init stage.
  */
+#define WC_TX_DRIVER(post2, idriver, ipre) \
+       ((post2 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) | \
+        (idriver << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) | \
+        (ipre << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET))
+
+#define WC_TX_FIR(post, main, pre) \
+       ((post << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) | \
+        (main << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) | \
+        (pre << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET))
+
 static void bnx2x_warpcore_enable_AN_KR2(struct bnx2x_phy *phy,
                                         struct link_params *params,
                                         struct link_vars *vars)
@@ -3728,7 +3738,7 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
        if (((vars->line_speed == SPEED_AUTO_NEG) &&
             (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) ||
            (vars->line_speed == SPEED_1000)) {
-               u32 addr = MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2;
+               u16 addr = MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2;
                an_adv |= (1<<5);
 
                /* Enable CL37 1G Parallel Detect */
@@ -3753,20 +3763,13 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
        /* Set Transmit PMD settings */
        lane = bnx2x_get_warpcore_lane(phy, params);
        bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
-                     MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane,
-                    ((0x02 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
-                     (0x06 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
-                     (0x09 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET)));
+                        MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane,
+                        WC_TX_DRIVER(0x02, 0x06, 0x09));
        /* Configure the next lane if dual mode */
        if (phy->flags & FLAGS_WC_DUAL_MODE)
                bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
                                 MDIO_WC_REG_TX0_TX_DRIVER + 0x10*(lane+1),
-                                ((0x02 <<
-                                MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
-                                 (0x06 <<
-                                  MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
-                                 (0x09 <<
-                               MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET)));
+                                WC_TX_DRIVER(0x02, 0x06, 0x09));
        bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
                         MDIO_WC_REG_CL72_USERB0_CL72_OS_DEF_CTRL,
                         0x03f0);
@@ -3909,6 +3912,8 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy,
 {
        struct bnx2x *bp = params->bp;
        u16 misc1_val, tap_val, tx_driver_val, lane, val;
+       u32 cfg_tap_val, tx_drv_brdct, tx_equal;
+
        /* Hold rxSeqStart */
        bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD,
                                 MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, 0x8000);
@@ -3952,23 +3957,33 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy,
 
        if (is_xfi) {
                misc1_val |= 0x5;
-               tap_val = ((0x08 << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) |
-                          (0x37 << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) |
-                          (0x00 << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET));
-               tx_driver_val =
-                     ((0x00 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
-                      (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
-                      (0x03 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET));
-
+               tap_val = WC_TX_FIR(0x08, 0x37, 0x00);
+               tx_driver_val = WC_TX_DRIVER(0x00, 0x02, 0x03);
        } else {
+               cfg_tap_val = REG_RD(bp, params->shmem_base +
+                                    offsetof(struct shmem_region, dev_info.
+                                             port_hw_config[params->port].
+                                             sfi_tap_values));
+
+               tx_equal = cfg_tap_val & PORT_HW_CFG_TX_EQUALIZATION_MASK;
+
+               tx_drv_brdct = (cfg_tap_val &
+                               PORT_HW_CFG_TX_DRV_BROADCAST_MASK) >>
+                              PORT_HW_CFG_TX_DRV_BROADCAST_SHIFT;
+
                misc1_val |= 0x9;
-               tap_val = ((0x0f << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) |
-                          (0x2b << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) |
-                          (0x02 << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET));
-               tx_driver_val =
-                     ((0x03 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
-                      (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
-                      (0x06 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET));
+
+               /* TAP values are controlled by nvram, if value there isn't 0 */
+               if (tx_equal)
+                       tap_val = (u16)tx_equal;
+               else
+                       tap_val = WC_TX_FIR(0x0f, 0x2b, 0x02);
+
+               if (tx_drv_brdct)
+                       tx_driver_val = WC_TX_DRIVER(0x03, (u16)tx_drv_brdct,
+                                                    0x06);
+               else
+                       tx_driver_val = WC_TX_DRIVER(0x03, 0x02, 0x06);
        }
        bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
                         MDIO_WC_REG_SERDESDIGITAL_MISC1, misc1_val);
@@ -4105,15 +4120,11 @@ static void bnx2x_warpcore_set_20G_DXGXS(struct bnx2x *bp,
        /* Set Transmit PMD settings */
        bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
                         MDIO_WC_REG_TX_FIR_TAP,
-                       ((0x12 << MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET) |
-                        (0x2d << MDIO_WC_REG_TX_FIR_TAP_MAIN_TAP_OFFSET) |
-                        (0x00 << MDIO_WC_REG_TX_FIR_TAP_PRE_TAP_OFFSET) |
-                        MDIO_WC_REG_TX_FIR_TAP_ENABLE));
+                        (WC_TX_FIR(0x12, 0x2d, 0x00) |
+                         MDIO_WC_REG_TX_FIR_TAP_ENABLE));
        bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
-                     MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane,
-                    ((0x02 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) |
-                     (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) |
-                     (0x02 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET)));
+                        MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane,
+                        WC_TX_DRIVER(0x02, 0x02, 0x02));
 }
 
 static void bnx2x_warpcore_set_sgmii_speed(struct bnx2x_phy *phy,
@@ -4750,8 +4761,8 @@ void bnx2x_link_status_update(struct link_params *params,
                                            port_mb[port].link_status));
 
        /* Force link UP in non LOOPBACK_EXT loopback mode(s) */
-       if (bp->link_params.loopback_mode != LOOPBACK_NONE &&
-           bp->link_params.loopback_mode != LOOPBACK_EXT)
+       if (params->loopback_mode != LOOPBACK_NONE &&
+           params->loopback_mode != LOOPBACK_EXT)
                vars->link_status |= LINK_STATUS_LINK_UP;
 
        if (bnx2x_eee_has_cap(params))
@@ -7758,7 +7769,8 @@ static void bnx2x_sfp_set_transmitter(struct link_params *params,
 
 static int bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy,
                                             struct link_params *params,
-                                            u16 addr, u8 byte_cnt, u8 *o_buf)
+                                            u8 dev_addr, u16 addr, u8 byte_cnt,
+                                            u8 *o_buf, u8 is_init)
 {
        struct bnx2x *bp = params->bp;
        u16 val = 0;
@@ -7771,7 +7783,7 @@ static int bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy,
        /* Set the read command byte count */
        bnx2x_cl45_write(bp, phy,
                         MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_BYTE_CNT,
-                        (byte_cnt | 0xa000));
+                        (byte_cnt | (dev_addr << 8)));
 
        /* Set the read command address */
        bnx2x_cl45_write(bp, phy,
@@ -7845,6 +7857,7 @@ static void bnx2x_warpcore_power_module(struct link_params *params,
 }
 static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy,
                                                 struct link_params *params,
+                                                u8 dev_addr,
                                                 u16 addr, u8 byte_cnt,
                                                 u8 *o_buf, u8 is_init)
 {
@@ -7869,7 +7882,7 @@ static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy,
                        usleep_range(1000, 2000);
                        bnx2x_warpcore_power_module(params, 1);
                }
-               rc = bnx2x_bsc_read(params, phy, 0xa0, addr32, 0, byte_cnt,
+               rc = bnx2x_bsc_read(params, phy, dev_addr, addr32, 0, byte_cnt,
                                    data_array);
        } while ((rc != 0) && (++cnt < I2C_WA_RETRY_CNT));
 
@@ -7885,7 +7898,8 @@ static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy,
 
 static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy,
                                             struct link_params *params,
-                                            u16 addr, u8 byte_cnt, u8 *o_buf)
+                                            u8 dev_addr, u16 addr, u8 byte_cnt,
+                                            u8 *o_buf, u8 is_init)
 {
        struct bnx2x *bp = params->bp;
        u16 val, i;
@@ -7896,6 +7910,15 @@ static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy,
                return -EINVAL;
        }
 
+       /* Set 2-wire transfer rate of SFP+ module EEPROM
+        * to 100Khz since some DACs(direct attached cables) do
+        * not work at 400Khz.
+        */
+       bnx2x_cl45_write(bp, phy,
+                        MDIO_PMA_DEVAD,
+                        MDIO_PMA_REG_8727_TWO_WIRE_SLAVE_ADDR,
+                        ((dev_addr << 8) | 1));
+
        /* Need to read from 1.8000 to clear it */
        bnx2x_cl45_read(bp, phy,
                        MDIO_PMA_DEVAD,
@@ -7968,26 +7991,44 @@ static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy,
 
        return -EINVAL;
 }
-
 int bnx2x_read_sfp_module_eeprom(struct bnx2x_phy *phy,
-                                struct link_params *params, u16 addr,
-                                u8 byte_cnt, u8 *o_buf)
+                                struct link_params *params, u8 dev_addr,
+                                u16 addr, u16 byte_cnt, u8 *o_buf)
 {
-       int rc = -EOPNOTSUPP;
+       int rc = 0;
+       struct bnx2x *bp = params->bp;
+       u8 xfer_size;
+       u8 *user_data = o_buf;
+       read_sfp_module_eeprom_func_p read_func;
+
+       if ((dev_addr != 0xa0) && (dev_addr != 0xa2)) {
+               DP(NETIF_MSG_LINK, "invalid dev_addr 0x%x\n", dev_addr);
+               return -EINVAL;
+       }
+
        switch (phy->type) {
        case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726:
-               rc = bnx2x_8726_read_sfp_module_eeprom(phy, params, addr,
-                                                      byte_cnt, o_buf);
-       break;
+               read_func = bnx2x_8726_read_sfp_module_eeprom;
+               break;
        case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727:
        case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722:
-               rc = bnx2x_8727_read_sfp_module_eeprom(phy, params, addr,
-                                                      byte_cnt, o_buf);
-       break;
+               read_func = bnx2x_8727_read_sfp_module_eeprom;
+               break;
        case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
-               rc = bnx2x_warpcore_read_sfp_module_eeprom(phy, params, addr,
-                                                          byte_cnt, o_buf, 0);
-       break;
+               read_func = bnx2x_warpcore_read_sfp_module_eeprom;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       while (!rc && (byte_cnt > 0)) {
+               xfer_size = (byte_cnt > SFP_EEPROM_PAGE_SIZE) ?
+                       SFP_EEPROM_PAGE_SIZE : byte_cnt;
+               rc = read_func(phy, params, dev_addr, addr, xfer_size,
+                              user_data, 0);
+               byte_cnt -= xfer_size;
+               user_data += xfer_size;
+               addr += xfer_size;
        }
        return rc;
 }
@@ -8004,6 +8045,7 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy,
        /* First check for copper cable */
        if (bnx2x_read_sfp_module_eeprom(phy,
                                         params,
+                                        I2C_DEV_ADDR_A0,
                                         SFP_EEPROM_CON_TYPE_ADDR,
                                         2,
                                         (u8 *)val) != 0) {
@@ -8021,6 +8063,7 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy,
                 */
                if (bnx2x_read_sfp_module_eeprom(phy,
                                               params,
+                                              I2C_DEV_ADDR_A0,
                                               SFP_EEPROM_FC_TX_TECH_ADDR,
                                               1,
                                               &copper_module_type) != 0) {
@@ -8049,20 +8092,24 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy,
                break;
        }
        case SFP_EEPROM_CON_TYPE_VAL_LC:
+       case SFP_EEPROM_CON_TYPE_VAL_RJ45:
                check_limiting_mode = 1;
                if ((val[1] & (SFP_EEPROM_COMP_CODE_SR_MASK |
                               SFP_EEPROM_COMP_CODE_LR_MASK |
                               SFP_EEPROM_COMP_CODE_LRM_MASK)) == 0) {
-                       DP(NETIF_MSG_LINK, "1G Optic module detected\n");
+                       DP(NETIF_MSG_LINK, "1G SFP module detected\n");
                        gport = params->port;
                        phy->media_type = ETH_PHY_SFP_1G_FIBER;
-                       phy->req_line_speed = SPEED_1000;
-                       if (!CHIP_IS_E1x(bp))
-                               gport = BP_PATH(bp) + (params->port << 1);
-                       netdev_err(bp->dev, "Warning: Link speed was forced to 1000Mbps."
-                             " Current SFP module in port %d is not"
-                             " compliant with 10G Ethernet\n",
-                        gport);
+                       if (phy->req_line_speed != SPEED_1000) {
+                               phy->req_line_speed = SPEED_1000;
+                               if (!CHIP_IS_E1x(bp)) {
+                                       gport = BP_PATH(bp) +
+                                       (params->port << 1);
+                               }
+                               netdev_err(bp->dev,
+                                          "Warning: Link speed was forced to 1000Mbps. Current SFP module in port %d is not compliant with 10G Ethernet\n",
+                                          gport);
+                       }
                } else {
                        int idx, cfg_idx = 0;
                        DP(NETIF_MSG_LINK, "10G Optic module detected\n");
@@ -8101,6 +8148,7 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy,
                u8 options[SFP_EEPROM_OPTIONS_SIZE];
                if (bnx2x_read_sfp_module_eeprom(phy,
                                                 params,
+                                                I2C_DEV_ADDR_A0,
                                                 SFP_EEPROM_OPTIONS_ADDR,
                                                 SFP_EEPROM_OPTIONS_SIZE,
                                                 options) != 0) {
@@ -8167,6 +8215,7 @@ static int bnx2x_verify_sfp_module(struct bnx2x_phy *phy,
        /* Format the warning message */
        if (bnx2x_read_sfp_module_eeprom(phy,
                                         params,
+                                        I2C_DEV_ADDR_A0,
                                         SFP_EEPROM_VENDOR_NAME_ADDR,
                                         SFP_EEPROM_VENDOR_NAME_SIZE,
                                         (u8 *)vendor_name))
@@ -8175,6 +8224,7 @@ static int bnx2x_verify_sfp_module(struct bnx2x_phy *phy,
                vendor_name[SFP_EEPROM_VENDOR_NAME_SIZE] = '\0';
        if (bnx2x_read_sfp_module_eeprom(phy,
                                         params,
+                                        I2C_DEV_ADDR_A0,
                                         SFP_EEPROM_PART_NO_ADDR,
                                         SFP_EEPROM_PART_NO_SIZE,
                                         (u8 *)vendor_pn))
@@ -8205,12 +8255,13 @@ static int bnx2x_wait_for_sfp_module_initialized(struct bnx2x_phy *phy,
 
        for (timeout = 0; timeout < 60; timeout++) {
                if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)
-                       rc = bnx2x_warpcore_read_sfp_module_eeprom(phy,
-                                                                  params, 1,
-                                                                  1, &val, 1);
+                       rc = bnx2x_warpcore_read_sfp_module_eeprom(
+                               phy, params, I2C_DEV_ADDR_A0, 1, 1, &val,
+                               1);
                else
-                       rc = bnx2x_read_sfp_module_eeprom(phy, params, 1, 1,
-                                                         &val);
+                       rc = bnx2x_read_sfp_module_eeprom(phy, params,
+                                                         I2C_DEV_ADDR_A0,
+                                                         1, 1, &val);
                if (rc == 0) {
                        DP(NETIF_MSG_LINK,
                           "SFP+ module initialization took %d ms\n",
@@ -8219,7 +8270,8 @@ static int bnx2x_wait_for_sfp_module_initialized(struct bnx2x_phy *phy,
                }
                usleep_range(5000, 10000);
        }
-       rc = bnx2x_read_sfp_module_eeprom(phy, params, 1, 1, &val);
+       rc = bnx2x_read_sfp_module_eeprom(phy, params, I2C_DEV_ADDR_A0,
+                                         1, 1, &val);
        return rc;
 }
 
@@ -8376,15 +8428,6 @@ static void bnx2x_8727_specific_func(struct bnx2x_phy *phy,
                bnx2x_cl45_write(bp, phy,
                                 MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_PCS_OPT_CTRL,
                                 val);
-
-               /* Set 2-wire transfer rate of SFP+ module EEPROM
-                * to 100Khz since some DACs(direct attached cables) do
-                * not work at 400Khz.
-                */
-               bnx2x_cl45_write(bp, phy,
-                                MDIO_PMA_DEVAD,
-                                MDIO_PMA_REG_8727_TWO_WIRE_SLAVE_ADDR,
-                                0xa001);
                break;
        default:
                DP(NETIF_MSG_LINK, "Function 0x%x not supported by 8727\n",
@@ -9528,8 +9571,7 @@ static void bnx2x_save_848xx_spirom_version(struct bnx2x_phy *phy,
        } else {
                /* For 32-bit registers in 848xx, access via MDIO2ARM i/f. */
                /* (1) set reg 0xc200_0014(SPI_BRIDGE_CTRL_2) to 0x03000000 */
-               for (i = 0; i < ARRAY_SIZE(reg_set);
-                     i++)
+               for (i = 0; i < ARRAY_SIZE(reg_set); i++)
                        bnx2x_cl45_write(bp, phy, reg_set[i].devad,
                                         reg_set[i].reg, reg_set[i].val);
 
@@ -10281,7 +10323,8 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy,
                                LINK_STATUS_LINK_PARTNER_10GXFD_CAPABLE;
 
                /* Determine if EEE was negotiated */
-               if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833)
+               if ((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) ||
+                   (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834))
                        bnx2x_eee_an_resolve(phy, params, vars);
        }
 
@@ -12242,7 +12285,7 @@ static void bnx2x_init_bmac_loopback(struct link_params *params,
 
                bnx2x_xgxs_deassert(params);
 
-               /* set bmac loopback */
+               /* Set bmac loopback */
                bnx2x_bmac_enable(params, vars, 1, 1);
 
                REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
@@ -12261,7 +12304,7 @@ static void bnx2x_init_emac_loopback(struct link_params *params,
                vars->phy_flags = PHY_XGXS_FLAG;
 
                bnx2x_xgxs_deassert(params);
-               /* set bmac loopback */
+               /* Set bmac loopback */
                bnx2x_emac_enable(params, vars, 1);
                bnx2x_emac_program(params, vars);
                REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
@@ -12521,6 +12564,7 @@ int bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
                   params->req_line_speed[0], params->req_flow_ctrl[0]);
        DP(NETIF_MSG_LINK, "(2) req_speed %d, req_flowctrl %d\n",
                   params->req_line_speed[1], params->req_flow_ctrl[1]);
+       DP(NETIF_MSG_LINK, "req_adv_flow_ctrl 0x%x\n", params->req_fc_auto_adv);
        vars->link_status = 0;
        vars->phy_link_up = 0;
        vars->link_up = 0;
@@ -13440,8 +13484,8 @@ static void bnx2x_check_kr2_wa(struct link_params *params,
        int sigdet;
 
        /* Once KR2 was disabled, wait 5 seconds before checking KR2 recovery
-        * since some switches tend to reinit the AN process and clear the
-        * advertised BP/NP after ~2 seconds causing the KR2 to be disabled
+        * Since some switches tend to reinit the AN process and clear the
+        * the advertised BP/NP after ~2 seconds causing the KR2 to be disabled
         * and recovered many times
         */
        if (vars->check_kr2_recovery_cnt > 0) {
@@ -13469,8 +13513,10 @@ static void bnx2x_check_kr2_wa(struct link_params *params,
 
        /* CL73 has not begun yet */
        if (base_page == 0) {
-               if (!(vars->link_attr_sync & LINK_ATTR_SYNC_KR2_ENABLE))
+               if (!(vars->link_attr_sync & LINK_ATTR_SYNC_KR2_ENABLE)) {
                        bnx2x_kr2_recovery(params, vars, phy);
+                       DP(NETIF_MSG_LINK, "No BP\n");
+               }
                return;
        }
 
@@ -13486,7 +13532,7 @@ static void bnx2x_check_kr2_wa(struct link_params *params,
        if (!(vars->link_attr_sync & LINK_ATTR_SYNC_KR2_ENABLE)) {
                if (!not_kr2_device) {
                        DP(NETIF_MSG_LINK, "BP=0x%x, NP=0x%x\n", base_page,
-                                      next_page);
+                          next_page);
                        bnx2x_kr2_recovery(params, vars, phy);
                }
                return;
index 56c2aae4e2c81b33818d6628cc342e67862d7ebd..4df45234fdc067fc635919bd70cb9195c0a71525 100644 (file)
@@ -41,6 +41,9 @@
 #define SPEED_AUTO_NEG         0
 #define SPEED_20000            20000
 
+#define I2C_DEV_ADDR_A0                        0xa0
+#define I2C_DEV_ADDR_A2                        0xa2
+
 #define SFP_EEPROM_PAGE_SIZE                   16
 #define SFP_EEPROM_VENDOR_NAME_ADDR            0x14
 #define SFP_EEPROM_VENDOR_NAME_SIZE            16
 #define SFP_EEPROM_SERIAL_SIZE                 16
 #define SFP_EEPROM_DATE_ADDR                   0x54 /* ASCII YYMMDD */
 #define SFP_EEPROM_DATE_SIZE                   6
+#define SFP_EEPROM_DIAG_TYPE_ADDR              0x5c
+#define SFP_EEPROM_DIAG_TYPE_SIZE              1
+#define SFP_EEPROM_DIAG_ADDR_CHANGE_REQ                (1<<2)
+#define SFP_EEPROM_SFF_8472_COMP_ADDR          0x5e
+#define SFP_EEPROM_SFF_8472_COMP_SIZE          1
+
+#define SFP_EEPROM_A2_CHECKSUM_RANGE           0x5e
+#define SFP_EEPROM_A2_CC_DMI_ADDR              0x5f
+
 #define PWR_FLT_ERR_MSG_LEN                    250
 
 #define XGXS_EXT_PHY_TYPE(ext_phy_config) \
@@ -420,8 +432,8 @@ void bnx2x_sfx7101_sp_sw_reset(struct bnx2x *bp, struct bnx2x_phy *phy);
 
 /* Read "byte_cnt" bytes from address "addr" from the SFP+ EEPROM */
 int bnx2x_read_sfp_module_eeprom(struct bnx2x_phy *phy,
-                                struct link_params *params, u16 addr,
-                                u8 byte_cnt, u8 *o_buf);
+                                struct link_params *params, u8 dev_addr,
+                                u16 addr, u16 byte_cnt, u8 *o_buf);
 
 void bnx2x_hw_reset_phy(struct link_params *params);
 
index c50696b396f1cb88196d4f682d61d902d0da9fc6..86d13870399ef0737835e813ee28942dc274a6fd 100644 (file)
@@ -75,8 +75,6 @@
 #define FW_FILE_NAME_E1H       "bnx2x/bnx2x-e1h-" FW_FILE_VERSION ".fw"
 #define FW_FILE_NAME_E2                "bnx2x/bnx2x-e2-" FW_FILE_VERSION ".fw"
 
-#define MAC_LEADING_ZERO_CNT (ALIGN(ETH_ALEN, sizeof(u32)) - ETH_ALEN)
-
 /* Time in jiffies before concluding the transmitter is hung */
 #define TX_TIMEOUT             (5*HZ)
 
@@ -2955,14 +2953,16 @@ static unsigned long bnx2x_get_common_flags(struct bnx2x *bp,
        __set_bit(BNX2X_Q_FLG_ACTIVE, &flags);
 
        /* tx only connections collect statistics (on the same index as the
-        *  parent connection). The statistics are zeroed when the parent
-        *  connection is initialized.
+        * parent connection). The statistics are zeroed when the parent
+        * connection is initialized.
         */
 
        __set_bit(BNX2X_Q_FLG_STATS, &flags);
        if (zero_stats)
                __set_bit(BNX2X_Q_FLG_ZERO_STATS, &flags);
 
+       __set_bit(BNX2X_Q_FLG_PCSUM_ON_PKT, &flags);
+       __set_bit(BNX2X_Q_FLG_TUN_INC_INNER_IP_ID, &flags);
 
 #ifdef BNX2X_STOP_ON_ERROR
        __set_bit(BNX2X_Q_FLG_TX_SEC, &flags);
@@ -3227,16 +3227,29 @@ static void bnx2x_drv_info_ether_stat(struct bnx2x *bp)
 {
        struct eth_stats_info *ether_stat =
                &bp->slowpath->drv_info_to_mcp.ether_stat;
+       struct bnx2x_vlan_mac_obj *mac_obj =
+               &bp->sp_objs->mac_obj;
+       int i;
 
        strlcpy(ether_stat->version, DRV_MODULE_VERSION,
                ETH_STAT_INFO_VERSION_LEN);
 
-       bp->sp_objs[0].mac_obj.get_n_elements(bp, &bp->sp_objs[0].mac_obj,
-                                       DRV_INFO_ETH_STAT_NUM_MACS_REQUIRED,
-                                       ether_stat->mac_local);
-
+       /* get DRV_INFO_ETH_STAT_NUM_MACS_REQUIRED macs, placing them in the
+        * mac_local field in ether_stat struct. The base address is offset by 2
+        * bytes to account for the field being 8 bytes but a mac address is
+        * only 6 bytes. Likewise, the stride for the get_n_elements function is
+        * 2 bytes to compensate from the 6 bytes of a mac to the 8 bytes
+        * allocated by the ether_stat struct, so the macs will land in their
+        * proper positions.
+        */
+       for (i = 0; i < DRV_INFO_ETH_STAT_NUM_MACS_REQUIRED; i++)
+               memset(ether_stat->mac_local + i, 0,
+                      sizeof(ether_stat->mac_local[0]));
+       mac_obj->get_n_elements(bp, &bp->sp_objs[0].mac_obj,
+                               DRV_INFO_ETH_STAT_NUM_MACS_REQUIRED,
+                               ether_stat->mac_local + MAC_PAD, MAC_PAD,
+                               ETH_ALEN);
        ether_stat->mtu_size = bp->dev->mtu;
-
        if (bp->dev->features & NETIF_F_RXCSUM)
                ether_stat->feature_flags |= FEATURE_ETH_CHKSUM_OFFLOAD_MASK;
        if (bp->dev->features & NETIF_F_TSO)
@@ -3258,8 +3271,7 @@ static void bnx2x_drv_info_fcoe_stat(struct bnx2x *bp)
        if (!CNIC_LOADED(bp))
                return;
 
-       memcpy(fcoe_stat->mac_local + MAC_LEADING_ZERO_CNT,
-              bp->fip_mac, ETH_ALEN);
+       memcpy(fcoe_stat->mac_local + MAC_PAD, bp->fip_mac, ETH_ALEN);
 
        fcoe_stat->qos_priority =
                app->traffic_type_priority[LLFC_TRAFFIC_TYPE_FCOE];
@@ -3361,8 +3373,8 @@ static void bnx2x_drv_info_iscsi_stat(struct bnx2x *bp)
        if (!CNIC_LOADED(bp))
                return;
 
-       memcpy(iscsi_stat->mac_local + MAC_LEADING_ZERO_CNT,
-              bp->cnic_eth_dev.iscsi_mac, ETH_ALEN);
+       memcpy(iscsi_stat->mac_local + MAC_PAD, bp->cnic_eth_dev.iscsi_mac,
+              ETH_ALEN);
 
        iscsi_stat->qos_priority =
                app->traffic_type_priority[LLFC_TRAFFIC_TYPE_ISCSI];
@@ -6029,9 +6041,10 @@ void bnx2x_nic_init(struct bnx2x *bp, u32 load_code)
        rmb();
        bnx2x_init_rx_rings(bp);
        bnx2x_init_tx_rings(bp);
-
-       if (IS_VF(bp))
+       if (IS_VF(bp)) {
+               bnx2x_memset_stats(bp);
                return;
+       }
 
        /* Initialize MOD_ABS interrupts */
        bnx2x_init_mod_abs_int(bp, &bp->link_vars, bp->common.chip_id,
@@ -9525,6 +9538,10 @@ sp_rtnl_not_reset:
                bnx2x_vfpf_storm_rx_mode(bp);
        }
 
+       if (test_and_clear_bit(BNX2X_SP_RTNL_HYPERVISOR_VLAN,
+                              &bp->sp_rtnl_state))
+               bnx2x_pf_set_vfs_vlan(bp);
+
        /* work which needs rtnl lock not-taken (as it takes the lock itself and
         * can be called from other contexts as well)
         */
@@ -9532,8 +9549,10 @@ sp_rtnl_not_reset:
 
        /* enable SR-IOV if applicable */
        if (IS_SRIOV(bp) && test_and_clear_bit(BNX2X_SP_RTNL_ENABLE_SRIOV,
-                                              &bp->sp_rtnl_state))
+                                              &bp->sp_rtnl_state)) {
+               bnx2x_disable_sriov(bp);
                bnx2x_enable_sriov(bp);
+       }
 }
 
 static void bnx2x_period_task(struct work_struct *work)
@@ -9701,6 +9720,31 @@ static struct bnx2x_prev_path_list *
        return NULL;
 }
 
+static int bnx2x_prev_path_mark_eeh(struct bnx2x *bp)
+{
+       struct bnx2x_prev_path_list *tmp_list;
+       int rc;
+
+       rc = down_interruptible(&bnx2x_prev_sem);
+       if (rc) {
+               BNX2X_ERR("Received %d when tried to take lock\n", rc);
+               return rc;
+       }
+
+       tmp_list = bnx2x_prev_path_get_entry(bp);
+       if (tmp_list) {
+               tmp_list->aer = 1;
+               rc = 0;
+       } else {
+               BNX2X_ERR("path %d: Entry does not exist for eeh; Flow occurs before initial insmod is over ?\n",
+                         BP_PATH(bp));
+       }
+
+       up(&bnx2x_prev_sem);
+
+       return rc;
+}
+
 static bool bnx2x_prev_is_path_marked(struct bnx2x *bp)
 {
        struct bnx2x_prev_path_list *tmp_list;
@@ -9709,14 +9753,15 @@ static bool bnx2x_prev_is_path_marked(struct bnx2x *bp)
        if (down_trylock(&bnx2x_prev_sem))
                return false;
 
-       list_for_each_entry(tmp_list, &bnx2x_prev_list, list) {
-               if (PCI_SLOT(bp->pdev->devfn) == tmp_list->slot &&
-                   bp->pdev->bus->number == tmp_list->bus &&
-                   BP_PATH(bp) == tmp_list->path) {
+       tmp_list = bnx2x_prev_path_get_entry(bp);
+       if (tmp_list) {
+               if (tmp_list->aer) {
+                       DP(NETIF_MSG_HW, "Path %d was marked by AER\n",
+                          BP_PATH(bp));
+               } else {
                        rc = true;
                        BNX2X_DEV_INFO("Path %d was already cleaned from previous drivers\n",
                                       BP_PATH(bp));
-                       break;
                }
        }
 
@@ -9730,6 +9775,28 @@ static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi)
        struct bnx2x_prev_path_list *tmp_list;
        int rc;
 
+       rc = down_interruptible(&bnx2x_prev_sem);
+       if (rc) {
+               BNX2X_ERR("Received %d when tried to take lock\n", rc);
+               return rc;
+       }
+
+       /* Check whether the entry for this path already exists */
+       tmp_list = bnx2x_prev_path_get_entry(bp);
+       if (tmp_list) {
+               if (!tmp_list->aer) {
+                       BNX2X_ERR("Re-Marking the path.\n");
+               } else {
+                       DP(NETIF_MSG_HW, "Removing AER indication from path %d\n",
+                          BP_PATH(bp));
+                       tmp_list->aer = 0;
+               }
+               up(&bnx2x_prev_sem);
+               return 0;
+       }
+       up(&bnx2x_prev_sem);
+
+       /* Create an entry for this path and add it */
        tmp_list = kmalloc(sizeof(struct bnx2x_prev_path_list), GFP_KERNEL);
        if (!tmp_list) {
                BNX2X_ERR("Failed to allocate 'bnx2x_prev_path_list'\n");
@@ -9739,6 +9806,7 @@ static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi)
        tmp_list->bus = bp->pdev->bus->number;
        tmp_list->slot = PCI_SLOT(bp->pdev->devfn);
        tmp_list->path = BP_PATH(bp);
+       tmp_list->aer = 0;
        tmp_list->undi = after_undi ? (1 << BP_PORT(bp)) : 0;
 
        rc = down_interruptible(&bnx2x_prev_sem);
@@ -9746,8 +9814,8 @@ static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi)
                BNX2X_ERR("Received %d when tried to take lock\n", rc);
                kfree(tmp_list);
        } else {
-               BNX2X_DEV_INFO("Marked path [%d] - finished previous unload\n",
-                               BP_PATH(bp));
+               DP(NETIF_MSG_HW, "Marked path [%d] - finished previous unload\n",
+                  BP_PATH(bp));
                list_add(&tmp_list->list, &bnx2x_prev_list);
                up(&bnx2x_prev_sem);
        }
@@ -9990,6 +10058,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp)
        }
 
        do {
+               int aer = 0;
                /* Lock MCP using an unload request */
                fw = bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS, 0);
                if (!fw) {
@@ -9998,7 +10067,18 @@ static int bnx2x_prev_unload(struct bnx2x *bp)
                        break;
                }
 
-               if (fw == FW_MSG_CODE_DRV_UNLOAD_COMMON) {
+               rc = down_interruptible(&bnx2x_prev_sem);
+               if (rc) {
+                       BNX2X_ERR("Cannot check for AER; Received %d when tried to take lock\n",
+                                 rc);
+               } else {
+                       /* If Path is marked by EEH, ignore unload status */
+                       aer = !!(bnx2x_prev_path_get_entry(bp) &&
+                                bnx2x_prev_path_get_entry(bp)->aer);
+                       up(&bnx2x_prev_sem);
+               }
+
+               if (fw == FW_MSG_CODE_DRV_UNLOAD_COMMON || aer) {
                        rc = bnx2x_prev_unload_common(bp);
                        break;
                }
@@ -10038,8 +10118,12 @@ static void bnx2x_get_common_hwinfo(struct bnx2x *bp)
        id = ((val & 0xffff) << 16);
        val = REG_RD(bp, MISC_REG_CHIP_REV);
        id |= ((val & 0xf) << 12);
-       val = REG_RD(bp, MISC_REG_CHIP_METAL);
-       id |= ((val & 0xff) << 4);
+
+       /* Metal is read from PCI regs, but we can't access >=0x400 from
+        * the configuration space (so we need to reg_rd)
+        */
+       val = REG_RD(bp, PCICFG_OFFSET + PCI_ID_VAL3);
+       id |= (((val >> 24) & 0xf) << 4);
        val = REG_RD(bp, MISC_REG_BOND_ID);
        id |= (val & 0xf);
        bp->common.chip_id = id;
@@ -10816,14 +10900,12 @@ static void bnx2x_get_cnic_mac_hwinfo(struct bnx2x *bp)
                        }
                }
 
-               if (IS_MF_STORAGE_SD(bp))
-                       /* Zero primary MAC configuration */
-                       memset(bp->dev->dev_addr, 0, ETH_ALEN);
-
-               if (IS_MF_FCOE_AFEX(bp) || IS_MF_FCOE_SD(bp))
-                       /* use FIP MAC as primary MAC */
+               /* If this is a storage-only interface, use SAN mac as
+                * primary MAC. Notice that for SD this is already the case,
+                * as the SAN mac was copied from the primary MAC.
+                */
+               if (IS_MF_FCOE_AFEX(bp))
                        memcpy(bp->dev->dev_addr, fip_mac, ETH_ALEN);
-
        } else {
                val2 = SHMEM_RD(bp, dev_info.port_hw_config[port].
                                iscsi_mac_upper);
@@ -11060,6 +11142,9 @@ static int bnx2x_get_hwinfo(struct bnx2x *bp)
                                } else
                                        BNX2X_DEV_INFO("illegal OV for SD\n");
                                break;
+                       case SHARED_FEAT_CFG_FORCE_SF_MODE_FORCED_SF:
+                               bp->mf_config[vn] = 0;
+                               break;
                        default:
                                /* Unknown configuration: reset mf_config */
                                bp->mf_config[vn] = 0;
@@ -11406,26 +11491,6 @@ static int bnx2x_init_bp(struct bnx2x *bp)
  * net_device service functions
  */
 
-static int bnx2x_open_epilog(struct bnx2x *bp)
-{
-       /* Enable sriov via delayed work. This must be done via delayed work
-        * because it causes the probe of the vf devices to be run, which invoke
-        * register_netdevice which must have rtnl lock taken. As we are holding
-        * the lock right now, that could only work if the probe would not take
-        * the lock. However, as the probe of the vf may be called from other
-        * contexts as well (such as passthrough to vm failes) it can't assume
-        * the lock is being held for it. Using delayed work here allows the
-        * probe code to simply take the lock (i.e. wait for it to be released
-        * if it is being held).
-        */
-       smp_mb__before_clear_bit();
-       set_bit(BNX2X_SP_RTNL_ENABLE_SRIOV, &bp->sp_rtnl_state);
-       smp_mb__after_clear_bit();
-       schedule_delayed_work(&bp->sp_rtnl_task, 0);
-
-       return 0;
-}
-
 /* called with rtnl_lock */
 static int bnx2x_open(struct net_device *dev)
 {
@@ -11795,6 +11860,8 @@ static const struct net_device_ops bnx2x_netdev_ops = {
        .ndo_setup_tc           = bnx2x_setup_tc,
 #ifdef CONFIG_BNX2X_SRIOV
        .ndo_set_vf_mac         = bnx2x_set_vf_mac,
+       .ndo_set_vf_vlan        = bnx2x_set_vf_vlan,
+       .ndo_get_vf_config      = bnx2x_get_vf_config,
 #endif
 #ifdef NETDEV_FCOE_WWNN
        .ndo_fcoe_get_wwn       = bnx2x_fcoe_get_wwn,
@@ -11957,19 +12024,26 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev,
        dev->watchdog_timeo = TX_TIMEOUT;
 
        dev->netdev_ops = &bnx2x_netdev_ops;
-       bnx2x_set_ethtool_ops(dev);
+       bnx2x_set_ethtool_ops(bp, dev);
 
        dev->priv_flags |= IFF_UNICAST_FLT;
 
        dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 |
                NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO |
-               NETIF_F_RXHASH | NETIF_F_HW_VLAN_TX;
+               NETIF_F_RXHASH | NETIF_F_HW_VLAN_CTAG_TX;
+       if (!CHIP_IS_E1x(bp)) {
+               dev->hw_features |= NETIF_F_GSO_GRE;
+               dev->hw_enc_features =
+                       NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
+                       NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 |
+                       NETIF_F_GSO_GRE;
+       }
 
        dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_HIGHDMA;
 
-       dev->features |= dev->hw_features | NETIF_F_HW_VLAN_RX;
+       dev->features |= dev->hw_features | NETIF_F_HW_VLAN_CTAG_RX;
        if (bp->flags & USING_DAC_FLAG)
                dev->features |= NETIF_F_HIGHDMA;
 
@@ -12451,7 +12525,7 @@ static int bnx2x_init_one(struct pci_dev *pdev,
         * l2 connections.
         */
        if (IS_VF(bp)) {
-               bnx2x_vf_map_doorbells(bp);
+               bp->doorbells = bnx2x_vf_doorbells(bp);
                rc = bnx2x_vf_pci_alloc(bp);
                if (rc)
                        goto init_one_exit;
@@ -12479,13 +12553,8 @@ static int bnx2x_init_one(struct pci_dev *pdev,
                        goto init_one_exit;
        }
 
-       /* Enable SRIOV if capability found in configuration space.
-        * Once the generic SR-IOV framework makes it in from the
-        * pci tree this will be revised, to allow dynamic control
-        * over the number of VFs. Right now, change the num of vfs
-        * param below to enable SR-IOV.
-        */
-       rc = bnx2x_iov_init_one(bp, int_mode, 0/*num vfs*/);
+       /* Enable SRIOV if capability found in configuration space */
+       rc = bnx2x_iov_init_one(bp, int_mode, BNX2X_MAX_NUM_OF_VFS);
        if (rc)
                goto init_one_exit;
 
@@ -12497,16 +12566,6 @@ static int bnx2x_init_one(struct pci_dev *pdev,
        if (CHIP_IS_E1x(bp))
                bp->flags |= NO_FCOE_FLAG;
 
-       /* disable FCOE for 57840 device, until FW supports it */
-       switch (ent->driver_data) {
-       case BCM57840_O:
-       case BCM57840_4_10:
-       case BCM57840_2_20:
-       case BCM57840_MFO:
-       case BCM57840_MF:
-               bp->flags |= NO_FCOE_FLAG;
-       }
-
        /* Set bp->num_queues for MSI-X mode*/
        bnx2x_set_num_queues(bp);
 
@@ -12640,9 +12699,7 @@ static void bnx2x_remove_one(struct pci_dev *pdev)
 
 static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
 {
-       int i;
-
-       bp->state = BNX2X_STATE_ERROR;
+       bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
 
        bp->rx_mode = BNX2X_RX_MODE_NONE;
 
@@ -12651,29 +12708,21 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
 
        /* Stop Tx */
        bnx2x_tx_disable(bp);
-
-       bnx2x_netif_stop(bp, 0);
        /* Delete all NAPI objects */
        bnx2x_del_all_napi(bp);
        if (CNIC_LOADED(bp))
                bnx2x_del_all_napi_cnic(bp);
+       netdev_reset_tc(bp->dev);
 
        del_timer_sync(&bp->timer);
+       cancel_delayed_work(&bp->sp_task);
+       cancel_delayed_work(&bp->period_task);
 
-       bnx2x_stats_handle(bp, STATS_EVENT_STOP);
-
-       /* Release IRQs */
-       bnx2x_free_irq(bp);
-
-       /* Free SKBs, SGEs, TPA pool and driver internals */
-       bnx2x_free_skbs(bp);
-
-       for_each_rx_queue(bp, i)
-               bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE);
-
-       bnx2x_free_mem(bp);
+       spin_lock_bh(&bp->stats_lock);
+       bp->stats_state = STATS_STATE_DISABLED;
+       spin_unlock_bh(&bp->stats_lock);
 
-       bp->state = BNX2X_STATE_CLOSED;
+       bnx2x_save_statistics(bp);
 
        netif_carrier_off(bp->dev);
 
@@ -12709,6 +12758,8 @@ static pci_ers_result_t bnx2x_io_error_detected(struct pci_dev *pdev,
 
        rtnl_lock();
 
+       BNX2X_ERR("IO error detected\n");
+
        netif_device_detach(dev);
 
        if (state == pci_channel_io_perm_failure) {
@@ -12719,6 +12770,8 @@ static pci_ers_result_t bnx2x_io_error_detected(struct pci_dev *pdev,
        if (netif_running(dev))
                bnx2x_eeh_nic_unload(bp);
 
+       bnx2x_prev_path_mark_eeh(bp);
+
        pci_disable_device(pdev);
 
        rtnl_unlock();
@@ -12737,9 +12790,10 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        struct bnx2x *bp = netdev_priv(dev);
+       int i;
 
        rtnl_lock();
-
+       BNX2X_ERR("IO slot reset initializing...\n");
        if (pci_enable_device(pdev)) {
                dev_err(&pdev->dev,
                        "Cannot re-enable PCI device after reset\n");
@@ -12753,6 +12807,42 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev)
        if (netif_running(dev))
                bnx2x_set_power_state(bp, PCI_D0);
 
+       if (netif_running(dev)) {
+               BNX2X_ERR("IO slot reset --> driver unload\n");
+               if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) {
+                       u32 v;
+
+                       v = SHMEM2_RD(bp,
+                                     drv_capabilities_flag[BP_FW_MB_IDX(bp)]);
+                       SHMEM2_WR(bp, drv_capabilities_flag[BP_FW_MB_IDX(bp)],
+                                 v & ~DRV_FLAGS_CAPABILITIES_LOADED_L2);
+               }
+               bnx2x_drain_tx_queues(bp);
+               bnx2x_send_unload_req(bp, UNLOAD_RECOVERY);
+               bnx2x_netif_stop(bp, 1);
+               bnx2x_free_irq(bp);
+
+               /* Report UNLOAD_DONE to MCP */
+               bnx2x_send_unload_done(bp, true);
+
+               bp->sp_state = 0;
+               bp->port.pmf = 0;
+
+               bnx2x_prev_unload(bp);
+
+               /* We should have resetted the engine, so It's fair to
+                * assume the FW will no longer write to the bnx2x driver.
+                */
+               bnx2x_squeeze_objects(bp);
+               bnx2x_free_skbs(bp);
+               for_each_rx_queue(bp, i)
+                       bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE);
+               bnx2x_free_fp_mem(bp);
+               bnx2x_free_mem(bp);
+
+               bp->state = BNX2X_STATE_CLOSED;
+       }
+
        rtnl_unlock();
 
        return PCI_ERS_RESULT_RECOVERED;
@@ -12779,6 +12869,9 @@ static void bnx2x_io_resume(struct pci_dev *pdev)
 
        bnx2x_eeh_recover(bp);
 
+       bp->fw_seq = SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) &
+                                                       DRV_MSG_SEQ_NUMBER_MASK;
+
        if (netif_running(dev))
                bnx2x_nic_load(bp, LOAD_NORMAL);
 
@@ -12801,6 +12894,9 @@ static struct pci_driver bnx2x_pci_driver = {
        .suspend     = bnx2x_suspend,
        .resume      = bnx2x_resume,
        .err_handler = &bnx2x_err_handler,
+#ifdef CONFIG_BNX2X_SRIOV
+       .sriov_configure = bnx2x_sriov_configure,
+#endif
 };
 
 static int __init bnx2x_init(void)
index 791eb2d530111dd7e5fa8aa5c88adadf5e91ddb9..d22bc40091ec47738a0132dd549c095645f90702 100644 (file)
 /* [R 4] This field indicates the type of the device. '0' - 2 Ports; '1' - 1
    Port. */
 #define MISC_REG_BOND_ID                                        0xa400
-/* [R 8] These bits indicate the metal revision of the chip. This value
-   starts at 0x00 for each all-layer tape-out and increments by one for each
-   tape-out. */
-#define MISC_REG_CHIP_METAL                                     0xa404
 /* [R 16] These bits indicate the part number for the chip. */
 #define MISC_REG_CHIP_NUM                                       0xa408
 /* [R 4] These bits indicate the base revision of the chip. This value
 #define PCI_PM_DATA_B                                  0x414
 #define PCI_ID_VAL1                                    0x434
 #define PCI_ID_VAL2                                    0x438
+#define PCI_ID_VAL3                                    0x43c
+
 #define GRC_CONFIG_REG_PF_INIT_VF              0x624
 #define GRC_CR_PF_INIT_VF_PF_FIRST_VF_NUM_MASK 0xf
 /* First VF_NUM for PF is encoded in this register.
index 7306416bc90d29af91461440ff19da77f07429e7..32a9609cc98bcd36e56b0193bba3a3508424f5cb 100644 (file)
@@ -30,8 +30,6 @@
 
 #define BNX2X_MAX_EMUL_MULTI           16
 
-#define MAC_LEADING_ZERO_CNT (ALIGN(ETH_ALEN, sizeof(u32)) - ETH_ALEN)
-
 /**** Exe Queue interfaces ****/
 
 /**
@@ -444,30 +442,21 @@ static bool bnx2x_put_credit_vlan_mac(struct bnx2x_vlan_mac_obj *o)
 }
 
 static int bnx2x_get_n_elements(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *o,
-                               int n, u8 *buf)
+                               int n, u8 *base, u8 stride, u8 size)
 {
        struct bnx2x_vlan_mac_registry_elem *pos;
-       u8 *next = buf;
+       u8 *next = base;
        int counter = 0;
 
        /* traverse list */
        list_for_each_entry(pos, &o->head, link) {
                if (counter < n) {
-                       /* place leading zeroes in buffer */
-                       memset(next, 0, MAC_LEADING_ZERO_CNT);
-
-                       /* place mac after leading zeroes*/
-                       memcpy(next + MAC_LEADING_ZERO_CNT, pos->u.mac.mac,
-                              ETH_ALEN);
-
-                       /* calculate address of next element and
-                        * advance counter
-                        */
+                       memcpy(next, &pos->u, size);
                        counter++;
-                       next = buf + counter * ALIGN(ETH_ALEN, sizeof(u32));
+                       DP(BNX2X_MSG_SP, "copied element number %d to address %p element was:\n",
+                          counter, next);
+                       next += stride + size;
 
-                       DP(BNX2X_MSG_SP, "copied element number %d to address %p element was %pM\n",
-                          counter, next, pos->u.mac.mac);
                }
        }
        return counter * ETH_ALEN;
@@ -487,7 +476,8 @@ static int bnx2x_check_mac_add(struct bnx2x *bp,
 
        /* Check if a requested MAC already exists */
        list_for_each_entry(pos, &o->head, link)
-               if (!memcmp(data->mac.mac, pos->u.mac.mac, ETH_ALEN))
+               if (!memcmp(data->mac.mac, pos->u.mac.mac, ETH_ALEN) &&
+                   (data->mac.is_inner_mac == pos->u.mac.is_inner_mac))
                        return -EEXIST;
 
        return 0;
@@ -520,7 +510,9 @@ static int bnx2x_check_vlan_mac_add(struct bnx2x *bp,
        list_for_each_entry(pos, &o->head, link)
                if ((data->vlan_mac.vlan == pos->u.vlan_mac.vlan) &&
                    (!memcmp(data->vlan_mac.mac, pos->u.vlan_mac.mac,
-                            ETH_ALEN)))
+                                 ETH_ALEN)) &&
+                   (data->vlan_mac.is_inner_mac ==
+                    pos->u.vlan_mac.is_inner_mac))
                        return -EEXIST;
 
        return 0;
@@ -538,7 +530,8 @@ static struct bnx2x_vlan_mac_registry_elem *
        DP(BNX2X_MSG_SP, "Checking MAC %pM for DEL command\n", data->mac.mac);
 
        list_for_each_entry(pos, &o->head, link)
-               if (!memcmp(data->mac.mac, pos->u.mac.mac, ETH_ALEN))
+               if ((!memcmp(data->mac.mac, pos->u.mac.mac, ETH_ALEN)) &&
+                   (data->mac.is_inner_mac == pos->u.mac.is_inner_mac))
                        return pos;
 
        return NULL;
@@ -573,7 +566,9 @@ static struct bnx2x_vlan_mac_registry_elem *
        list_for_each_entry(pos, &o->head, link)
                if ((data->vlan_mac.vlan == pos->u.vlan_mac.vlan) &&
                    (!memcmp(data->vlan_mac.mac, pos->u.vlan_mac.mac,
-                            ETH_ALEN)))
+                            ETH_ALEN)) &&
+                   (data->vlan_mac.is_inner_mac ==
+                    pos->u.vlan_mac.is_inner_mac))
                        return pos;
 
        return NULL;
@@ -770,6 +765,8 @@ static void bnx2x_set_one_mac_e2(struct bnx2x *bp,
        bnx2x_set_fw_mac_addr(&rule_entry->mac.mac_msb,
                              &rule_entry->mac.mac_mid,
                              &rule_entry->mac.mac_lsb, mac);
+       rule_entry->mac.inner_mac =
+               cpu_to_le16(elem->cmd_data.vlan_mac.u.mac.is_inner_mac);
 
        /* MOVE: Add a rule that will add this MAC to the target Queue */
        if (cmd == BNX2X_VLAN_MAC_MOVE) {
@@ -786,6 +783,9 @@ static void bnx2x_set_one_mac_e2(struct bnx2x *bp,
                bnx2x_set_fw_mac_addr(&rule_entry->mac.mac_msb,
                                      &rule_entry->mac.mac_mid,
                                      &rule_entry->mac.mac_lsb, mac);
+               rule_entry->mac.inner_mac =
+                       cpu_to_le16(elem->cmd_data.vlan_mac.
+                                               u.mac.is_inner_mac);
        }
 
        /* Set the ramrod data header */
@@ -974,7 +974,8 @@ static void bnx2x_set_one_vlan_mac_e2(struct bnx2x *bp,
        bnx2x_set_fw_mac_addr(&rule_entry->pair.mac_msb,
                              &rule_entry->pair.mac_mid,
                              &rule_entry->pair.mac_lsb, mac);
-
+       rule_entry->pair.inner_mac =
+               cpu_to_le16(elem->cmd_data.vlan_mac.u.vlan_mac.is_inner_mac);
        /* MOVE: Add a rule that will add this MAC to the target Queue */
        if (cmd == BNX2X_VLAN_MAC_MOVE) {
                rule_entry++;
@@ -991,6 +992,9 @@ static void bnx2x_set_one_vlan_mac_e2(struct bnx2x *bp,
                bnx2x_set_fw_mac_addr(&rule_entry->pair.mac_msb,
                                      &rule_entry->pair.mac_mid,
                                      &rule_entry->pair.mac_lsb, mac);
+               rule_entry->pair.inner_mac =
+                       cpu_to_le16(elem->cmd_data.vlan_mac.u.
+                                               vlan_mac.is_inner_mac);
        }
 
        /* Set the ramrod data header */
@@ -1854,6 +1858,7 @@ static int bnx2x_vlan_mac_del_all(struct bnx2x *bp,
                                return rc;
                        }
                        list_del(&exeq_pos->link);
+                       bnx2x_exe_queue_free_elem(bp, exeq_pos);
                }
        }
 
@@ -2012,6 +2017,7 @@ void bnx2x_init_vlan_obj(struct bnx2x *bp,
                vlan_obj->check_move        = bnx2x_check_move;
                vlan_obj->ramrod_cmd        =
                        RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES;
+               vlan_obj->get_n_elements    = bnx2x_get_n_elements;
 
                /* Exe Queue */
                bnx2x_exe_queue_init(bp,
@@ -4426,6 +4432,12 @@ static void bnx2x_q_fill_init_tx_data(struct bnx2x_queue_sp_obj *o,
        tx_data->force_default_pri_flg =
                test_bit(BNX2X_Q_FLG_FORCE_DEFAULT_PRI, flags);
 
+       tx_data->tunnel_lso_inc_ip_id =
+               test_bit(BNX2X_Q_FLG_TUN_INC_INNER_IP_ID, flags);
+       tx_data->tunnel_non_lso_pcsum_location =
+               test_bit(BNX2X_Q_FLG_PCSUM_ON_PKT, flags) ? PCSUM_ON_PKT :
+                                                                 PCSUM_ON_BD;
+
        tx_data->tx_status_block_id = params->fw_sb_id;
        tx_data->tx_sb_index_number = params->sb_cq_index;
        tx_data->tss_leading_client_id = params->tss_leading_cl_id;
@@ -5669,17 +5681,18 @@ static inline int bnx2x_func_send_start(struct bnx2x *bp,
        memset(rdata, 0, sizeof(*rdata));
 
        /* Fill the ramrod data with provided parameters */
-       rdata->function_mode    = (u8)start_params->mf_mode;
-       rdata->sd_vlan_tag      = cpu_to_le16(start_params->sd_vlan_tag);
-       rdata->path_id          = BP_PATH(bp);
-       rdata->network_cos_mode = start_params->network_cos_mode;
-
-       /*
-        *  No need for an explicit memory barrier here as long we would
-        *  need to ensure the ordering of writing to the SPQ element
-        *  and updating of the SPQ producer which involves a memory
-        *  read and we will have to put a full memory barrier there
-        *  (inside bnx2x_sp_post()).
+       rdata->function_mode    = (u8)start_params->mf_mode;
+       rdata->sd_vlan_tag      = cpu_to_le16(start_params->sd_vlan_tag);
+       rdata->path_id          = BP_PATH(bp);
+       rdata->network_cos_mode = start_params->network_cos_mode;
+       rdata->gre_tunnel_mode  = start_params->gre_tunnel_mode;
+       rdata->gre_tunnel_rss   = start_params->gre_tunnel_rss;
+
+       /* No need for an explicit memory barrier here as long we would
+        * need to ensure the ordering of writing to the SPQ element
+        * and updating of the SPQ producer which involves a memory
+        * read and we will have to put a full memory barrier there
+        * (inside bnx2x_sp_post()).
         */
 
        return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_FUNCTION_START, 0,
index ff907609b9fc5cef7c975b2a28453d562e09fc02..43c00bc84a08826368d58e6253f3070a37118a68 100644 (file)
@@ -100,6 +100,7 @@ struct bnx2x_raw_obj {
 /************************* VLAN-MAC commands related parameters ***************/
 struct bnx2x_mac_ramrod_data {
        u8 mac[ETH_ALEN];
+       u8 is_inner_mac;
 };
 
 struct bnx2x_vlan_ramrod_data {
@@ -108,6 +109,7 @@ struct bnx2x_vlan_ramrod_data {
 
 struct bnx2x_vlan_mac_ramrod_data {
        u8 mac[ETH_ALEN];
+       u8 is_inner_mac;
        u16 vlan;
 };
 
@@ -313,8 +315,9 @@ struct bnx2x_vlan_mac_obj {
         *
         * @return number of copied bytes
         */
-       int (*get_n_elements)(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *o,
-                             int n, u8 *buf);
+       int (*get_n_elements)(struct bnx2x *bp,
+                             struct bnx2x_vlan_mac_obj *o, int n, u8 *base,
+                             u8 stride, u8 size);
 
        /**
         * Checks if ADD-ramrod with the given params may be performed.
@@ -824,7 +827,9 @@ enum {
        BNX2X_Q_FLG_TX_SEC,
        BNX2X_Q_FLG_ANTI_SPOOF,
        BNX2X_Q_FLG_SILENT_VLAN_REM,
-       BNX2X_Q_FLG_FORCE_DEFAULT_PRI
+       BNX2X_Q_FLG_FORCE_DEFAULT_PRI,
+       BNX2X_Q_FLG_PCSUM_ON_PKT,
+       BNX2X_Q_FLG_TUN_INC_INNER_IP_ID
 };
 
 /* Queue type options: queue type may be a compination of below. */
@@ -842,6 +847,7 @@ enum bnx2x_q_type {
 #define BNX2X_MULTI_TX_COS_E3B0                        3
 #define BNX2X_MULTI_TX_COS                     3 /* Maximum possible */
 
+#define MAC_PAD (ALIGN(ETH_ALEN, sizeof(u32)) - ETH_ALEN)
 
 struct bnx2x_queue_init_params {
        struct {
@@ -1118,6 +1124,15 @@ struct bnx2x_func_start_params {
 
        /* Function cos mode */
        u8 network_cos_mode;
+
+       /* NVGRE classification enablement */
+       u8 nvgre_clss_en;
+
+       /* NO_GRE_TUNNEL/NVGRE_TUNNEL/L2GRE_TUNNEL/IPGRE_TUNNEL */
+       u8 gre_tunnel_mode;
+
+       /* GRE_OUTER_HEADERS_RSS/GRE_INNER_HEADERS_RSS/NVGRE_KEY_ENTROPY_RSS */
+       u8 gre_tunnel_rss;
 };
 
 struct bnx2x_func_switch_update_params {
index 6adfa2093581ef611960e0c9fe364b69726d6de5..2ce7c7471367812bc88375ab600211c7b2a437db 100644 (file)
@@ -20,7 +20,9 @@
 #include "bnx2x.h"
 #include "bnx2x_init.h"
 #include "bnx2x_cmn.h"
+#include "bnx2x_sp.h"
 #include <linux/crc32.h>
+#include <linux/if_vlan.h>
 
 /* General service functions */
 static void storm_memset_vf_to_pf(struct bnx2x *bp, u16 abs_fid,
@@ -555,8 +557,7 @@ static int bnx2x_vfop_config_list(struct bnx2x *bp,
                rc = bnx2x_config_vlan_mac(bp, vlan_mac);
                if (rc >= 0) {
                        cnt += pos->add ? 1 : -1;
-                       list_del(&pos->link);
-                       list_add(&pos->link, &rollback_list);
+                       list_move(&pos->link, &rollback_list);
                        rc = 0;
                } else if (rc == -EEXIST) {
                        rc = 0;
@@ -958,6 +959,12 @@ op_err:
        BNX2X_ERR("QSETUP[%d:%d] error: rc %d\n", vf->abs_vfid, qid, vfop->rc);
 op_done:
        case BNX2X_VFOP_QSETUP_DONE:
+               vf->cfg_flags |= VF_CFG_VLAN;
+               smp_mb__before_clear_bit();
+               set_bit(BNX2X_SP_RTNL_HYPERVISOR_VLAN,
+                       &bp->sp_rtnl_state);
+               smp_mb__after_clear_bit();
+               schedule_delayed_work(&bp->sp_rtnl_task, 0);
                bnx2x_vfop_end(bp, vf, vfop);
                return;
        default:
@@ -1459,7 +1466,6 @@ static u8 bnx2x_vf_is_pcie_pending(struct bnx2x *bp, u8 abs_vfid)
                return bnx2x_is_pcie_pending(dev);
 
 unknown_dev:
-       BNX2X_ERR("Unknown device\n");
        return false;
 }
 
@@ -1926,20 +1932,22 @@ int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param,
 
        /* SRIOV can be enabled only with MSIX */
        if (int_mode_param == BNX2X_INT_MODE_MSI ||
-           int_mode_param == BNX2X_INT_MODE_INTX)
+           int_mode_param == BNX2X_INT_MODE_INTX) {
                BNX2X_ERR("Forced MSI/INTx mode is incompatible with SRIOV\n");
+               return 0;
+       }
 
        err = -EIO;
        /* verify ari is enabled */
        if (!bnx2x_ari_enabled(bp->pdev)) {
-               BNX2X_ERR("ARI not supported, SRIOV can not be enabled\n");
-               return err;
+               BNX2X_ERR("ARI not supported (check pci bridge ARI forwarding), SRIOV can not be enabled\n");
+               return 0;
        }
 
        /* verify igu is in normal mode */
        if (CHIP_INT_MODE_IS_BC(bp)) {
                BNX2X_ERR("IGU not normal mode,  SRIOV can not be enabled\n");
-               return err;
+               return 0;
        }
 
        /* allocate the vfs database */
@@ -1964,8 +1972,10 @@ int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param,
        if (iov->total == 0)
                goto failed;
 
-       /* calculate the actual number of VFs */
-       iov->nr_virtfn = min_t(u16, iov->total, (u16)num_vfs_param);
+       iov->nr_virtfn = min_t(u16, iov->total, num_vfs_param);
+
+       DP(BNX2X_MSG_IOV, "num_vfs_param was %d, nr_virtfn was %d\n",
+          num_vfs_param, iov->nr_virtfn);
 
        /* allocate the vf array */
        bp->vfdb->vfs = kzalloc(sizeof(struct bnx2x_virtf) *
@@ -2378,8 +2388,8 @@ int bnx2x_iov_eq_sp_event(struct bnx2x *bp, union event_ring_elem *elem)
                goto get_vf;
        case EVENT_RING_OPCODE_MALICIOUS_VF:
                abs_vfid = elem->message.data.malicious_vf_event.vf_id;
-               DP(BNX2X_MSG_IOV, "Got VF MALICIOUS notification abs_vfid=%d\n",
-                  abs_vfid);
+               DP(BNX2X_MSG_IOV, "Got VF MALICIOUS notification abs_vfid=%d err_id=0x%x\n",
+                  abs_vfid, elem->message.data.malicious_vf_event.err_id);
                goto get_vf;
        default:
                return 1;
@@ -2436,8 +2446,8 @@ get_vf:
                /* Do nothing for now */
                break;
        case EVENT_RING_OPCODE_MALICIOUS_VF:
-               DP(BNX2X_MSG_IOV, "got VF [%d] MALICIOUS notification\n",
-                  vf->abs_vfid);
+               DP(BNX2X_MSG_IOV, "Got VF MALICIOUS notification abs_vfid=%d error id %x\n",
+                  abs_vfid, elem->message.data.malicious_vf_event.err_id);
                /* Do nothing for now */
                break;
        }
@@ -3012,21 +3022,138 @@ void bnx2x_unlock_vf_pf_channel(struct bnx2x *bp, struct bnx2x_virtf *vf,
        vf->op_current = CHANNEL_TLV_NONE;
 }
 
-void bnx2x_enable_sriov(struct bnx2x *bp)
+int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs_param)
 {
-       int rc = 0;
 
-       /* disbale sriov in case it is still enabled */
+       struct bnx2x *bp = netdev_priv(pci_get_drvdata(dev));
+
+       DP(BNX2X_MSG_IOV, "bnx2x_sriov_configure called with %d, BNX2X_NR_VIRTFN(bp) was %d\n",
+          num_vfs_param, BNX2X_NR_VIRTFN(bp));
+
+       /* HW channel is only operational when PF is up */
+       if (bp->state != BNX2X_STATE_OPEN) {
+               BNX2X_ERR("VF num configurtion via sysfs not supported while PF is down");
+               return -EINVAL;
+       }
+
+       /* we are always bound by the total_vfs in the configuration space */
+       if (num_vfs_param > BNX2X_NR_VIRTFN(bp)) {
+               BNX2X_ERR("truncating requested number of VFs (%d) down to maximum allowed (%d)\n",
+                         num_vfs_param, BNX2X_NR_VIRTFN(bp));
+               num_vfs_param = BNX2X_NR_VIRTFN(bp);
+       }
+
+       bp->requested_nr_virtfn = num_vfs_param;
+       if (num_vfs_param == 0) {
+               pci_disable_sriov(dev);
+               return 0;
+       } else {
+               return bnx2x_enable_sriov(bp);
+       }
+}
+
+int bnx2x_enable_sriov(struct bnx2x *bp)
+{
+       int rc = 0, req_vfs = bp->requested_nr_virtfn;
+
+       rc = pci_enable_sriov(bp->pdev, req_vfs);
+       if (rc) {
+               BNX2X_ERR("pci_enable_sriov failed with %d\n", rc);
+               return rc;
+       }
+       DP(BNX2X_MSG_IOV, "sriov enabled (%d vfs)\n", req_vfs);
+       return req_vfs;
+}
+
+void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp)
+{
+       int vfidx;
+       struct pf_vf_bulletin_content *bulletin;
+
+       DP(BNX2X_MSG_IOV, "configuring vlan for VFs from sp-task\n");
+       for_each_vf(bp, vfidx) {
+       bulletin = BP_VF_BULLETIN(bp, vfidx);
+               if (BP_VF(bp, vfidx)->cfg_flags & VF_CFG_VLAN)
+                       bnx2x_set_vf_vlan(bp->dev, vfidx, bulletin->vlan, 0);
+       }
+}
+
+void bnx2x_disable_sriov(struct bnx2x *bp)
+{
        pci_disable_sriov(bp->pdev);
-       DP(BNX2X_MSG_IOV, "sriov disabled\n");
+}
+
+static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx,
+                              struct bnx2x_virtf *vf)
+{
+       if (!IS_SRIOV(bp)) {
+               BNX2X_ERR("vf ndo called though sriov is disabled\n");
+               return -EINVAL;
+       }
+
+       if (vfidx >= BNX2X_NR_VIRTFN(bp)) {
+               BNX2X_ERR("vf ndo called for uninitialized VF. vfidx was %d BNX2X_NR_VIRTFN was %d\n",
+                         vfidx, BNX2X_NR_VIRTFN(bp));
+               return -EINVAL;
+       }
+
+       if (!vf) {
+               BNX2X_ERR("vf ndo called but vf was null. vfidx was %d\n",
+                         vfidx);
+               return -EINVAL;
+       }
 
-       /* enable sriov */
-       DP(BNX2X_MSG_IOV, "vf num (%d)\n", (bp->vfdb->sriov.nr_virtfn));
-       rc = pci_enable_sriov(bp->pdev, (bp->vfdb->sriov.nr_virtfn));
+       return 0;
+}
+
+int bnx2x_get_vf_config(struct net_device *dev, int vfidx,
+                       struct ifla_vf_info *ivi)
+{
+       struct bnx2x *bp = netdev_priv(dev);
+       struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
+       struct bnx2x_vlan_mac_obj *mac_obj = &bnx2x_vfq(vf, 0, mac_obj);
+       struct bnx2x_vlan_mac_obj *vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj);
+       struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
+       int rc;
+
+       /* sanity */
+       rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
        if (rc)
-               BNX2X_ERR("pci_enable_sriov failed with %d\n", rc);
-       else
-               DP(BNX2X_MSG_IOV, "sriov enabled\n");
+               return rc;
+       if (!mac_obj || !vlan_obj || !bulletin) {
+               BNX2X_ERR("VF partially initialized\n");
+               return -EINVAL;
+       }
+
+       ivi->vf = vfidx;
+       ivi->qos = 0;
+       ivi->tx_rate = 10000; /* always 10G. TBA take from link struct */
+       ivi->spoofchk = 1; /*always enabled */
+       if (vf->state == VF_ENABLED) {
+               /* mac and vlan are in vlan_mac objects */
+               mac_obj->get_n_elements(bp, mac_obj, 1, (u8 *)&ivi->mac,
+                                       0, ETH_ALEN);
+               vlan_obj->get_n_elements(bp, vlan_obj, 1, (u8 *)&ivi->vlan,
+                                        0, VLAN_HLEN);
+       } else {
+               /* mac */
+               if (bulletin->valid_bitmap & (1 << MAC_ADDR_VALID))
+                       /* mac configured by ndo so its in bulletin board */
+                       memcpy(&ivi->mac, bulletin->mac, ETH_ALEN);
+               else
+                       /* funtion has not been loaded yet. Show mac as 0s */
+                       memset(&ivi->mac, 0, ETH_ALEN);
+
+               /* vlan */
+               if (bulletin->valid_bitmap & (1 << VLAN_VALID))
+                       /* vlan configured by ndo so its in bulletin board */
+                       memcpy(&ivi->vlan, &bulletin->vlan, VLAN_HLEN);
+               else
+                       /* funtion has not been loaded yet. Show vlans as 0s */
+                       memset(&ivi->vlan, 0, VLAN_HLEN);
+       }
+
+       return 0;
 }
 
 /* New mac for VF. Consider these cases:
@@ -3044,23 +3171,19 @@ void bnx2x_enable_sriov(struct bnx2x *bp)
  * VF to configure any mac for itself except for this mac. In case of a race
  * where the VF fails to see the new post on its bulletin board before sending a
  * mac configuration request, the PF will simply fail the request and VF can try
- * again after consulting its bulletin board
+ * again after consulting its bulletin board.
  */
-int bnx2x_set_vf_mac(struct net_device *dev, int queue, u8 *mac)
+int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac)
 {
        struct bnx2x *bp = netdev_priv(dev);
-       int rc, q_logical_state, vfidx = queue;
+       int rc, q_logical_state;
        struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
        struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
 
-       /* if SRIOV is disabled there is nothing to do (and somewhere, someone
-        * has erred).
-        */
-       if (!IS_SRIOV(bp)) {
-               BNX2X_ERR("bnx2x_set_vf_mac called though sriov is disabled\n");
-               return -EINVAL;
-       }
-
+       /* sanity */
+       rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
+       if (rc)
+               return rc;
        if (!is_valid_ether_addr(mac)) {
                BNX2X_ERR("mac address invalid\n");
                return -EINVAL;
@@ -3085,7 +3208,7 @@ int bnx2x_set_vf_mac(struct net_device *dev, int queue, u8 *mac)
        if (vf->state == VF_ENABLED &&
            q_logical_state == BNX2X_Q_LOGICAL_STATE_ACTIVE) {
                /* configure the mac in device on this vf's queue */
-               unsigned long flags = 0;
+               unsigned long ramrod_flags = 0;
                struct bnx2x_vlan_mac_obj *mac_obj = &bnx2x_vfq(vf, 0, mac_obj);
 
                /* must lock vfpf channel to protect against vf flows */
@@ -3106,14 +3229,133 @@ int bnx2x_set_vf_mac(struct net_device *dev, int queue, u8 *mac)
                }
 
                /* configure the new mac to device */
-               __set_bit(RAMROD_COMP_WAIT, &flags);
+               __set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
                bnx2x_set_mac_one(bp, (u8 *)&bulletin->mac, mac_obj, true,
-                                 BNX2X_ETH_MAC, &flags);
+                                 BNX2X_ETH_MAC, &ramrod_flags);
 
                bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_MAC);
        }
 
-       return rc;
+       return 0;
+}
+
+int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
+{
+       struct bnx2x *bp = netdev_priv(dev);
+       int rc, q_logical_state;
+       struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
+       struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
+
+       /* sanity */
+       rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
+       if (rc)
+               return rc;
+
+       if (vlan > 4095) {
+               BNX2X_ERR("illegal vlan value %d\n", vlan);
+               return -EINVAL;
+       }
+
+       DP(BNX2X_MSG_IOV, "configuring VF %d with VLAN %d qos %d\n",
+          vfidx, vlan, 0);
+
+       /* update PF's copy of the VF's bulletin. No point in posting the vlan
+        * to the VF since it doesn't have anything to do with it. But it useful
+        * to store it here in case the VF is not up yet and we can only
+        * configure the vlan later when it does.
+        */
+       bulletin->valid_bitmap |= 1 << VLAN_VALID;
+       bulletin->vlan = vlan;
+
+       /* is vf initialized and queue set up? */
+       q_logical_state =
+               bnx2x_get_q_logical_state(bp, &bnx2x_vfq(vf, 0, sp_obj));
+       if (vf->state == VF_ENABLED &&
+           q_logical_state == BNX2X_Q_LOGICAL_STATE_ACTIVE) {
+               /* configure the vlan in device on this vf's queue */
+               unsigned long ramrod_flags = 0;
+               unsigned long vlan_mac_flags = 0;
+               struct bnx2x_vlan_mac_obj *vlan_obj =
+                       &bnx2x_vfq(vf, 0, vlan_obj);
+               struct bnx2x_vlan_mac_ramrod_params ramrod_param;
+               struct bnx2x_queue_state_params q_params = {NULL};
+               struct bnx2x_queue_update_params *update_params;
+
+               memset(&ramrod_param, 0, sizeof(ramrod_param));
+
+               /* must lock vfpf channel to protect against vf flows */
+               bnx2x_lock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_VLAN);
+
+               /* remove existing vlans */
+               __set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
+               rc = vlan_obj->delete_all(bp, vlan_obj, &vlan_mac_flags,
+                                         &ramrod_flags);
+               if (rc) {
+                       BNX2X_ERR("failed to delete vlans\n");
+                       return -EINVAL;
+               }
+
+               /* send queue update ramrod to configure default vlan and silent
+                * vlan removal
+                */
+               __set_bit(RAMROD_COMP_WAIT, &q_params.ramrod_flags);
+               q_params.cmd = BNX2X_Q_CMD_UPDATE;
+               q_params.q_obj = &bnx2x_vfq(vf, 0, sp_obj);
+               update_params = &q_params.params.update;
+               __set_bit(BNX2X_Q_UPDATE_DEF_VLAN_EN_CHNG,
+                         &update_params->update_flags);
+               __set_bit(BNX2X_Q_UPDATE_SILENT_VLAN_REM_CHNG,
+                         &update_params->update_flags);
+
+               if (vlan == 0) {
+                       /* if vlan is 0 then we want to leave the VF traffic
+                        * untagged, and leave the incoming traffic untouched
+                        * (i.e. do not remove any vlan tags).
+                        */
+                       __clear_bit(BNX2X_Q_UPDATE_DEF_VLAN_EN,
+                                   &update_params->update_flags);
+                       __clear_bit(BNX2X_Q_UPDATE_SILENT_VLAN_REM,
+                                   &update_params->update_flags);
+               } else {
+                       /* configure the new vlan to device */
+                       __set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
+                       ramrod_param.vlan_mac_obj = vlan_obj;
+                       ramrod_param.ramrod_flags = ramrod_flags;
+                       ramrod_param.user_req.u.vlan.vlan = vlan;
+                       ramrod_param.user_req.cmd = BNX2X_VLAN_MAC_ADD;
+                       rc = bnx2x_config_vlan_mac(bp, &ramrod_param);
+                       if (rc) {
+                               BNX2X_ERR("failed to configure vlan\n");
+                               return -EINVAL;
+                       }
+
+                       /* configure default vlan to vf queue and set silent
+                        * vlan removal (the vf remains unaware of this vlan).
+                        */
+                       update_params = &q_params.params.update;
+                       __set_bit(BNX2X_Q_UPDATE_DEF_VLAN_EN,
+                                 &update_params->update_flags);
+                       __set_bit(BNX2X_Q_UPDATE_SILENT_VLAN_REM,
+                                 &update_params->update_flags);
+                       update_params->def_vlan = vlan;
+               }
+
+               /* Update the Queue state */
+               rc = bnx2x_queue_state_change(bp, &q_params);
+               if (rc) {
+                       BNX2X_ERR("Failed to configure default VLAN\n");
+                       return rc;
+               }
+
+               /* clear the flag indicating that this VF needs its vlan
+                * (will only be set if the HV configured th Vlan before vf was
+                * and we were called because the VF came up later
+                */
+               vf->cfg_flags &= ~VF_CFG_VLAN;
+
+               bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_VLAN);
+       }
+       return 0;
 }
 
 /* crc is the first field in the bulletin board. compute the crc over the
@@ -3165,20 +3407,26 @@ enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp)
                memcpy(bp->dev->dev_addr, bulletin.mac, ETH_ALEN);
        }
 
+       /* the vlan in bulletin board is valid and is new */
+       if (bulletin.valid_bitmap & 1 << VLAN_VALID)
+               memcpy(&bulletin.vlan, &bp->old_bulletin.vlan, VLAN_HLEN);
+
        /* copy new bulletin board to bp */
        bp->old_bulletin = bulletin;
 
        return PFVF_BULLETIN_UPDATED;
 }
 
-void bnx2x_vf_map_doorbells(struct bnx2x *bp)
+void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
 {
        /* vf doorbells are embedded within the regview */
-       bp->doorbells = bp->regview + PXP_VF_ADDR_DB_START;
+       return bp->regview + PXP_VF_ADDR_DB_START;
 }
 
 int bnx2x_vf_pci_alloc(struct bnx2x *bp)
 {
+       mutex_init(&bp->vf2pf_mutex);
+
        /* allocate vf2pf mailbox for vf to pf channel */
        BNX2X_PCI_ALLOC(bp->vf2pf_mbox, &bp->vf2pf_mbox_mapping,
                        sizeof(struct bnx2x_vf_mbx_msg));
@@ -3196,3 +3444,26 @@ alloc_mem_err:
                       sizeof(union pf_vf_bulletin));
        return -ENOMEM;
 }
+
+int bnx2x_open_epilog(struct bnx2x *bp)
+{
+       /* Enable sriov via delayed work. This must be done via delayed work
+        * because it causes the probe of the vf devices to be run, which invoke
+        * register_netdevice which must have rtnl lock taken. As we are holding
+        * the lock right now, that could only work if the probe would not take
+        * the lock. However, as the probe of the vf may be called from other
+        * contexts as well (such as passthrough to vm failes) it can't assume
+        * the lock is being held for it. Using delayed work here allows the
+        * probe code to simply take the lock (i.e. wait for it to be released
+        * if it is being held). We only want to do this if the number of VFs
+        * was set before PF driver was loaded.
+        */
+       if (IS_SRIOV(bp) && BNX2X_NR_VIRTFN(bp)) {
+               smp_mb__before_clear_bit();
+               set_bit(BNX2X_SP_RTNL_ENABLE_SRIOV, &bp->sp_rtnl_state);
+               smp_mb__after_clear_bit();
+               schedule_delayed_work(&bp->sp_rtnl_task, 0);
+       }
+
+       return 0;
+}
index b4050173add939a1ebbf164253b14b6b7e019947..d4b17b7a774e244c95b7690da027c99b0a89a6e7 100644 (file)
@@ -193,6 +193,7 @@ struct bnx2x_virtf {
 #define VF_CFG_TPA             0x0004
 #define VF_CFG_INT_SIMD                0x0008
 #define VF_CACHE_LINE          0x0010
+#define VF_CFG_VLAN            0x0020
 
        u8 state;
 #define VF_FREE                0       /* VF ready to be acquired holds no resc */
@@ -712,6 +713,7 @@ void bnx2x_add_tlv(struct bnx2x *bp, void *tlvs_list, u16 offset, u16 type,
                   u16 length);
 void bnx2x_vfpf_prep(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv,
                     u16 type, u16 length);
+void bnx2x_vfpf_finalize(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv);
 void bnx2x_dp_tlv_list(struct bnx2x *bp, void *tlvs_list);
 
 bool bnx2x_tlv_supported(u16 tlvtype);
@@ -750,13 +752,17 @@ static inline int bnx2x_vf_ustorm_prods_offset(struct bnx2x *bp,
 }
 
 enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp);
-void bnx2x_vf_map_doorbells(struct bnx2x *bp);
+void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp);
 int bnx2x_vf_pci_alloc(struct bnx2x *bp);
-void bnx2x_enable_sriov(struct bnx2x *bp);
+int bnx2x_enable_sriov(struct bnx2x *bp);
+void bnx2x_disable_sriov(struct bnx2x *bp);
 static inline int bnx2x_vf_headroom(struct bnx2x *bp)
 {
        return bp->vfdb->sriov.nr_virtfn * BNX2X_CLIENTS_PER_VF;
 }
+void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp);
+int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs);
+int bnx2x_open_epilog(struct bnx2x *bp);
 
 #else /* CONFIG_BNX2X_SRIOV */
 
@@ -779,7 +785,8 @@ static inline void bnx2x_iov_init_dmae(struct bnx2x *bp) {}
 static inline int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param,
                                     int num_vfs_param) {return 0; }
 static inline void bnx2x_iov_remove_one(struct bnx2x *bp) {}
-static inline void bnx2x_enable_sriov(struct bnx2x *bp) {}
+static inline int bnx2x_enable_sriov(struct bnx2x *bp) {return 0; }
+static inline void bnx2x_disable_sriov(struct bnx2x *bp) {}
 static inline int bnx2x_vfpf_acquire(struct bnx2x *bp,
                                     u8 tx_count, u8 rx_count) {return 0; }
 static inline int bnx2x_vfpf_release(struct bnx2x *bp) {return 0; }
@@ -802,8 +809,15 @@ static inline enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp
        return PFVF_BULLETIN_UNCHANGED;
 }
 
-static inline int bnx2x_vf_map_doorbells(struct bnx2x *bp) {return 0; }
+static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
+{
+       return NULL;
+}
+
 static inline int bnx2x_vf_pci_alloc(struct bnx2x *bp) {return 0; }
+static inline void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp) {}
+static inline int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs) {return 0; }
+static inline int bnx2x_open_epilog(struct bnx2x *bp) {return 0; }
 
 #endif /* CONFIG_BNX2X_SRIOV */
 #endif /* bnx2x_sriov.h */
index 4397f8b76f2ea79e23bbe730329e488ded73f749..2ca3d94fcec2ba5f5f11db647d4454ddeafa34ad 100644 (file)
@@ -1547,11 +1547,51 @@ static void bnx2x_prep_fw_stats_req(struct bnx2x *bp)
        }
 }
 
+void bnx2x_memset_stats(struct bnx2x *bp)
+{
+       int i;
+
+       /* function stats */
+       for_each_queue(bp, i) {
+               struct bnx2x_fp_stats *fp_stats = &bp->fp_stats[i];
+
+               memset(&fp_stats->old_tclient, 0,
+                      sizeof(fp_stats->old_tclient));
+               memset(&fp_stats->old_uclient, 0,
+                      sizeof(fp_stats->old_uclient));
+               memset(&fp_stats->old_xclient, 0,
+                      sizeof(fp_stats->old_xclient));
+               if (bp->stats_init) {
+                       memset(&fp_stats->eth_q_stats, 0,
+                              sizeof(fp_stats->eth_q_stats));
+                       memset(&fp_stats->eth_q_stats_old, 0,
+                              sizeof(fp_stats->eth_q_stats_old));
+               }
+       }
+
+       memset(&bp->dev->stats, 0, sizeof(bp->dev->stats));
+
+       if (bp->stats_init) {
+               memset(&bp->net_stats_old, 0, sizeof(bp->net_stats_old));
+               memset(&bp->fw_stats_old, 0, sizeof(bp->fw_stats_old));
+               memset(&bp->eth_stats_old, 0, sizeof(bp->eth_stats_old));
+               memset(&bp->eth_stats, 0, sizeof(bp->eth_stats));
+               memset(&bp->func_stats, 0, sizeof(bp->func_stats));
+       }
+
+       bp->stats_state = STATS_STATE_DISABLED;
+
+       if (bp->port.pmf && bp->port.port_stx)
+               bnx2x_port_stats_base_init(bp);
+
+       /* mark the end of statistics initializiation */
+       bp->stats_init = false;
+}
+
 void bnx2x_stats_init(struct bnx2x *bp)
 {
        int /*abs*/port = BP_PORT(bp);
        int mb_idx = BP_FW_MB_IDX(bp);
-       int i;
 
        bp->stats_pending = 0;
        bp->executer_idx = 0;
@@ -1587,36 +1627,11 @@ void bnx2x_stats_init(struct bnx2x *bp)
                            &(bp->port.old_nig_stats.egress_mac_pkt1_lo), 2);
        }
 
-       /* function stats */
-       for_each_queue(bp, i) {
-               struct bnx2x_fp_stats *fp_stats = &bp->fp_stats[i];
-
-               memset(&fp_stats->old_tclient, 0,
-                      sizeof(fp_stats->old_tclient));
-               memset(&fp_stats->old_uclient, 0,
-                      sizeof(fp_stats->old_uclient));
-               memset(&fp_stats->old_xclient, 0,
-                      sizeof(fp_stats->old_xclient));
-               if (bp->stats_init) {
-                       memset(&fp_stats->eth_q_stats, 0,
-                              sizeof(fp_stats->eth_q_stats));
-                       memset(&fp_stats->eth_q_stats_old, 0,
-                              sizeof(fp_stats->eth_q_stats_old));
-               }
-       }
-
        /* Prepare statistics ramrod data */
        bnx2x_prep_fw_stats_req(bp);
 
-       memset(&bp->dev->stats, 0, sizeof(bp->dev->stats));
+       /* Clean SP from previous statistics */
        if (bp->stats_init) {
-               memset(&bp->net_stats_old, 0, sizeof(bp->net_stats_old));
-               memset(&bp->fw_stats_old, 0, sizeof(bp->fw_stats_old));
-               memset(&bp->eth_stats_old, 0, sizeof(bp->eth_stats_old));
-               memset(&bp->eth_stats, 0, sizeof(bp->eth_stats));
-               memset(&bp->func_stats, 0, sizeof(bp->func_stats));
-
-               /* Clean SP from previous statistics */
                if (bp->func_stx) {
                        memset(bnx2x_sp(bp, func_stats), 0,
                               sizeof(struct host_func_stats));
@@ -1626,13 +1641,7 @@ void bnx2x_stats_init(struct bnx2x *bp)
                }
        }
 
-       bp->stats_state = STATS_STATE_DISABLED;
-
-       if (bp->port.pmf && bp->port.port_stx)
-               bnx2x_port_stats_base_init(bp);
-
-       /* mark the end of statistics initializiation */
-       bp->stats_init = false;
+       bnx2x_memset_stats(bp);
 }
 
 void bnx2x_save_statistics(struct bnx2x *bp)
index 198f6f1c9ad5a342736f0d51528539ee5f22f775..d117f472816c39effa4f48f4928f52ff373436ba 100644 (file)
@@ -540,8 +540,8 @@ struct bnx2x_fw_port_stats_old {
 /* forward */
 struct bnx2x;
 
+void bnx2x_memset_stats(struct bnx2x *bp);
 void bnx2x_stats_init(struct bnx2x *bp);
-
 void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event);
 
 /**
index 531eebf40d60b7b064043415b6c3ca44ef3e0b51..90fbf9cc2c2c78b8f2d1d044cba4abfdc835f0bf 100644 (file)
@@ -36,6 +36,8 @@ void bnx2x_add_tlv(struct bnx2x *bp, void *tlvs_list, u16 offset, u16 type,
 void bnx2x_vfpf_prep(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv,
                     u16 type, u16 length)
 {
+       mutex_lock(&bp->vf2pf_mutex);
+
        DP(BNX2X_MSG_IOV, "preparing to send %d tlv over vf pf channel\n",
           type);
 
@@ -49,6 +51,15 @@ void bnx2x_vfpf_prep(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv,
        first_tlv->resp_msg_offset = sizeof(bp->vf2pf_mbox->req);
 }
 
+/* releases the mailbox */
+void bnx2x_vfpf_finalize(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv)
+{
+       DP(BNX2X_MSG_IOV, "done sending [%d] tlv over vf pf channel\n",
+          first_tlv->tl.type);
+
+       mutex_unlock(&bp->vf2pf_mutex);
+}
+
 /* list the types and lengths of the tlvs on the buffer */
 void bnx2x_dp_tlv_list(struct bnx2x *bp, void *tlvs_list)
 {
@@ -181,8 +192,10 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
        /* clear mailbox and prep first tlv */
        bnx2x_vfpf_prep(bp, &req->first_tlv, CHANNEL_TLV_ACQUIRE, sizeof(*req));
 
-       if (bnx2x_get_vf_id(bp, &vf_id))
-               return -EAGAIN;
+       if (bnx2x_get_vf_id(bp, &vf_id)) {
+               rc = -EAGAIN;
+               goto out;
+       }
 
        req->vfdev_info.vf_id = vf_id;
        req->vfdev_info.vf_os = 0;
@@ -213,7 +226,7 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
 
                /* PF timeout */
                if (rc)
-                       return rc;
+                       goto out;
 
                /* copy acquire response from buffer to bp */
                memcpy(&bp->acquire_resp, resp, sizeof(bp->acquire_resp));
@@ -253,7 +266,8 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
                        /* PF reports error */
                        BNX2X_ERR("Failed to get the requested amount of resources: %d. Breaking...\n",
                                  bp->acquire_resp.hdr.status);
-                       return -EAGAIN;
+                       rc = -EAGAIN;
+                       goto out;
                }
        }
 
@@ -279,20 +293,24 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
                       bp->acquire_resp.resc.current_mac_addr,
                       ETH_ALEN);
 
-       return 0;
+out:
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
+       return rc;
 }
 
 int bnx2x_vfpf_release(struct bnx2x *bp)
 {
        struct vfpf_release_tlv *req = &bp->vf2pf_mbox->req.release;
        struct pfvf_general_resp_tlv *resp = &bp->vf2pf_mbox->resp.general_resp;
-       u32 rc = 0, vf_id;
+       u32 rc, vf_id;
 
        /* clear mailbox and prep first tlv */
        bnx2x_vfpf_prep(bp, &req->first_tlv, CHANNEL_TLV_RELEASE, sizeof(*req));
 
-       if (bnx2x_get_vf_id(bp, &vf_id))
-               return -EAGAIN;
+       if (bnx2x_get_vf_id(bp, &vf_id)) {
+               rc = -EAGAIN;
+               goto out;
+       }
 
        req->vf_id = vf_id;
 
@@ -308,7 +326,8 @@ int bnx2x_vfpf_release(struct bnx2x *bp)
 
        if (rc)
                /* PF timeout */
-               return rc;
+               goto out;
+
        if (resp->hdr.status == PFVF_STATUS_SUCCESS) {
                /* PF released us */
                DP(BNX2X_MSG_SP, "vf released\n");
@@ -316,10 +335,13 @@ int bnx2x_vfpf_release(struct bnx2x *bp)
                /* PF reports error */
                BNX2X_ERR("PF failed our release request - are we out of sync? response status: %d\n",
                          resp->hdr.status);
-               return -EAGAIN;
+               rc = -EAGAIN;
+               goto out;
        }
+out:
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
 
-       return 0;
+       return rc;
 }
 
 /* Tell PF about SB addresses */
@@ -350,16 +372,20 @@ int bnx2x_vfpf_init(struct bnx2x *bp)
 
        rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);
        if (rc)
-               return rc;
+               goto out;
 
        if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
                BNX2X_ERR("INIT VF failed: %d. Breaking...\n",
                          resp->hdr.status);
-               return -EAGAIN;
+               rc = -EAGAIN;
+               goto out;
        }
 
        DP(BNX2X_MSG_SP, "INIT VF Succeeded\n");
-       return 0;
+out:
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
+
+       return rc;
 }
 
 /* CLOSE VF - opposite to INIT_VF */
@@ -401,6 +427,8 @@ void bnx2x_vfpf_close_vf(struct bnx2x *bp)
                BNX2X_ERR("Sending CLOSE failed: pf response was %d\n",
                          resp->hdr.status);
 
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
+
 free_irq:
        /* Disable HW interrupts, NAPI */
        bnx2x_netif_stop(bp, 0);
@@ -435,7 +463,6 @@ int bnx2x_vfpf_setup_q(struct bnx2x *bp, int fp_idx)
        /* calculate queue flags */
        flags |= VFPF_QUEUE_FLG_STATS;
        flags |= VFPF_QUEUE_FLG_CACHE_ALIGN;
-       flags |= IS_MF_SD(bp) ? VFPF_QUEUE_FLG_OV : 0;
        flags |= VFPF_QUEUE_FLG_VLAN;
        DP(NETIF_MSG_IFUP, "vlan removal enabled\n");
 
@@ -486,8 +513,11 @@ int bnx2x_vfpf_setup_q(struct bnx2x *bp, int fp_idx)
        if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
                BNX2X_ERR("Status of SETUP_Q for queue[%d] is %d\n",
                          fp_idx, resp->hdr.status);
-               return -EINVAL;
+               rc = -EINVAL;
        }
+
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
+
        return rc;
 }
 
@@ -515,17 +545,19 @@ int bnx2x_vfpf_teardown_queue(struct bnx2x *bp, int qidx)
        if (rc) {
                BNX2X_ERR("Sending TEARDOWN for queue %d failed: %d\n", qidx,
                          rc);
-               return rc;
+               goto out;
        }
 
        /* PF failed the transaction */
        if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
                BNX2X_ERR("TEARDOWN for queue %d failed: %d\n", qidx,
                          resp->hdr.status);
-               return -EINVAL;
+               rc = -EINVAL;
        }
 
-       return 0;
+out:
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
+       return rc;
 }
 
 /* request pf to add a mac for the vf */
@@ -533,7 +565,7 @@ int bnx2x_vfpf_set_mac(struct bnx2x *bp)
 {
        struct vfpf_set_q_filters_tlv *req = &bp->vf2pf_mbox->req.set_q_filters;
        struct pfvf_general_resp_tlv *resp = &bp->vf2pf_mbox->resp.general_resp;
-       int rc;
+       int rc = 0;
 
        /* clear mailbox and prep first tlv */
        bnx2x_vfpf_prep(bp, &req->first_tlv, CHANNEL_TLV_SET_Q_FILTERS,
@@ -562,7 +594,7 @@ int bnx2x_vfpf_set_mac(struct bnx2x *bp)
        rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);
        if (rc) {
                BNX2X_ERR("failed to send message to pf. rc was %d\n", rc);
-               return rc;
+               goto out;
        }
 
        /* failure may mean PF was configured with a new mac for us */
@@ -587,8 +619,10 @@ int bnx2x_vfpf_set_mac(struct bnx2x *bp)
 
        if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
                BNX2X_ERR("vfpf SET MAC failed: %d\n", resp->hdr.status);
-               return -EINVAL;
+               rc = -EINVAL;
        }
+out:
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
 
        return 0;
 }
@@ -643,14 +677,16 @@ int bnx2x_vfpf_set_mcast(struct net_device *dev)
        rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);
        if (rc) {
                BNX2X_ERR("Sending a message failed: %d\n", rc);
-               return rc;
+               goto out;
        }
 
        if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
                BNX2X_ERR("Set Rx mode/multicast failed: %d\n",
                          resp->hdr.status);
-               return -EINVAL;
+               rc = -EINVAL;
        }
+out:
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
 
        return 0;
 }
@@ -689,7 +725,8 @@ int bnx2x_vfpf_storm_rx_mode(struct bnx2x *bp)
                break;
        default:
                BNX2X_ERR("BAD rx mode (%d)\n", mode);
-               return -EINVAL;
+               rc = -EINVAL;
+               goto out;
        }
 
        req->flags |= VFPF_SET_Q_FILTERS_RX_MASK_CHANGED;
@@ -708,8 +745,10 @@ int bnx2x_vfpf_storm_rx_mode(struct bnx2x *bp)
 
        if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
                BNX2X_ERR("Set Rx mode failed: %d\n", resp->hdr.status);
-               return -EINVAL;
+               rc = -EINVAL;
        }
+out:
+       bnx2x_vfpf_finalize(bp, &req->first_tlv);
 
        return rc;
 }
@@ -1004,7 +1043,7 @@ static void bnx2x_vf_mbx_init_vf(struct bnx2x *bp, struct bnx2x_virtf *vf,
 }
 
 /* convert MBX queue-flags to standard SP queue-flags */
-static void bnx2x_vf_mbx_set_q_flags(u32 mbx_q_flags,
+static void bnx2x_vf_mbx_set_q_flags(struct bnx2x *bp, u32 mbx_q_flags,
                                     unsigned long *sp_q_flags)
 {
        if (mbx_q_flags & VFPF_QUEUE_FLG_TPA)
@@ -1015,8 +1054,6 @@ static void bnx2x_vf_mbx_set_q_flags(u32 mbx_q_flags,
                __set_bit(BNX2X_Q_FLG_TPA_GRO, sp_q_flags);
        if (mbx_q_flags & VFPF_QUEUE_FLG_STATS)
                __set_bit(BNX2X_Q_FLG_STATS, sp_q_flags);
-       if (mbx_q_flags & VFPF_QUEUE_FLG_OV)
-               __set_bit(BNX2X_Q_FLG_OV, sp_q_flags);
        if (mbx_q_flags & VFPF_QUEUE_FLG_VLAN)
                __set_bit(BNX2X_Q_FLG_VLAN, sp_q_flags);
        if (mbx_q_flags & VFPF_QUEUE_FLG_COS)
@@ -1025,6 +1062,10 @@ static void bnx2x_vf_mbx_set_q_flags(u32 mbx_q_flags,
                __set_bit(BNX2X_Q_FLG_HC, sp_q_flags);
        if (mbx_q_flags & VFPF_QUEUE_FLG_DHC)
                __set_bit(BNX2X_Q_FLG_DHC, sp_q_flags);
+
+       /* outer vlan removal is set according to the PF's multi fuction mode */
+       if (IS_MF_SD(bp))
+               __set_bit(BNX2X_Q_FLG_OV, sp_q_flags);
 }
 
 static void bnx2x_vf_mbx_setup_q(struct bnx2x *bp, struct bnx2x_virtf *vf,
@@ -1075,11 +1116,11 @@ static void bnx2x_vf_mbx_setup_q(struct bnx2x *bp, struct bnx2x_virtf *vf,
                        init_p->tx.hc_rate = setup_q->txq.hc_rate;
                        init_p->tx.sb_cq_index = setup_q->txq.sb_index;
 
-                       bnx2x_vf_mbx_set_q_flags(setup_q->txq.flags,
+                       bnx2x_vf_mbx_set_q_flags(bp, setup_q->txq.flags,
                                                 &init_p->tx.flags);
 
                        /* tx setup - flags */
-                       bnx2x_vf_mbx_set_q_flags(setup_q->txq.flags,
+                       bnx2x_vf_mbx_set_q_flags(bp, setup_q->txq.flags,
                                                 &setup_p->flags);
 
                        /* tx setup - general, nothing */
@@ -1107,11 +1148,11 @@ static void bnx2x_vf_mbx_setup_q(struct bnx2x *bp, struct bnx2x_virtf *vf,
                        /* rx init */
                        init_p->rx.hc_rate = setup_q->rxq.hc_rate;
                        init_p->rx.sb_cq_index = setup_q->rxq.sb_index;
-                       bnx2x_vf_mbx_set_q_flags(setup_q->rxq.flags,
+                       bnx2x_vf_mbx_set_q_flags(bp, setup_q->rxq.flags,
                                                 &init_p->rx.flags);
 
                        /* rx setup - flags */
-                       bnx2x_vf_mbx_set_q_flags(setup_q->rxq.flags,
+                       bnx2x_vf_mbx_set_q_flags(bp, setup_q->rxq.flags,
                                                 &setup_p->flags);
 
                        /* rx setup - general */
index bfc80baec00d7564628f4088be05c47ae3f9a899..41708faab5752ac00f4f59edcceebf8545f59da3 100644 (file)
@@ -328,9 +328,15 @@ struct pf_vf_bulletin_content {
 #define MAC_ADDR_VALID         0       /* alert the vf that a new mac address
                                         * is available for it
                                         */
+#define VLAN_VALID             1       /* when set, the vf should not access
+                                        * the vfpf channel
+                                        */
 
        u8 mac[ETH_ALEN];
-       u8 padding[2];
+       u8 mac_padding[2];
+
+       u16 vlan;
+       u8 vlan_padding[6];
 };
 
 union pf_vf_bulletin {
@@ -353,6 +359,7 @@ enum channel_tlvs {
        CHANNEL_TLV_LIST_END,
        CHANNEL_TLV_FLR,
        CHANNEL_TLV_PF_SET_MAC,
+       CHANNEL_TLV_PF_SET_VLAN,
        CHANNEL_TLV_MAX
 };
 
index e9b35da375cb4c3b7bf5c31a1b03d18097ad44c1..e80bfb60c3efb922a76149ce81b9a120c60fe9ce 100644 (file)
@@ -831,11 +831,8 @@ static int sbdma_add_rcvbuffer(struct sbmac_softc *sc, struct sbmacdma *d,
                sb_new = netdev_alloc_skb(dev, ENET_PACKET_SIZE +
                                               SMP_CACHE_BYTES * 2 +
                                               NET_IP_ALIGN);
-               if (sb_new == NULL) {
-                       pr_info("%s: sk_buff allocation failed\n",
-                              d->sbdma_eth->sbm_dev->name);
+               if (sb_new == NULL)
                        return -ENOBUFS;
-               }
 
                sbdma_align_skb(sb_new, SMP_CACHE_BYTES, NET_IP_ALIGN);
        }
index 17a972734ba746d9218df4bf025218a91ebee947..ac83c87e0b1b3af379beff49782a66a1e155d61e 100644 (file)
@@ -94,10 +94,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
 
 #define DRV_MODULE_NAME                "tg3"
 #define TG3_MAJ_NUM                    3
-#define TG3_MIN_NUM                    130
+#define TG3_MIN_NUM                    131
 #define DRV_MODULE_VERSION     \
        __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
-#define DRV_MODULE_RELDATE     "February 14, 2013"
+#define DRV_MODULE_RELDATE     "April 09, 2013"
 
 #define RESET_KIND_SHUTDOWN    0
 #define RESET_KIND_INIT                1
@@ -212,6 +212,7 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
 #define TG3_FW_UPDATE_FREQ_SEC         (TG3_FW_UPDATE_TIMEOUT_SEC / 2)
 
 #define FIRMWARE_TG3           "tigon/tg3.bin"
+#define FIRMWARE_TG357766      "tigon/tg357766.bin"
 #define FIRMWARE_TG3TSO                "tigon/tg3_tso.bin"
 #define FIRMWARE_TG3TSO5       "tigon/tg3_tso5.bin"
 
@@ -1873,6 +1874,20 @@ static void tg3_link_report(struct tg3 *tp)
        tp->link_up = netif_carrier_ok(tp->dev);
 }
 
+static u32 tg3_decode_flowctrl_1000T(u32 adv)
+{
+       u32 flowctrl = 0;
+
+       if (adv & ADVERTISE_PAUSE_CAP) {
+               flowctrl |= FLOW_CTRL_RX;
+               if (!(adv & ADVERTISE_PAUSE_ASYM))
+                       flowctrl |= FLOW_CTRL_TX;
+       } else if (adv & ADVERTISE_PAUSE_ASYM)
+               flowctrl |= FLOW_CTRL_TX;
+
+       return flowctrl;
+}
+
 static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl)
 {
        u16 miireg;
@@ -1889,6 +1904,20 @@ static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl)
        return miireg;
 }
 
+static u32 tg3_decode_flowctrl_1000X(u32 adv)
+{
+       u32 flowctrl = 0;
+
+       if (adv & ADVERTISE_1000XPAUSE) {
+               flowctrl |= FLOW_CTRL_RX;
+               if (!(adv & ADVERTISE_1000XPSE_ASYM))
+                       flowctrl |= FLOW_CTRL_TX;
+       } else if (adv & ADVERTISE_1000XPSE_ASYM)
+               flowctrl |= FLOW_CTRL_TX;
+
+       return flowctrl;
+}
+
 static u8 tg3_resolve_flowctrl_1000X(u16 lcladv, u16 rmtadv)
 {
        u8 cap = 0;
@@ -2199,7 +2228,7 @@ static void tg3_phy_toggle_apd(struct tg3 *tp, bool enable)
        tg3_writephy(tp, MII_TG3_MISC_SHDW, reg);
 }
 
-static void tg3_phy_toggle_automdix(struct tg3 *tp, int enable)
+static void tg3_phy_toggle_automdix(struct tg3 *tp, bool enable)
 {
        u32 phy;
 
@@ -2291,7 +2320,7 @@ static void tg3_phy_apply_otp(struct tg3 *tp)
        tg3_phy_toggle_auxctl_smdsp(tp, false);
 }
 
-static void tg3_phy_eee_adjust(struct tg3 *tp, u32 current_link_up)
+static void tg3_phy_eee_adjust(struct tg3 *tp, bool current_link_up)
 {
        u32 val;
 
@@ -2301,7 +2330,7 @@ static void tg3_phy_eee_adjust(struct tg3 *tp, u32 current_link_up)
        tp->setlpicnt = 0;
 
        if (tp->link_config.autoneg == AUTONEG_ENABLE &&
-           current_link_up == 1 &&
+           current_link_up &&
            tp->link_config.active_duplex == DUPLEX_FULL &&
            (tp->link_config.active_speed == SPEED_100 ||
             tp->link_config.active_speed == SPEED_1000)) {
@@ -2323,7 +2352,7 @@ static void tg3_phy_eee_adjust(struct tg3 *tp, u32 current_link_up)
        }
 
        if (!tp->setlpicnt) {
-               if (current_link_up == 1 &&
+               if (current_link_up &&
                   !tg3_phy_toggle_auxctl_smdsp(tp, true)) {
                        tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, 0x0000);
                        tg3_phy_toggle_auxctl_smdsp(tp, false);
@@ -2530,6 +2559,13 @@ static void tg3_carrier_off(struct tg3 *tp)
        tp->link_up = false;
 }
 
+static void tg3_warn_mgmt_link_flap(struct tg3 *tp)
+{
+       if (tg3_flag(tp, ENABLE_ASF))
+               netdev_warn(tp->dev,
+                           "Management side-band traffic will be interrupted during phy settings change\n");
+}
+
 /* This will reset the tigon3 PHY if there is no valid
  * link unless the FORCE argument is non-zero.
  */
@@ -2669,7 +2705,7 @@ out:
        if (tg3_chip_rev_id(tp) == CHIPREV_ID_5762_A0)
                tg3_phydsp_write(tp, 0xffb, 0x4000);
 
-       tg3_phy_toggle_automdix(tp, 1);
+       tg3_phy_toggle_automdix(tp, true);
        tg3_phy_set_wirespeed(tp);
        return 0;
 }
@@ -2925,6 +2961,9 @@ static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power)
 {
        u32 val;
 
+       if (tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN)
+               return;
+
        if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES) {
                if (tg3_asic_rev(tp) == ASIC_REV_5704) {
                        u32 sg_dig_ctrl = tr32(SG_DIG_CTRL);
@@ -3448,11 +3487,58 @@ static int tg3_nvram_write_block(struct tg3 *tp, u32 offset, u32 len, u8 *buf)
 #define TX_CPU_SCRATCH_SIZE    0x04000
 
 /* tp->lock is held. */
-static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
+static int tg3_pause_cpu(struct tg3 *tp, u32 cpu_base)
 {
        int i;
+       const int iters = 10000;
 
-       BUG_ON(offset == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS));
+       for (i = 0; i < iters; i++) {
+               tw32(cpu_base + CPU_STATE, 0xffffffff);
+               tw32(cpu_base + CPU_MODE,  CPU_MODE_HALT);
+               if (tr32(cpu_base + CPU_MODE) & CPU_MODE_HALT)
+                       break;
+       }
+
+       return (i == iters) ? -EBUSY : 0;
+}
+
+/* tp->lock is held. */
+static int tg3_rxcpu_pause(struct tg3 *tp)
+{
+       int rc = tg3_pause_cpu(tp, RX_CPU_BASE);
+
+       tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
+       tw32_f(RX_CPU_BASE + CPU_MODE,  CPU_MODE_HALT);
+       udelay(10);
+
+       return rc;
+}
+
+/* tp->lock is held. */
+static int tg3_txcpu_pause(struct tg3 *tp)
+{
+       return tg3_pause_cpu(tp, TX_CPU_BASE);
+}
+
+/* tp->lock is held. */
+static void tg3_resume_cpu(struct tg3 *tp, u32 cpu_base)
+{
+       tw32(cpu_base + CPU_STATE, 0xffffffff);
+       tw32_f(cpu_base + CPU_MODE,  0x00000000);
+}
+
+/* tp->lock is held. */
+static void tg3_rxcpu_resume(struct tg3 *tp)
+{
+       tg3_resume_cpu(tp, RX_CPU_BASE);
+}
+
+/* tp->lock is held. */
+static int tg3_halt_cpu(struct tg3 *tp, u32 cpu_base)
+{
+       int rc;
+
+       BUG_ON(cpu_base == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS));
 
        if (tg3_asic_rev(tp) == ASIC_REV_5906) {
                u32 val = tr32(GRC_VCPU_EXT_CTRL);
@@ -3460,17 +3546,8 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
                tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_HALT_CPU);
                return 0;
        }
-       if (offset == RX_CPU_BASE) {
-               for (i = 0; i < 10000; i++) {
-                       tw32(offset + CPU_STATE, 0xffffffff);
-                       tw32(offset + CPU_MODE,  CPU_MODE_HALT);
-                       if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
-                               break;
-               }
-
-               tw32(offset + CPU_STATE, 0xffffffff);
-               tw32_f(offset + CPU_MODE,  CPU_MODE_HALT);
-               udelay(10);
+       if (cpu_base == RX_CPU_BASE) {
+               rc = tg3_rxcpu_pause(tp);
        } else {
                /*
                 * There is only an Rx CPU for the 5750 derivative in the
@@ -3479,17 +3556,12 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
                if (tg3_flag(tp, IS_SSB_CORE))
                        return 0;
 
-               for (i = 0; i < 10000; i++) {
-                       tw32(offset + CPU_STATE, 0xffffffff);
-                       tw32(offset + CPU_MODE,  CPU_MODE_HALT);
-                       if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
-                               break;
-               }
+               rc = tg3_txcpu_pause(tp);
        }
 
-       if (i >= 10000) {
+       if (rc) {
                netdev_err(tp->dev, "%s timed out, %s CPU\n",
-                          __func__, offset == RX_CPU_BASE ? "RX" : "TX");
+                          __func__, cpu_base == RX_CPU_BASE ? "RX" : "TX");
                return -ENODEV;
        }
 
@@ -3499,19 +3571,41 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
        return 0;
 }
 
-struct fw_info {
-       unsigned int fw_base;
-       unsigned int fw_len;
-       const __be32 *fw_data;
-};
+static int tg3_fw_data_len(struct tg3 *tp,
+                          const struct tg3_firmware_hdr *fw_hdr)
+{
+       int fw_len;
+
+       /* Non fragmented firmware have one firmware header followed by a
+        * contiguous chunk of data to be written. The length field in that
+        * header is not the length of data to be written but the complete
+        * length of the bss. The data length is determined based on
+        * tp->fw->size minus headers.
+        *
+        * Fragmented firmware have a main header followed by multiple
+        * fragments. Each fragment is identical to non fragmented firmware
+        * with a firmware header followed by a contiguous chunk of data. In
+        * the main header, the length field is unused and set to 0xffffffff.
+        * In each fragment header the length is the entire size of that
+        * fragment i.e. fragment data + header length. Data length is
+        * therefore length field in the header minus TG3_FW_HDR_LEN.
+        */
+       if (tp->fw_len == 0xffffffff)
+               fw_len = be32_to_cpu(fw_hdr->len);
+       else
+               fw_len = tp->fw->size;
+
+       return (fw_len - TG3_FW_HDR_LEN) / sizeof(u32);
+}
 
 /* tp->lock is held. */
 static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base,
                                 u32 cpu_scratch_base, int cpu_scratch_size,
-                                struct fw_info *info)
+                                const struct tg3_firmware_hdr *fw_hdr)
 {
-       int err, lock_err, i;
+       int err, i;
        void (*write_op)(struct tg3 *, u32, u32);
+       int total_len = tp->fw->size;
 
        if (cpu_base == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS)) {
                netdev_err(tp->dev,
@@ -3520,30 +3614,49 @@ static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base,
                return -EINVAL;
        }
 
-       if (tg3_flag(tp, 5705_PLUS))
+       if (tg3_flag(tp, 5705_PLUS) && tg3_asic_rev(tp) != ASIC_REV_57766)
                write_op = tg3_write_mem;
        else
                write_op = tg3_write_indirect_reg32;
 
-       /* It is possible that bootcode is still loading at this point.
-        * Get the nvram lock first before halting the cpu.
-        */
-       lock_err = tg3_nvram_lock(tp);
-       err = tg3_halt_cpu(tp, cpu_base);
-       if (!lock_err)
-               tg3_nvram_unlock(tp);
-       if (err)
-               goto out;
+       if (tg3_asic_rev(tp) != ASIC_REV_57766) {
+               /* It is possible that bootcode is still loading at this point.
+                * Get the nvram lock first before halting the cpu.
+                */
+               int lock_err = tg3_nvram_lock(tp);
+               err = tg3_halt_cpu(tp, cpu_base);
+               if (!lock_err)
+                       tg3_nvram_unlock(tp);
+               if (err)
+                       goto out;
 
-       for (i = 0; i < cpu_scratch_size; i += sizeof(u32))
-               write_op(tp, cpu_scratch_base + i, 0);
-       tw32(cpu_base + CPU_STATE, 0xffffffff);
-       tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT);
-       for (i = 0; i < (info->fw_len / sizeof(u32)); i++)
-               write_op(tp, (cpu_scratch_base +
-                             (info->fw_base & 0xffff) +
-                             (i * sizeof(u32))),
-                             be32_to_cpu(info->fw_data[i]));
+               for (i = 0; i < cpu_scratch_size; i += sizeof(u32))
+                       write_op(tp, cpu_scratch_base + i, 0);
+               tw32(cpu_base + CPU_STATE, 0xffffffff);
+               tw32(cpu_base + CPU_MODE,
+                    tr32(cpu_base + CPU_MODE) | CPU_MODE_HALT);
+       } else {
+               /* Subtract additional main header for fragmented firmware and
+                * advance to the first fragment
+                */
+               total_len -= TG3_FW_HDR_LEN;
+               fw_hdr++;
+       }
+
+       do {
+               u32 *fw_data = (u32 *)(fw_hdr + 1);
+               for (i = 0; i < tg3_fw_data_len(tp, fw_hdr); i++)
+                       write_op(tp, cpu_scratch_base +
+                                    (be32_to_cpu(fw_hdr->base_addr) & 0xffff) +
+                                    (i * sizeof(u32)),
+                                be32_to_cpu(fw_data[i]));
+
+               total_len -= be32_to_cpu(fw_hdr->len);
+
+               /* Advance to next fragment */
+               fw_hdr = (struct tg3_firmware_hdr *)
+                        ((void *)fw_hdr + be32_to_cpu(fw_hdr->len));
+       } while (total_len > 0);
 
        err = 0;
 
@@ -3551,14 +3664,34 @@ out:
        return err;
 }
 
+/* tp->lock is held. */
+static int tg3_pause_cpu_and_set_pc(struct tg3 *tp, u32 cpu_base, u32 pc)
+{
+       int i;
+       const int iters = 5;
+
+       tw32(cpu_base + CPU_STATE, 0xffffffff);
+       tw32_f(cpu_base + CPU_PC, pc);
+
+       for (i = 0; i < iters; i++) {
+               if (tr32(cpu_base + CPU_PC) == pc)
+                       break;
+               tw32(cpu_base + CPU_STATE, 0xffffffff);
+               tw32(cpu_base + CPU_MODE,  CPU_MODE_HALT);
+               tw32_f(cpu_base + CPU_PC, pc);
+               udelay(1000);
+       }
+
+       return (i == iters) ? -EBUSY : 0;
+}
+
 /* tp->lock is held. */
 static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp)
 {
-       struct fw_info info;
-       const __be32 *fw_data;
-       int err, i;
+       const struct tg3_firmware_hdr *fw_hdr;
+       int err;
 
-       fw_data = (void *)tp->fw->data;
+       fw_hdr = (struct tg3_firmware_hdr *)tp->fw->data;
 
        /* Firmware blob starts with version numbers, followed by
           start address and length. We are setting complete length.
@@ -3566,60 +3699,117 @@ static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp)
           Remainder is the blob to be loaded contiguously
           from start address. */
 
-       info.fw_base = be32_to_cpu(fw_data[1]);
-       info.fw_len = tp->fw->size - 12;
-       info.fw_data = &fw_data[3];
-
        err = tg3_load_firmware_cpu(tp, RX_CPU_BASE,
                                    RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE,
-                                   &info);
+                                   fw_hdr);
        if (err)
                return err;
 
        err = tg3_load_firmware_cpu(tp, TX_CPU_BASE,
                                    TX_CPU_SCRATCH_BASE, TX_CPU_SCRATCH_SIZE,
-                                   &info);
+                                   fw_hdr);
        if (err)
                return err;
 
        /* Now startup only the RX cpu. */
-       tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
-       tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
-
-       for (i = 0; i < 5; i++) {
-               if (tr32(RX_CPU_BASE + CPU_PC) == info.fw_base)
-                       break;
-               tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
-               tw32(RX_CPU_BASE + CPU_MODE,  CPU_MODE_HALT);
-               tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
-               udelay(1000);
-       }
-       if (i >= 5) {
+       err = tg3_pause_cpu_and_set_pc(tp, RX_CPU_BASE,
+                                      be32_to_cpu(fw_hdr->base_addr));
+       if (err) {
                netdev_err(tp->dev, "%s fails to set RX CPU PC, is %08x "
                           "should be %08x\n", __func__,
-                          tr32(RX_CPU_BASE + CPU_PC), info.fw_base);
+                          tr32(RX_CPU_BASE + CPU_PC),
+                               be32_to_cpu(fw_hdr->base_addr));
                return -ENODEV;
        }
-       tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
-       tw32_f(RX_CPU_BASE + CPU_MODE,  0x00000000);
+
+       tg3_rxcpu_resume(tp);
+
+       return 0;
+}
+
+static int tg3_validate_rxcpu_state(struct tg3 *tp)
+{
+       const int iters = 1000;
+       int i;
+       u32 val;
+
+       /* Wait for boot code to complete initialization and enter service
+        * loop. It is then safe to download service patches
+        */
+       for (i = 0; i < iters; i++) {
+               if (tr32(RX_CPU_HWBKPT) == TG3_SBROM_IN_SERVICE_LOOP)
+                       break;
+
+               udelay(10);
+       }
+
+       if (i == iters) {
+               netdev_err(tp->dev, "Boot code not ready for service patches\n");
+               return -EBUSY;
+       }
+
+       val = tg3_read_indirect_reg32(tp, TG3_57766_FW_HANDSHAKE);
+       if (val & 0xff) {
+               netdev_warn(tp->dev,
+                           "Other patches exist. Not downloading EEE patch\n");
+               return -EEXIST;
+       }
 
        return 0;
 }
 
+/* tp->lock is held. */
+static void tg3_load_57766_firmware(struct tg3 *tp)
+{
+       struct tg3_firmware_hdr *fw_hdr;
+
+       if (!tg3_flag(tp, NO_NVRAM))
+               return;
+
+       if (tg3_validate_rxcpu_state(tp))
+               return;
+
+       if (!tp->fw)
+               return;
+
+       /* This firmware blob has a different format than older firmware
+        * releases as given below. The main difference is we have fragmented
+        * data to be written to non-contiguous locations.
+        *
+        * In the beginning we have a firmware header identical to other
+        * firmware which consists of version, base addr and length. The length
+        * here is unused and set to 0xffffffff.
+        *
+        * This is followed by a series of firmware fragments which are
+        * individually identical to previous firmware. i.e. they have the
+        * firmware header and followed by data for that fragment. The version
+        * field of the individual fragment header is unused.
+        */
+
+       fw_hdr = (struct tg3_firmware_hdr *)tp->fw->data;
+       if (be32_to_cpu(fw_hdr->base_addr) != TG3_57766_FW_BASE_ADDR)
+               return;
+
+       if (tg3_rxcpu_pause(tp))
+               return;
+
+       /* tg3_load_firmware_cpu() will always succeed for the 57766 */
+       tg3_load_firmware_cpu(tp, 0, TG3_57766_FW_BASE_ADDR, 0, fw_hdr);
+
+       tg3_rxcpu_resume(tp);
+}
+
 /* tp->lock is held. */
 static int tg3_load_tso_firmware(struct tg3 *tp)
 {
-       struct fw_info info;
-       const __be32 *fw_data;
+       const struct tg3_firmware_hdr *fw_hdr;
        unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size;
-       int err, i;
+       int err;
 
-       if (tg3_flag(tp, HW_TSO_1) ||
-           tg3_flag(tp, HW_TSO_2) ||
-           tg3_flag(tp, HW_TSO_3))
+       if (!tg3_flag(tp, FW_TSO))
                return 0;
 
-       fw_data = (void *)tp->fw->data;
+       fw_hdr = (struct tg3_firmware_hdr *)tp->fw->data;
 
        /* Firmware blob starts with version numbers, followed by
           start address and length. We are setting complete length.
@@ -3627,10 +3817,7 @@ static int tg3_load_tso_firmware(struct tg3 *tp)
           Remainder is the blob to be loaded contiguously
           from start address. */
 
-       info.fw_base = be32_to_cpu(fw_data[1]);
        cpu_scratch_size = tp->fw_len;
-       info.fw_len = tp->fw->size - 12;
-       info.fw_data = &fw_data[3];
 
        if (tg3_asic_rev(tp) == ASIC_REV_5705) {
                cpu_base = RX_CPU_BASE;
@@ -3643,36 +3830,28 @@ static int tg3_load_tso_firmware(struct tg3 *tp)
 
        err = tg3_load_firmware_cpu(tp, cpu_base,
                                    cpu_scratch_base, cpu_scratch_size,
-                                   &info);
+                                   fw_hdr);
        if (err)
                return err;
 
        /* Now startup the cpu. */
-       tw32(cpu_base + CPU_STATE, 0xffffffff);
-       tw32_f(cpu_base + CPU_PC, info.fw_base);
-
-       for (i = 0; i < 5; i++) {
-               if (tr32(cpu_base + CPU_PC) == info.fw_base)
-                       break;
-               tw32(cpu_base + CPU_STATE, 0xffffffff);
-               tw32(cpu_base + CPU_MODE,  CPU_MODE_HALT);
-               tw32_f(cpu_base + CPU_PC, info.fw_base);
-               udelay(1000);
-       }
-       if (i >= 5) {
+       err = tg3_pause_cpu_and_set_pc(tp, cpu_base,
+                                      be32_to_cpu(fw_hdr->base_addr));
+       if (err) {
                netdev_err(tp->dev,
                           "%s fails to set CPU PC, is %08x should be %08x\n",
-                          __func__, tr32(cpu_base + CPU_PC), info.fw_base);
+                          __func__, tr32(cpu_base + CPU_PC),
+                          be32_to_cpu(fw_hdr->base_addr));
                return -ENODEV;
        }
-       tw32(cpu_base + CPU_STATE, 0xffffffff);
-       tw32_f(cpu_base + CPU_MODE,  0x00000000);
+
+       tg3_resume_cpu(tp, cpu_base);
        return 0;
 }
 
 
 /* tp->lock is held. */
-static void __tg3_set_mac_addr(struct tg3 *tp, int skip_mac_1)
+static void __tg3_set_mac_addr(struct tg3 *tp, bool skip_mac_1)
 {
        u32 addr_high, addr_low;
        int i;
@@ -3735,7 +3914,7 @@ static int tg3_power_up(struct tg3 *tp)
        return err;
 }
 
-static int tg3_setup_phy(struct tg3 *, int);
+static int tg3_setup_phy(struct tg3 *, bool);
 
 static int tg3_power_down_prepare(struct tg3 *tp)
 {
@@ -3807,7 +3986,7 @@ static int tg3_power_down_prepare(struct tg3 *tp)
                        tp->phy_flags |= TG3_PHYFLG_IS_LOW_POWER;
 
                if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES))
-                       tg3_setup_phy(tp, 0);
+                       tg3_setup_phy(tp, false);
        }
 
        if (tg3_asic_rev(tp) == ASIC_REV_5906) {
@@ -3848,7 +4027,13 @@ static int tg3_power_down_prepare(struct tg3 *tp)
 
                        if (tp->phy_flags & TG3_PHYFLG_MII_SERDES)
                                mac_mode = MAC_MODE_PORT_MODE_GMII;
-                       else
+                       else if (tp->phy_flags &
+                                TG3_PHYFLG_KEEP_LINK_ON_PWRDN) {
+                               if (tp->link_config.active_speed == SPEED_1000)
+                                       mac_mode = MAC_MODE_PORT_MODE_GMII;
+                               else
+                                       mac_mode = MAC_MODE_PORT_MODE_MII;
+                       } else
                                mac_mode = MAC_MODE_PORT_MODE_MII;
 
                        mac_mode |= tp->mac_mode & MAC_MODE_LINK_POLARITY;
@@ -4102,12 +4287,16 @@ static void tg3_phy_copper_begin(struct tg3 *tp)
            (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)) {
                u32 adv, fc;
 
-               if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
+               if ((tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) &&
+                   !(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN)) {
                        adv = ADVERTISED_10baseT_Half |
                              ADVERTISED_10baseT_Full;
                        if (tg3_flag(tp, WOL_SPEED_100MB))
                                adv |= ADVERTISED_100baseT_Half |
                                       ADVERTISED_100baseT_Full;
+                       if (tp->phy_flags & TG3_PHYFLG_1G_ON_VAUX_OK)
+                               adv |= ADVERTISED_1000baseT_Half |
+                                      ADVERTISED_1000baseT_Full;
 
                        fc = FLOW_CTRL_TX | FLOW_CTRL_RX;
                } else {
@@ -4121,6 +4310,15 @@ static void tg3_phy_copper_begin(struct tg3 *tp)
 
                tg3_phy_autoneg_cfg(tp, adv, fc);
 
+               if ((tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) &&
+                   (tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN)) {
+                       /* Normally during power down we want to autonegotiate
+                        * the lowest possible speed for WOL. However, to avoid
+                        * link flap, we leave it untouched.
+                        */
+                       return;
+               }
+
                tg3_writephy(tp, MII_BMCR,
                             BMCR_ANENABLE | BMCR_ANRESTART);
        } else {
@@ -4177,6 +4375,103 @@ static void tg3_phy_copper_begin(struct tg3 *tp)
        }
 }
 
+static int tg3_phy_pull_config(struct tg3 *tp)
+{
+       int err;
+       u32 val;
+
+       err = tg3_readphy(tp, MII_BMCR, &val);
+       if (err)
+               goto done;
+
+       if (!(val & BMCR_ANENABLE)) {
+               tp->link_config.autoneg = AUTONEG_DISABLE;
+               tp->link_config.advertising = 0;
+               tg3_flag_clear(tp, PAUSE_AUTONEG);
+
+               err = -EIO;
+
+               switch (val & (BMCR_SPEED1000 | BMCR_SPEED100)) {
+               case 0:
+                       if (tp->phy_flags & TG3_PHYFLG_ANY_SERDES)
+                               goto done;
+
+                       tp->link_config.speed = SPEED_10;
+                       break;
+               case BMCR_SPEED100:
+                       if (tp->phy_flags & TG3_PHYFLG_ANY_SERDES)
+                               goto done;
+
+                       tp->link_config.speed = SPEED_100;
+                       break;
+               case BMCR_SPEED1000:
+                       if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY)) {
+                               tp->link_config.speed = SPEED_1000;
+                               break;
+                       }
+                       /* Fall through */
+               default:
+                       goto done;
+               }
+
+               if (val & BMCR_FULLDPLX)
+                       tp->link_config.duplex = DUPLEX_FULL;
+               else
+                       tp->link_config.duplex = DUPLEX_HALF;
+
+               tp->link_config.flowctrl = FLOW_CTRL_RX | FLOW_CTRL_TX;
+
+               err = 0;
+               goto done;
+       }
+
+       tp->link_config.autoneg = AUTONEG_ENABLE;
+       tp->link_config.advertising = ADVERTISED_Autoneg;
+       tg3_flag_set(tp, PAUSE_AUTONEG);
+
+       if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES)) {
+               u32 adv;
+
+               err = tg3_readphy(tp, MII_ADVERTISE, &val);
+               if (err)
+                       goto done;
+
+               adv = mii_adv_to_ethtool_adv_t(val & ADVERTISE_ALL);
+               tp->link_config.advertising |= adv | ADVERTISED_TP;
+
+               tp->link_config.flowctrl = tg3_decode_flowctrl_1000T(val);
+       } else {
+               tp->link_config.advertising |= ADVERTISED_FIBRE;
+       }
+
+       if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY)) {
+               u32 adv;
+
+               if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES)) {
+                       err = tg3_readphy(tp, MII_CTRL1000, &val);
+                       if (err)
+                               goto done;
+
+                       adv = mii_ctrl1000_to_ethtool_adv_t(val);
+               } else {
+                       err = tg3_readphy(tp, MII_ADVERTISE, &val);
+                       if (err)
+                               goto done;
+
+                       adv = tg3_decode_flowctrl_1000X(val);
+                       tp->link_config.flowctrl = adv;
+
+                       val &= (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL);
+                       adv = mii_adv_to_ethtool_adv_x(val);
+               }
+
+               tp->link_config.advertising |= adv;
+       }
+
+done:
+       return err;
+}
+
 static int tg3_init_5401phy_dsp(struct tg3 *tp)
 {
        int err;
@@ -4196,6 +4491,32 @@ static int tg3_init_5401phy_dsp(struct tg3 *tp)
        return err;
 }
 
+static bool tg3_phy_eee_config_ok(struct tg3 *tp)
+{
+       u32 val;
+       u32 tgtadv = 0;
+       u32 advertising = tp->link_config.advertising;
+
+       if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP))
+               return true;
+
+       if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val))
+               return false;
+
+       val &= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
+
+
+       if (advertising & ADVERTISED_100baseT_Full)
+               tgtadv |= MDIO_AN_EEE_ADV_100TX;
+       if (advertising & ADVERTISED_1000baseT_Full)
+               tgtadv |= MDIO_AN_EEE_ADV_1000T;
+
+       if (val != tgtadv)
+               return false;
+
+       return true;
+}
+
 static bool tg3_phy_copper_an_config_ok(struct tg3 *tp, u32 *lcladv)
 {
        u32 advmsk, tgtadv, advertising;
@@ -4262,7 +4583,7 @@ static bool tg3_phy_copper_fetch_rmtadv(struct tg3 *tp, u32 *rmtadv)
        return true;
 }
 
-static bool tg3_test_and_report_link_chg(struct tg3 *tp, int curr_link_up)
+static bool tg3_test_and_report_link_chg(struct tg3 *tp, bool curr_link_up)
 {
        if (curr_link_up != tp->link_up) {
                if (curr_link_up) {
@@ -4280,23 +4601,28 @@ static bool tg3_test_and_report_link_chg(struct tg3 *tp, int curr_link_up)
        return false;
 }
 
-static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset)
+static void tg3_clear_mac_status(struct tg3 *tp)
+{
+       tw32(MAC_EVENT, 0);
+
+       tw32_f(MAC_STATUS,
+              MAC_STATUS_SYNC_CHANGED |
+              MAC_STATUS_CFG_CHANGED |
+              MAC_STATUS_MI_COMPLETION |
+              MAC_STATUS_LNKSTATE_CHANGED);
+       udelay(40);
+}
+
+static int tg3_setup_copper_phy(struct tg3 *tp, bool force_reset)
 {
-       int current_link_up;
+       bool current_link_up;
        u32 bmsr, val;
        u32 lcl_adv, rmt_adv;
        u16 current_speed;
        u8 current_duplex;
        int i, err;
 
-       tw32(MAC_EVENT, 0);
-
-       tw32_f(MAC_STATUS,
-            (MAC_STATUS_SYNC_CHANGED |
-             MAC_STATUS_CFG_CHANGED |
-             MAC_STATUS_MI_COMPLETION |
-             MAC_STATUS_LNKSTATE_CHANGED));
-       udelay(40);
+       tg3_clear_mac_status(tp);
 
        if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
                tw32_f(MAC_MI_MODE,
@@ -4316,7 +4642,7 @@ static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset)
                tg3_readphy(tp, MII_BMSR, &bmsr);
                if (!tg3_readphy(tp, MII_BMSR, &bmsr) &&
                    !(bmsr & BMSR_LSTATUS))
-                       force_reset = 1;
+                       force_reset = true;
        }
        if (force_reset)
                tg3_phy_reset(tp);
@@ -4380,7 +4706,7 @@ static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset)
                        tg3_writephy(tp, MII_TG3_EXT_CTRL, 0);
        }
 
-       current_link_up = 0;
+       current_link_up = false;
        current_speed = SPEED_UNKNOWN;
        current_duplex = DUPLEX_UNKNOWN;
        tp->phy_flags &= ~TG3_PHYFLG_MDIX_STATE;
@@ -4439,21 +4765,31 @@ static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset)
                tp->link_config.active_duplex = current_duplex;
 
                if (tp->link_config.autoneg == AUTONEG_ENABLE) {
+                       bool eee_config_ok = tg3_phy_eee_config_ok(tp);
+
                        if ((bmcr & BMCR_ANENABLE) &&
+                           eee_config_ok &&
                            tg3_phy_copper_an_config_ok(tp, &lcl_adv) &&
                            tg3_phy_copper_fetch_rmtadv(tp, &rmt_adv))
-                               current_link_up = 1;
+                               current_link_up = true;
+
+                       /* EEE settings changes take effect only after a phy
+                        * reset.  If we have skipped a reset due to Link Flap
+                        * Avoidance being enabled, do it now.
+                        */
+                       if (!eee_config_ok &&
+                           (tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
+                           !force_reset)
+                               tg3_phy_reset(tp);
                } else {
                        if (!(bmcr & BMCR_ANENABLE) &&
                            tp->link_config.speed == current_speed &&
-                           tp->link_config.duplex == current_duplex &&
-                           tp->link_config.flowctrl ==
-                           tp->link_config.active_flowctrl) {
-                               current_link_up = 1;
+                           tp->link_config.duplex == current_duplex) {
+                               current_link_up = true;
                        }
                }
 
-               if (current_link_up == 1 &&
+               if (current_link_up &&
                    tp->link_config.active_duplex == DUPLEX_FULL) {
                        u32 reg, bit;
 
@@ -4473,11 +4809,11 @@ static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset)
        }
 
 relink:
-       if (current_link_up == 0 || (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)) {
+       if (!current_link_up || (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)) {
                tg3_phy_copper_begin(tp);
 
                if (tg3_flag(tp, ROBOSWITCH)) {
-                       current_link_up = 1;
+                       current_link_up = true;
                        /* FIXME: when BCM5325 switch is used use 100 MBit/s */
                        current_speed = SPEED_1000;
                        current_duplex = DUPLEX_FULL;
@@ -4488,11 +4824,11 @@ relink:
                tg3_readphy(tp, MII_BMSR, &bmsr);
                if ((!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) ||
                    (tp->mac_mode & MAC_MODE_PORT_INT_LPBACK))
-                       current_link_up = 1;
+                       current_link_up = true;
        }
 
        tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK;
-       if (current_link_up == 1) {
+       if (current_link_up) {
                if (tp->link_config.active_speed == SPEED_100 ||
                    tp->link_config.active_speed == SPEED_10)
                        tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
@@ -4528,7 +4864,7 @@ relink:
                tp->mac_mode |= MAC_MODE_HALF_DUPLEX;
 
        if (tg3_asic_rev(tp) == ASIC_REV_5700) {
-               if (current_link_up == 1 &&
+               if (current_link_up &&
                    tg3_5700_link_polarity(tp, tp->link_config.active_speed))
                        tp->mac_mode |= MAC_MODE_LINK_POLARITY;
                else
@@ -4559,7 +4895,7 @@ relink:
        udelay(40);
 
        if (tg3_asic_rev(tp) == ASIC_REV_5700 &&
-           current_link_up == 1 &&
+           current_link_up &&
            tp->link_config.active_speed == SPEED_1000 &&
            (tg3_flag(tp, PCIX_MODE) || tg3_flag(tp, PCI_HIGH_SPEED))) {
                udelay(120);
@@ -4999,19 +5335,19 @@ static void tg3_init_bcm8002(struct tg3 *tp)
        tg3_writephy(tp, 0x10, 0x8011);
 }
 
-static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
+static bool tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
 {
        u16 flowctrl;
+       bool current_link_up;
        u32 sg_dig_ctrl, sg_dig_status;
        u32 serdes_cfg, expected_sg_dig_ctrl;
        int workaround, port_a;
-       int current_link_up;
 
        serdes_cfg = 0;
        expected_sg_dig_ctrl = 0;
        workaround = 0;
        port_a = 1;
-       current_link_up = 0;
+       current_link_up = false;
 
        if (tg3_chip_rev_id(tp) != CHIPREV_ID_5704_A0 &&
            tg3_chip_rev_id(tp) != CHIPREV_ID_5704_A1) {
@@ -5042,7 +5378,7 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
                }
                if (mac_status & MAC_STATUS_PCS_SYNCED) {
                        tg3_setup_flow_control(tp, 0, 0);
-                       current_link_up = 1;
+                       current_link_up = true;
                }
                goto out;
        }
@@ -5063,7 +5399,7 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
                                    MAC_STATUS_RCVD_CFG)) ==
                     MAC_STATUS_PCS_SYNCED)) {
                        tp->serdes_counter--;
-                       current_link_up = 1;
+                       current_link_up = true;
                        goto out;
                }
 restart_autoneg:
@@ -5098,7 +5434,7 @@ restart_autoneg:
                                           mii_adv_to_ethtool_adv_x(remote_adv);
 
                        tg3_setup_flow_control(tp, local_adv, remote_adv);
-                       current_link_up = 1;
+                       current_link_up = true;
                        tp->serdes_counter = 0;
                        tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT;
                } else if (!(sg_dig_status & SG_DIG_AUTONEG_COMPLETE)) {
@@ -5126,7 +5462,7 @@ restart_autoneg:
                                if ((mac_status & MAC_STATUS_PCS_SYNCED) &&
                                    !(mac_status & MAC_STATUS_RCVD_CFG)) {
                                        tg3_setup_flow_control(tp, 0, 0);
-                                       current_link_up = 1;
+                                       current_link_up = true;
                                        tp->phy_flags |=
                                                TG3_PHYFLG_PARALLEL_DETECT;
                                        tp->serdes_counter =
@@ -5144,9 +5480,9 @@ out:
        return current_link_up;
 }
 
-static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status)
+static bool tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status)
 {
-       int current_link_up = 0;
+       bool current_link_up = false;
 
        if (!(mac_status & MAC_STATUS_PCS_SYNCED))
                goto out;
@@ -5173,7 +5509,7 @@ static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status)
 
                        tg3_setup_flow_control(tp, local_adv, remote_adv);
 
-                       current_link_up = 1;
+                       current_link_up = true;
                }
                for (i = 0; i < 30; i++) {
                        udelay(20);
@@ -5188,15 +5524,15 @@ static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status)
                }
 
                mac_status = tr32(MAC_STATUS);
-               if (current_link_up == 0 &&
+               if (!current_link_up &&
                    (mac_status & MAC_STATUS_PCS_SYNCED) &&
                    !(mac_status & MAC_STATUS_RCVD_CFG))
-                       current_link_up = 1;
+                       current_link_up = true;
        } else {
                tg3_setup_flow_control(tp, 0, 0);
 
                /* Forcing 1000FD link up. */
-               current_link_up = 1;
+               current_link_up = true;
 
                tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS));
                udelay(40);
@@ -5209,13 +5545,13 @@ out:
        return current_link_up;
 }
 
-static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
+static int tg3_setup_fiber_phy(struct tg3 *tp, bool force_reset)
 {
        u32 orig_pause_cfg;
        u16 orig_active_speed;
        u8 orig_active_duplex;
        u32 mac_status;
-       int current_link_up;
+       bool current_link_up;
        int i;
 
        orig_pause_cfg = tp->link_config.active_flowctrl;
@@ -5252,7 +5588,7 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
        tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED);
        udelay(40);
 
-       current_link_up = 0;
+       current_link_up = false;
        tp->link_config.rmt_adv = 0;
        mac_status = tr32(MAC_STATUS);
 
@@ -5277,7 +5613,7 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
 
        mac_status = tr32(MAC_STATUS);
        if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) {
-               current_link_up = 0;
+               current_link_up = false;
                if (tp->link_config.autoneg == AUTONEG_ENABLE &&
                    tp->serdes_counter == 0) {
                        tw32_f(MAC_MODE, (tp->mac_mode |
@@ -5287,7 +5623,7 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
                }
        }
 
-       if (current_link_up == 1) {
+       if (current_link_up) {
                tp->link_config.active_speed = SPEED_1000;
                tp->link_config.active_duplex = DUPLEX_FULL;
                tw32(MAC_LED_CTRL, (tp->led_ctrl |
@@ -5312,33 +5648,63 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
        return 0;
 }
 
-static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
+static int tg3_setup_fiber_mii_phy(struct tg3 *tp, bool force_reset)
 {
-       int current_link_up, err = 0;
+       int err = 0;
        u32 bmsr, bmcr;
-       u16 current_speed;
-       u8 current_duplex;
-       u32 local_adv, remote_adv;
+       u16 current_speed = SPEED_UNKNOWN;
+       u8 current_duplex = DUPLEX_UNKNOWN;
+       bool current_link_up = false;
+       u32 local_adv, remote_adv, sgsr;
+
+       if ((tg3_asic_rev(tp) == ASIC_REV_5719 ||
+            tg3_asic_rev(tp) == ASIC_REV_5720) &&
+            !tg3_readphy(tp, SERDES_TG3_1000X_STATUS, &sgsr) &&
+            (sgsr & SERDES_TG3_SGMII_MODE)) {
+
+               if (force_reset)
+                       tg3_phy_reset(tp);
+
+               tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK;
+
+               if (!(sgsr & SERDES_TG3_LINK_UP)) {
+                       tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
+               } else {
+                       current_link_up = true;
+                       if (sgsr & SERDES_TG3_SPEED_1000) {
+                               current_speed = SPEED_1000;
+                               tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
+                       } else if (sgsr & SERDES_TG3_SPEED_100) {
+                               current_speed = SPEED_100;
+                               tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
+                       } else {
+                               current_speed = SPEED_10;
+                               tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
+                       }
+
+                       if (sgsr & SERDES_TG3_FULL_DUPLEX)
+                               current_duplex = DUPLEX_FULL;
+                       else
+                               current_duplex = DUPLEX_HALF;
+               }
+
+               tw32_f(MAC_MODE, tp->mac_mode);
+               udelay(40);
+
+               tg3_clear_mac_status(tp);
+
+               goto fiber_setup_done;
+       }
 
        tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
        tw32_f(MAC_MODE, tp->mac_mode);
        udelay(40);
 
-       tw32(MAC_EVENT, 0);
-
-       tw32_f(MAC_STATUS,
-            (MAC_STATUS_SYNC_CHANGED |
-             MAC_STATUS_CFG_CHANGED |
-             MAC_STATUS_MI_COMPLETION |
-             MAC_STATUS_LNKSTATE_CHANGED));
-       udelay(40);
+       tg3_clear_mac_status(tp);
 
        if (force_reset)
                tg3_phy_reset(tp);
 
-       current_link_up = 0;
-       current_speed = SPEED_UNKNOWN;
-       current_duplex = DUPLEX_UNKNOWN;
        tp->link_config.rmt_adv = 0;
 
        err |= tg3_readphy(tp, MII_BMSR, &bmsr);
@@ -5424,7 +5790,7 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
 
        if (bmsr & BMSR_LSTATUS) {
                current_speed = SPEED_1000;
-               current_link_up = 1;
+               current_link_up = true;
                if (bmcr & BMCR_FULLDPLX)
                        current_duplex = DUPLEX_FULL;
                else
@@ -5451,12 +5817,13 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
                        } else if (!tg3_flag(tp, 5780_CLASS)) {
                                /* Link is up via parallel detect */
                        } else {
-                               current_link_up = 0;
+                               current_link_up = false;
                        }
                }
        }
 
-       if (current_link_up == 1 && current_duplex == DUPLEX_FULL)
+fiber_setup_done:
+       if (current_link_up && current_duplex == DUPLEX_FULL)
                tg3_setup_flow_control(tp, local_adv, remote_adv);
 
        tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX;
@@ -5535,7 +5902,7 @@ static void tg3_serdes_parallel_detect(struct tg3 *tp)
        }
 }
 
-static int tg3_setup_phy(struct tg3 *tp, int force_reset)
+static int tg3_setup_phy(struct tg3 *tp, bool force_reset)
 {
        u32 val;
        int err;
@@ -6348,7 +6715,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
 
                if (desc->type_flags & RXD_FLAG_VLAN &&
                    !(tp->rx_mode & RX_MODE_KEEP_VLAN_TAG))
-                       __vlan_hwaccel_put_tag(skb,
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
                                               desc->err_vlan & RXD_VLAN_MASK);
 
                napi_gro_receive(&tnapi->napi, skb);
@@ -6436,7 +6803,7 @@ static void tg3_poll_link(struct tg3 *tp)
                                      MAC_STATUS_LNKSTATE_CHANGED));
                                udelay(40);
                        } else
-                               tg3_setup_phy(tp, 0);
+                               tg3_setup_phy(tp, false);
                        spin_unlock(&tp->lock);
                }
        }
@@ -7533,7 +7900,7 @@ static int tg3_phy_lpbk_set(struct tg3 *tp, u32 speed, bool extlpbk)
        u32 val, bmcr, mac_mode, ptest = 0;
 
        tg3_phy_toggle_apd(tp, false);
-       tg3_phy_toggle_automdix(tp, 0);
+       tg3_phy_toggle_automdix(tp, false);
 
        if (extlpbk && tg3_phy_set_extloopbk(tp))
                return -EIO;
@@ -7641,7 +8008,7 @@ static void tg3_set_loopback(struct net_device *dev, netdev_features_t features)
                spin_lock_bh(&tp->lock);
                tg3_mac_loopback(tp, false);
                /* Force link status check */
-               tg3_setup_phy(tp, 1);
+               tg3_setup_phy(tp, true);
                spin_unlock_bh(&tp->lock);
                netdev_info(dev, "Internal MAC loopback mode disabled.\n");
        }
@@ -8039,11 +8406,9 @@ static int tg3_mem_rx_acquire(struct tg3 *tp)
                tnapi->rx_rcb = dma_alloc_coherent(&tp->pdev->dev,
                                                   TG3_RX_RCB_RING_BYTES(tp),
                                                   &tnapi->rx_rcb_mapping,
-                                                  GFP_KERNEL);
+                                                  GFP_KERNEL | __GFP_ZERO);
                if (!tnapi->rx_rcb)
                        goto err_out;
-
-               memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp));
        }
 
        return 0;
@@ -8093,12 +8458,10 @@ static int tg3_alloc_consistent(struct tg3 *tp)
        tp->hw_stats = dma_alloc_coherent(&tp->pdev->dev,
                                          sizeof(struct tg3_hw_stats),
                                          &tp->stats_mapping,
-                                         GFP_KERNEL);
+                                         GFP_KERNEL | __GFP_ZERO);
        if (!tp->hw_stats)
                goto err_out;
 
-       memset(tp->hw_stats, 0, sizeof(struct tg3_hw_stats));
-
        for (i = 0; i < tp->irq_cnt; i++) {
                struct tg3_napi *tnapi = &tp->napi[i];
                struct tg3_hw_status *sblk;
@@ -8106,11 +8469,10 @@ static int tg3_alloc_consistent(struct tg3 *tp)
                tnapi->hw_status = dma_alloc_coherent(&tp->pdev->dev,
                                                      TG3_HW_STATUS_SIZE,
                                                      &tnapi->status_mapping,
-                                                     GFP_KERNEL);
+                                                     GFP_KERNEL | __GFP_ZERO);
                if (!tnapi->hw_status)
                        goto err_out;
 
-               memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE);
                sblk = tnapi->hw_status;
 
                if (tg3_flag(tp, ENABLE_RSS)) {
@@ -8157,7 +8519,7 @@ err_out:
 /* To stop a block, clear the enable bit and poll till it
  * clears.  tp->lock is held.
  */
-static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, u32 enable_bit, int silent)
+static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, u32 enable_bit, bool silent)
 {
        unsigned int i;
        u32 val;
@@ -8201,7 +8563,7 @@ static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, u32 enable_bit, int
 }
 
 /* tp->lock is held. */
-static int tg3_abort_hw(struct tg3 *tp, int silent)
+static int tg3_abort_hw(struct tg3 *tp, bool silent)
 {
        int i, err;
 
@@ -8561,6 +8923,9 @@ static int tg3_chip_reset(struct tg3 *tp)
 
        /* Reprobe ASF enable state.  */
        tg3_flag_clear(tp, ENABLE_ASF);
+       tp->phy_flags &= ~(TG3_PHYFLG_1G_ON_VAUX_OK |
+                          TG3_PHYFLG_KEEP_LINK_ON_PWRDN);
+
        tg3_flag_clear(tp, ASF_NEW_HANDSHAKE);
        tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val);
        if (val == NIC_SRAM_DATA_SIG_MAGIC) {
@@ -8572,6 +8937,12 @@ static int tg3_chip_reset(struct tg3 *tp)
                        tp->last_event_jiffies = jiffies;
                        if (tg3_flag(tp, 5750_PLUS))
                                tg3_flag_set(tp, ASF_NEW_HANDSHAKE);
+
+                       tg3_read_mem(tp, NIC_SRAM_DATA_CFG_3, &nic_cfg);
+                       if (nic_cfg & NIC_SRAM_1G_ON_VAUX_OK)
+                               tp->phy_flags |= TG3_PHYFLG_1G_ON_VAUX_OK;
+                       if (nic_cfg & NIC_SRAM_LNK_FLAP_AVOID)
+                               tp->phy_flags |= TG3_PHYFLG_KEEP_LINK_ON_PWRDN;
                }
        }
 
@@ -8582,7 +8953,7 @@ static void tg3_get_nstats(struct tg3 *, struct rtnl_link_stats64 *);
 static void tg3_get_estats(struct tg3 *, struct tg3_ethtool_stats *);
 
 /* tp->lock is held. */
-static int tg3_halt(struct tg3 *tp, int kind, int silent)
+static int tg3_halt(struct tg3 *tp, int kind, bool silent)
 {
        int err;
 
@@ -8593,7 +8964,7 @@ static int tg3_halt(struct tg3 *tp, int kind, int silent)
        tg3_abort_hw(tp, silent);
        err = tg3_chip_reset(tp);
 
-       __tg3_set_mac_addr(tp, 0);
+       __tg3_set_mac_addr(tp, false);
 
        tg3_write_sig_legacy(tp, kind);
        tg3_write_sig_post_reset(tp, kind);
@@ -8617,7 +8988,8 @@ static int tg3_set_mac_addr(struct net_device *dev, void *p)
 {
        struct tg3 *tp = netdev_priv(dev);
        struct sockaddr *addr = p;
-       int err = 0, skip_mac_1 = 0;
+       int err = 0;
+       bool skip_mac_1 = false;
 
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
@@ -8638,7 +9010,7 @@ static int tg3_set_mac_addr(struct net_device *dev, void *p)
                /* Skip MAC addr 1 if ASF is using it. */
                if ((addr0_high != addr1_high || addr0_low != addr1_low) &&
                    !(addr1_high == 0 && addr1_low == 0))
-                       skip_mac_1 = 1;
+                       skip_mac_1 = true;
        }
        spin_lock_bh(&tp->lock);
        __tg3_set_mac_addr(tp, skip_mac_1);
@@ -9057,7 +9429,7 @@ static void tg3_rss_write_indir_tbl(struct tg3 *tp)
 }
 
 /* tp->lock is held. */
-static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
+static int tg3_reset_hw(struct tg3 *tp, bool reset_phy)
 {
        u32 val, rdmac_mode;
        int i, err, limit;
@@ -9106,6 +9478,12 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
                       TG3_CPMU_DBTMR2_TXIDXEQ_2047US);
        }
 
+       if ((tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
+           !(tp->phy_flags & TG3_PHYFLG_USER_CONFIGURED)) {
+               tg3_phy_pull_config(tp);
+               tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
+       }
+
        if (reset_phy)
                tg3_phy_reset(tp);
 
@@ -9444,7 +9822,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
        tg3_rings_reset(tp);
 
        /* Initialize MAC address and backoff seed. */
-       __tg3_set_mac_addr(tp, 0);
+       __tg3_set_mac_addr(tp, false);
 
        /* MTU + ethernet header + FCS + optional VLAN tag */
        tw32(MAC_RX_MTU_SIZE,
@@ -9781,6 +10159,13 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
                        return err;
        }
 
+       if (tg3_asic_rev(tp) == ASIC_REV_57766) {
+               /* Ignore any errors for the firmware download. If download
+                * fails, the device will operate with EEE disabled
+                */
+               tg3_load_57766_firmware(tp);
+       }
+
        if (tg3_flag(tp, TSO_CAPABLE)) {
                err = tg3_load_tso_firmware(tp);
                if (err)
@@ -9888,7 +10273,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
                if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)
                        tp->phy_flags &= ~TG3_PHYFLG_IS_LOW_POWER;
 
-               err = tg3_setup_phy(tp, 0);
+               err = tg3_setup_phy(tp, false);
                if (err)
                        return err;
 
@@ -9968,7 +10353,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
 /* Called at device open time to get the chip ready for
  * packet processing.  Invoked with tp->lock held.
  */
-static int tg3_init_hw(struct tg3 *tp, int reset_phy)
+static int tg3_init_hw(struct tg3 *tp, bool reset_phy)
 {
        tg3_switch_clocks(tp);
 
@@ -10229,7 +10614,7 @@ static void tg3_timer(unsigned long __opaque)
                                phy_event = 1;
 
                        if (phy_event)
-                               tg3_setup_phy(tp, 0);
+                               tg3_setup_phy(tp, false);
                } else if (tg3_flag(tp, POLL_SERDES)) {
                        u32 mac_stat = tr32(MAC_STATUS);
                        int need_setup = 0;
@@ -10252,7 +10637,7 @@ static void tg3_timer(unsigned long __opaque)
                                        tw32_f(MAC_MODE, tp->mac_mode);
                                        udelay(40);
                                }
-                               tg3_setup_phy(tp, 0);
+                               tg3_setup_phy(tp, false);
                        }
                } else if ((tp->phy_flags & TG3_PHYFLG_MII_SERDES) &&
                           tg3_flag(tp, 5780_CLASS)) {
@@ -10338,7 +10723,7 @@ static void tg3_timer_stop(struct tg3 *tp)
 /* Restart hardware after configuration changes, self-test, etc.
  * Invoked with tp->lock held.
  */
-static int tg3_restart_hw(struct tg3 *tp, int reset_phy)
+static int tg3_restart_hw(struct tg3 *tp, bool reset_phy)
        __releases(tp->lock)
        __acquires(tp->lock)
 {
@@ -10388,7 +10773,7 @@ static void tg3_reset_task(struct work_struct *work)
        }
 
        tg3_halt(tp, RESET_KIND_SHUTDOWN, 0);
-       err = tg3_init_hw(tp, 1);
+       err = tg3_init_hw(tp, true);
        if (err)
                goto out;
 
@@ -10558,7 +10943,7 @@ static int tg3_test_msi(struct tg3 *tp)
        tg3_full_lock(tp, 1);
 
        tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
-       err = tg3_init_hw(tp, 1);
+       err = tg3_init_hw(tp, true);
 
        tg3_full_unlock(tp);
 
@@ -10570,7 +10955,7 @@ static int tg3_test_msi(struct tg3 *tp)
 
 static int tg3_request_firmware(struct tg3 *tp)
 {
-       const __be32 *fw_data;
+       const struct tg3_firmware_hdr *fw_hdr;
 
        if (request_firmware(&tp->fw, tp->fw_needed, &tp->pdev->dev)) {
                netdev_err(tp->dev, "Failed to load firmware \"%s\"\n",
@@ -10578,15 +10963,15 @@ static int tg3_request_firmware(struct tg3 *tp)
                return -ENOENT;
        }
 
-       fw_data = (void *)tp->fw->data;
+       fw_hdr = (struct tg3_firmware_hdr *)tp->fw->data;
 
        /* Firmware blob starts with version numbers, followed by
         * start address and _full_ length including BSS sections
         * (which must be longer than the actual data, of course
         */
 
-       tp->fw_len = be32_to_cpu(fw_data[2]);   /* includes bss */
-       if (tp->fw_len < (tp->fw->size - 12)) {
+       tp->fw_len = be32_to_cpu(fw_hdr->len);  /* includes bss */
+       if (tp->fw_len < (tp->fw->size - TG3_FW_HDR_LEN)) {
                netdev_err(tp->dev, "bogus length %d in \"%s\"\n",
                           tp->fw_len, tp->fw_needed);
                release_firmware(tp->fw);
@@ -10885,7 +11270,15 @@ static int tg3_open(struct net_device *dev)
 
        if (tp->fw_needed) {
                err = tg3_request_firmware(tp);
-               if (tg3_chip_rev_id(tp) == CHIPREV_ID_5701_A0) {
+               if (tg3_asic_rev(tp) == ASIC_REV_57766) {
+                       if (err) {
+                               netdev_warn(tp->dev, "EEE capability disabled\n");
+                               tp->phy_flags &= ~TG3_PHYFLG_EEE_CAP;
+                       } else if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) {
+                               netdev_warn(tp->dev, "EEE capability restored\n");
+                               tp->phy_flags |= TG3_PHYFLG_EEE_CAP;
+                       }
+               } else if (tg3_chip_rev_id(tp) == CHIPREV_ID_5701_A0) {
                        if (err)
                                return err;
                } else if (err) {
@@ -10910,7 +11303,9 @@ static int tg3_open(struct net_device *dev)
 
        tg3_full_unlock(tp);
 
-       err = tg3_start(tp, true, true, true);
+       err = tg3_start(tp,
+                       !(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN),
+                       true, true);
        if (err) {
                tg3_frob_aux_power(tp, false);
                pci_set_power_state(tp->pdev, PCI_D3hot);
@@ -11416,8 +11811,12 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                tp->link_config.duplex = cmd->duplex;
        }
 
+       tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
+
+       tg3_warn_mgmt_link_flap(tp);
+
        if (netif_running(dev))
-               tg3_setup_phy(tp, 1);
+               tg3_setup_phy(tp, true);
 
        tg3_full_unlock(tp);
 
@@ -11494,6 +11893,8 @@ static int tg3_nway_reset(struct net_device *dev)
        if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES)
                return -EINVAL;
 
+       tg3_warn_mgmt_link_flap(tp);
+
        if (tg3_flag(tp, USE_PHYLIB)) {
                if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
                        return -EAGAIN;
@@ -11571,7 +11972,7 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e
 
        if (netif_running(dev)) {
                tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
-               err = tg3_restart_hw(tp, 1);
+               err = tg3_restart_hw(tp, false);
                if (!err)
                        tg3_netif_start(tp);
        }
@@ -11606,6 +12007,9 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
        struct tg3 *tp = netdev_priv(dev);
        int err = 0;
 
+       if (tp->link_config.autoneg == AUTONEG_ENABLE)
+               tg3_warn_mgmt_link_flap(tp);
+
        if (tg3_flag(tp, USE_PHYLIB)) {
                u32 newadv;
                struct phy_device *phydev;
@@ -11692,7 +12096,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
 
                if (netif_running(dev)) {
                        tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
-                       err = tg3_restart_hw(tp, 1);
+                       err = tg3_restart_hw(tp, false);
                        if (!err)
                                tg3_netif_start(tp);
                }
@@ -11700,6 +12104,8 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
                tg3_full_unlock(tp);
        }
 
+       tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
+
        return err;
 }
 
@@ -12760,7 +13166,7 @@ static int tg3_test_loopback(struct tg3 *tp, u64 *data, bool do_extlpbk)
                goto done;
        }
 
-       err = tg3_reset_hw(tp, 1);
+       err = tg3_reset_hw(tp, true);
        if (err) {
                data[TG3_MAC_LOOPB_TEST] = TG3_LOOPBACK_FAILED;
                data[TG3_PHY_LOOPB_TEST] = TG3_LOOPBACK_FAILED;
@@ -12927,7 +13333,7 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
                tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
                if (netif_running(dev)) {
                        tg3_flag_set(tp, INIT_COMPLETE);
-                       err2 = tg3_restart_hw(tp, 1);
+                       err2 = tg3_restart_hw(tp, true);
                        if (!err2)
                                tg3_netif_start(tp);
                }
@@ -13244,7 +13650,8 @@ static inline void tg3_set_mtu(struct net_device *dev, struct tg3 *tp,
 static int tg3_change_mtu(struct net_device *dev, int new_mtu)
 {
        struct tg3 *tp = netdev_priv(dev);
-       int err, reset_phy = 0;
+       int err;
+       bool reset_phy = false;
 
        if (new_mtu < TG3_MIN_MTU || new_mtu > TG3_MAX_MTU(tp))
                return -EINVAL;
@@ -13271,7 +13678,7 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu)
         * breaks all requests to 256 bytes.
         */
        if (tg3_asic_rev(tp) == ASIC_REV_57766)
-               reset_phy = 1;
+               reset_phy = true;
 
        err = tg3_restart_hw(tp, reset_phy);
 
@@ -13837,6 +14244,12 @@ static void tg3_get_5720_nvram_info(struct tg3 *tp)
                case FLASH_5762_EEPROM_LD:
                        nvmpinstrp = FLASH_5720_EEPROM_LD;
                        break;
+               case FLASH_5720VENDOR_M_ST_M45PE20:
+                       /* This pinstrap supports multiple sizes, so force it
+                        * to read the actual size from location 0xf0.
+                        */
+                       nvmpinstrp = FLASH_5720VENDOR_ST_45USPT;
+                       break;
                }
        }
 
@@ -14289,14 +14702,18 @@ static void tg3_get_eeprom_hw_cfg(struct tg3 *tp)
                    (cfg2 & NIC_SRAM_DATA_CFG_2_APD_EN))
                        tp->phy_flags |= TG3_PHYFLG_ENABLE_APD;
 
-               if (tg3_flag(tp, PCI_EXPRESS) &&
-                   tg3_asic_rev(tp) != ASIC_REV_5785 &&
-                   !tg3_flag(tp, 57765_PLUS)) {
+               if (tg3_flag(tp, PCI_EXPRESS)) {
                        u32 cfg3;
 
                        tg3_read_mem(tp, NIC_SRAM_DATA_CFG_3, &cfg3);
-                       if (cfg3 & NIC_SRAM_ASPM_DEBOUNCE)
+                       if (tg3_asic_rev(tp) != ASIC_REV_5785 &&
+                           !tg3_flag(tp, 57765_PLUS) &&
+                           (cfg3 & NIC_SRAM_ASPM_DEBOUNCE))
                                tg3_flag_set(tp, ASPM_WORKAROUND);
+                       if (cfg3 & NIC_SRAM_LNK_FLAP_AVOID)
+                               tp->phy_flags |= TG3_PHYFLG_KEEP_LINK_ON_PWRDN;
+                       if (cfg3 & NIC_SRAM_1G_ON_VAUX_OK)
+                               tp->phy_flags |= TG3_PHYFLG_1G_ON_VAUX_OK;
                }
 
                if (cfg4 & NIC_SRAM_RGMII_INBAND_DISABLE)
@@ -14450,6 +14867,12 @@ static int tg3_phy_probe(struct tg3 *tp)
                }
        }
 
+       if (!tg3_flag(tp, ENABLE_ASF) &&
+           !(tp->phy_flags & TG3_PHYFLG_ANY_SERDES) &&
+           !(tp->phy_flags & TG3_PHYFLG_10_100_ONLY))
+               tp->phy_flags &= ~(TG3_PHYFLG_1G_ON_VAUX_OK |
+                                  TG3_PHYFLG_KEEP_LINK_ON_PWRDN);
+
        if (tg3_flag(tp, USE_PHYLIB))
                return tg3_phy_init(tp);
 
@@ -14515,6 +14938,7 @@ static int tg3_phy_probe(struct tg3 *tp)
        if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES) &&
            (tg3_asic_rev(tp) == ASIC_REV_5719 ||
             tg3_asic_rev(tp) == ASIC_REV_5720 ||
+            tg3_asic_rev(tp) == ASIC_REV_57766 ||
             tg3_asic_rev(tp) == ASIC_REV_5762 ||
             (tg3_asic_rev(tp) == ASIC_REV_5717 &&
              tg3_chip_rev_id(tp) != CHIPREV_ID_5717_A0) ||
@@ -14524,7 +14948,8 @@ static int tg3_phy_probe(struct tg3 *tp)
 
        tg3_phy_init_link_config(tp);
 
-       if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES) &&
+       if (!(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
+           !(tp->phy_flags & TG3_PHYFLG_ANY_SERDES) &&
            !tg3_flag(tp, ENABLE_APE) &&
            !tg3_flag(tp, ENABLE_ASF)) {
                u32 bmsr, dummy;
@@ -15300,7 +15725,8 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
        } else if (tg3_asic_rev(tp) != ASIC_REV_5700 &&
                   tg3_asic_rev(tp) != ASIC_REV_5701 &&
                   tg3_chip_rev_id(tp) != CHIPREV_ID_5705_A0) {
-                       tg3_flag_set(tp, TSO_BUG);
+               tg3_flag_set(tp, FW_TSO);
+               tg3_flag_set(tp, TSO_BUG);
                if (tg3_asic_rev(tp) == ASIC_REV_5705)
                        tp->fw_needed = FIRMWARE_TG3TSO5;
                else
@@ -15311,7 +15737,7 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
        if (tg3_flag(tp, HW_TSO_1) ||
            tg3_flag(tp, HW_TSO_2) ||
            tg3_flag(tp, HW_TSO_3) ||
-           tp->fw_needed) {
+           tg3_flag(tp, FW_TSO)) {
                /* For firmware TSO, assume ASF is disabled.
                 * We'll disable TSO later if we discover ASF
                 * is enabled in tg3_get_eeprom_hw_cfg().
@@ -15326,6 +15752,9 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
        if (tg3_chip_rev_id(tp) == CHIPREV_ID_5701_A0)
                tp->fw_needed = FIRMWARE_TG3;
 
+       if (tg3_asic_rev(tp) == ASIC_REV_57766)
+               tp->fw_needed = FIRMWARE_TG357766;
+
        tp->irq_max = 1;
 
        if (tg3_flag(tp, 5750_PLUS)) {
@@ -15598,7 +16027,7 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
         */
        tg3_get_eeprom_hw_cfg(tp);
 
-       if (tp->fw_needed && tg3_flag(tp, ENABLE_ASF)) {
+       if (tg3_flag(tp, FW_TSO) && tg3_flag(tp, ENABLE_ASF)) {
                tg3_flag_clear(tp, TSO_CAPABLE);
                tg3_flag_clear(tp, TSO_BUG);
                tp->fw_needed = NULL;
@@ -15786,6 +16215,11 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
        udelay(50);
        tg3_nvram_init(tp);
 
+       /* If the device has an NVRAM, no need to load patch firmware */
+       if (tg3_asic_rev(tp) == ASIC_REV_57766 &&
+           !tg3_flag(tp, NO_NVRAM))
+               tp->fw_needed = NULL;
+
        grc_misc_cfg = tr32(GRC_MISC_CFG);
        grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK;
 
@@ -16144,7 +16578,7 @@ out:
 }
 
 static int tg3_do_test_dma(struct tg3 *tp, u32 *buf, dma_addr_t buf_dma,
-                          int size, int to_device)
+                          int size, bool to_device)
 {
        struct tg3_internal_buffer_desc test_desc;
        u32 sram_dma_descs;
@@ -16344,7 +16778,7 @@ static int tg3_test_dma(struct tg3 *tp)
                        p[i] = i;
 
                /* Send the buffer to the chip. */
-               ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 1);
+               ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, true);
                if (ret) {
                        dev_err(&tp->pdev->dev,
                                "%s: Buffer write failed. err = %d\n",
@@ -16367,7 +16801,7 @@ static int tg3_test_dma(struct tg3 *tp)
                }
 #endif
                /* Now read it back. */
-               ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 0);
+               ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, false);
                if (ret) {
                        dev_err(&tp->pdev->dev, "%s: Buffer read failed. "
                                "err = %d\n", __func__, ret);
@@ -16763,7 +17197,7 @@ static int tg3_init_one(struct pci_dev *pdev,
 
        tg3_init_bufmgr_config(tp);
 
-       features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+       features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 
        /* 5700 B0 chips do not support checksumming correctly due
         * to hardware bugs.
@@ -17048,7 +17482,7 @@ static int tg3_suspend(struct device *device)
                tg3_full_lock(tp, 0);
 
                tg3_flag_set(tp, INIT_COMPLETE);
-               err2 = tg3_restart_hw(tp, 1);
+               err2 = tg3_restart_hw(tp, true);
                if (err2)
                        goto out;
 
@@ -17082,7 +17516,8 @@ static int tg3_resume(struct device *device)
        tg3_full_lock(tp, 0);
 
        tg3_flag_set(tp, INIT_COMPLETE);
-       err = tg3_restart_hw(tp, 1);
+       err = tg3_restart_hw(tp,
+                            !(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN));
        if (err)
                goto out;
 
@@ -17098,15 +17533,9 @@ out:
 
        return err;
 }
+#endif /* CONFIG_PM_SLEEP */
 
 static SIMPLE_DEV_PM_OPS(tg3_pm_ops, tg3_suspend, tg3_resume);
-#define TG3_PM_OPS (&tg3_pm_ops)
-
-#else
-
-#define TG3_PM_OPS NULL
-
-#endif /* CONFIG_PM_SLEEP */
 
 /**
  * tg3_io_error_detected - called when PCI error is detected
@@ -17221,7 +17650,7 @@ static void tg3_io_resume(struct pci_dev *pdev)
 
        tg3_full_lock(tp, 0);
        tg3_flag_set(tp, INIT_COMPLETE);
-       err = tg3_restart_hw(tp, 1);
+       err = tg3_restart_hw(tp, true);
        if (err) {
                tg3_full_unlock(tp);
                netdev_err(netdev, "Cannot restart hardware after reset.\n");
@@ -17254,7 +17683,7 @@ static struct pci_driver tg3_driver = {
        .probe          = tg3_init_one,
        .remove         = tg3_remove_one,
        .err_handler    = &tg3_err_handler,
-       .driver.pm      = TG3_PM_OPS,
+       .driver.pm      = &tg3_pm_ops,
 };
 
 static int __init tg3_init(void)
index 8d7d4c2ab5d65151f3aff045f3e624ca2acdcede..9b2d3ac2474adda8e25398460f33d0abca38b05a 100644 (file)
 
 #define NIC_SRAM_DATA_CFG_3            0x00000d3c
 #define  NIC_SRAM_ASPM_DEBOUNCE                 0x00000002
+#define  NIC_SRAM_LNK_FLAP_AVOID        0x00400000
+#define  NIC_SRAM_1G_ON_VAUX_OK                 0x00800000
 
 #define NIC_SRAM_DATA_CFG_4            0x00000d60
 #define  NIC_SRAM_GMII_MODE             0x00000002
 #define  NIC_SRAM_MBUF_POOL_BASE5705   0x00010000
 #define  NIC_SRAM_MBUF_POOL_SIZE5705   0x0000e000
 
+#define TG3_SRAM_RXCPU_SCRATCH_BASE_57766      0x00030000
+#define  TG3_SRAM_RXCPU_SCRATCH_SIZE_57766      0x00010000
+#define TG3_57766_FW_BASE_ADDR                 0x00030000
+#define TG3_57766_FW_HANDSHAKE                 0x0003fccc
+#define TG3_SBROM_IN_SERVICE_LOOP              0x51
+
 #define TG3_SRAM_RX_STD_BDCACHE_SIZE_5700      128
 #define TG3_SRAM_RX_STD_BDCACHE_SIZE_5755      64
 #define TG3_SRAM_RX_STD_BDCACHE_SIZE_5906      32
 #define MII_TG3_FET_SHDW_AUXSTAT2      0x1b
 #define  MII_TG3_FET_SHDW_AUXSTAT2_APD 0x0020
 
+/* Serdes PHY Register Definitions */
+#define SERDES_TG3_1000X_STATUS                0x14
+#define  SERDES_TG3_SGMII_MODE          0x0001
+#define  SERDES_TG3_LINK_UP             0x0002
+#define  SERDES_TG3_FULL_DUPLEX                 0x0004
+#define  SERDES_TG3_SPEED_100           0x0008
+#define  SERDES_TG3_SPEED_1000          0x0010
 
 /* APE registers.  Accessible through BAR1 */
 #define TG3_APE_GPIO_MSG               0x0008
@@ -3009,17 +3024,18 @@ enum TG3_FLAGS {
        TG3_FLAG_JUMBO_CAPABLE,
        TG3_FLAG_CHIP_RESETTING,
        TG3_FLAG_INIT_COMPLETE,
-       TG3_FLAG_TSO_BUG,
        TG3_FLAG_MAX_RXPEND_64,
-       TG3_FLAG_TSO_CAPABLE,
        TG3_FLAG_PCI_EXPRESS, /* BCM5785 + pci_is_pcie() */
        TG3_FLAG_ASF_NEW_HANDSHAKE,
        TG3_FLAG_HW_AUTONEG,
        TG3_FLAG_IS_NIC,
        TG3_FLAG_FLASH,
+       TG3_FLAG_FW_TSO,
        TG3_FLAG_HW_TSO_1,
        TG3_FLAG_HW_TSO_2,
        TG3_FLAG_HW_TSO_3,
+       TG3_FLAG_TSO_CAPABLE,
+       TG3_FLAG_TSO_BUG,
        TG3_FLAG_ICH_WORKAROUND,
        TG3_FLAG_1SHOT_MSI,
        TG3_FLAG_NO_FWARE_REPORTED,
@@ -3064,6 +3080,13 @@ enum TG3_FLAGS {
        TG3_FLAG_NUMBER_OF_FLAGS,       /* Last entry in enum TG3_FLAGS */
 };
 
+struct tg3_firmware_hdr {
+       __be32 version; /* unused for fragments */
+       __be32 base_addr;
+       __be32 len;
+};
+#define TG3_FW_HDR_LEN         (sizeof(struct tg3_firmware_hdr))
+
 struct tg3 {
        /* begin "general, frequently-used members" cacheline section */
 
@@ -3267,6 +3290,7 @@ struct tg3 {
 #define TG3_PHYFLG_IS_LOW_POWER                0x00000001
 #define TG3_PHYFLG_IS_CONNECTED                0x00000002
 #define TG3_PHYFLG_USE_MI_INTERRUPT    0x00000004
+#define TG3_PHYFLG_USER_CONFIGURED     0x00000008
 #define TG3_PHYFLG_PHY_SERDES          0x00000010
 #define TG3_PHYFLG_MII_SERDES          0x00000020
 #define TG3_PHYFLG_ANY_SERDES          (TG3_PHYFLG_PHY_SERDES |        \
@@ -3284,6 +3308,8 @@ struct tg3 {
 #define TG3_PHYFLG_SERDES_PREEMPHASIS  0x00010000
 #define TG3_PHYFLG_PARALLEL_DETECT     0x00020000
 #define TG3_PHYFLG_EEE_CAP             0x00040000
+#define TG3_PHYFLG_1G_ON_VAUX_OK       0x00080000
+#define TG3_PHYFLG_KEEP_LINK_ON_PWRDN  0x00100000
 #define TG3_PHYFLG_MDIX_STATE          0x00200000
 
        u32                             led_ctrl;
index 3227fdde521b09323f6e4ba58188198052aeb70e..f2b73ffa91224b732459f2c3dd777449846c1331 100644 (file)
@@ -76,7 +76,7 @@ static void bfa_ioc_pf_disabled(struct bfa_ioc *ioc);
 static void bfa_ioc_pf_failed(struct bfa_ioc *ioc);
 static void bfa_ioc_pf_hwfailed(struct bfa_ioc *ioc);
 static void bfa_ioc_pf_fwmismatch(struct bfa_ioc *ioc);
-static void bfa_ioc_boot(struct bfa_ioc *ioc, u32 boot_type,
+static void bfa_ioc_boot(struct bfa_ioc *ioc, enum bfi_fwboot_type boot_type,
                         u32 boot_param);
 static u32 bfa_ioc_smem_pgnum(struct bfa_ioc *ioc, u32 fmaddr);
 static void bfa_ioc_get_adapter_serial_num(struct bfa_ioc *ioc,
index 7cce42dc2f20e2d4ce014380a48f583083899e1d..ce4a030d3d0cab9705e8715b6169a9031b0fd840 100644 (file)
@@ -610,7 +610,7 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget)
                rcb->rxq->rx_bytes += length;
 
                if (flags & BNA_CQ_EF_VLAN)
-                       __vlan_hwaccel_put_tag(skb, ntohs(cmpl->vlan_tag));
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(cmpl->vlan_tag));
 
                if (BNAD_RXBUF_IS_PAGE(unmap_q->type))
                        napi_gro_frags(&rx_ctrl->napi);
@@ -1264,9 +1264,8 @@ bnad_mem_alloc(struct bnad *bnad,
                        mem_info->mdl[i].len = mem_info->len;
                        mem_info->mdl[i].kva =
                                dma_alloc_coherent(&bnad->pcidev->dev,
-                                               mem_info->len, &dma_pa,
-                                               GFP_KERNEL);
-
+                                                  mem_info->len, &dma_pa,
+                                                  GFP_KERNEL);
                        if (mem_info->mdl[i].kva == NULL)
                                goto err_return;
 
@@ -3069,8 +3068,7 @@ bnad_change_mtu(struct net_device *netdev, int new_mtu)
 }
 
 static int
-bnad_vlan_rx_add_vid(struct net_device *netdev,
-                                unsigned short vid)
+bnad_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct bnad *bnad = netdev_priv(netdev);
        unsigned long flags;
@@ -3091,8 +3089,7 @@ bnad_vlan_rx_add_vid(struct net_device *netdev,
 }
 
 static int
-bnad_vlan_rx_kill_vid(struct net_device *netdev,
-                                 unsigned short vid)
+bnad_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct bnad *bnad = netdev_priv(netdev);
        unsigned long flags;
@@ -3171,14 +3168,14 @@ bnad_netdev_init(struct bnad *bnad, bool using_dac)
 
        netdev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM |
                NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
-               NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_TX;
+               NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_TX;
 
        netdev->vlan_features = NETIF_F_SG | NETIF_F_HIGHDMA |
                NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                NETIF_F_TSO | NETIF_F_TSO6;
 
        netdev->features |= netdev->hw_features |
-               NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
+               NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
 
        if (using_dac)
                netdev->features |= NETIF_F_HIGHDMA;
index 3becdb2deb46c8dbfb3faa4e2916e1cdf41bc6d3..cc9a185f0abbc03dfb9265a86a2d9fa13b07ba10 100644 (file)
@@ -47,22 +47,19 @@ static int at91ether_start(struct net_device *dev)
        int i;
 
        lp->rx_ring = dma_alloc_coherent(&lp->pdev->dev,
-                                       MAX_RX_DESCR * sizeof(struct macb_dma_desc),
-                                       &lp->rx_ring_dma, GFP_KERNEL);
-       if (!lp->rx_ring) {
-               netdev_err(dev, "unable to alloc rx ring DMA buffer\n");
+                                        (MAX_RX_DESCR *
+                                         sizeof(struct macb_dma_desc)),
+                                        &lp->rx_ring_dma, GFP_KERNEL);
+       if (!lp->rx_ring)
                return -ENOMEM;
-       }
 
        lp->rx_buffers = dma_alloc_coherent(&lp->pdev->dev,
-                                       MAX_RX_DESCR * MAX_RBUFF_SZ,
-                                       &lp->rx_buffers_dma, GFP_KERNEL);
+                                           MAX_RX_DESCR * MAX_RBUFF_SZ,
+                                           &lp->rx_buffers_dma, GFP_KERNEL);
        if (!lp->rx_buffers) {
-               netdev_err(dev, "unable to alloc rx data DMA buffer\n");
-
                dma_free_coherent(&lp->pdev->dev,
-                                       MAX_RX_DESCR * sizeof(struct macb_dma_desc),
-                                       lp->rx_ring, lp->rx_ring_dma);
+                                 MAX_RX_DESCR * sizeof(struct macb_dma_desc),
+                                 lp->rx_ring, lp->rx_ring_dma);
                lp->rx_ring = NULL;
                return -ENOMEM;
        }
@@ -209,7 +206,6 @@ static void at91ether_rx(struct net_device *dev)
                        netif_rx(skb);
                } else {
                        lp->stats.rx_dropped++;
-                       netdev_notice(dev, "Memory squeeze, dropping packet.\n");
                }
 
                if (lp->rx_ring[lp->rx_tail].ctrl & MACB_BIT(RX_MHASH_MATCH))
@@ -303,42 +299,7 @@ static const struct of_device_id at91ether_dt_ids[] = {
        { .compatible = "cdns,emac" },
        { /* sentinel */ }
 };
-
 MODULE_DEVICE_TABLE(of, at91ether_dt_ids);
-
-static int at91ether_get_phy_mode_dt(struct platform_device *pdev)
-{
-       struct device_node *np = pdev->dev.of_node;
-
-       if (np)
-               return of_get_phy_mode(np);
-
-       return -ENODEV;
-}
-
-static int at91ether_get_hwaddr_dt(struct macb *bp)
-{
-       struct device_node *np = bp->pdev->dev.of_node;
-
-       if (np) {
-               const char *mac = of_get_mac_address(np);
-               if (mac) {
-                       memcpy(bp->dev->dev_addr, mac, ETH_ALEN);
-                       return 0;
-               }
-       }
-
-       return -ENODEV;
-}
-#else
-static int at91ether_get_phy_mode_dt(struct platform_device *pdev)
-{
-       return -ENODEV;
-}
-static int at91ether_get_hwaddr_dt(struct macb *bp)
-{
-       return -ENODEV;
-}
 #endif
 
 /* Detect MAC & PHY and perform ethernet interface initialization */
@@ -352,6 +313,7 @@ static int __init at91ether_probe(struct platform_device *pdev)
        struct macb *lp;
        int res;
        u32 reg;
+       const char *mac;
 
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!regs)
@@ -403,11 +365,13 @@ static int __init at91ether_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
-       res = at91ether_get_hwaddr_dt(lp);
-       if (res < 0)
+       mac = of_get_mac_address(pdev->dev.of_node);
+       if (mac)
+               memcpy(lp->dev->dev_addr, mac, ETH_ALEN);
+       else
                macb_get_hwaddr(lp);
 
-       res = at91ether_get_phy_mode_dt(pdev);
+       res = of_get_phy_mode(pdev->dev.of_node);
        if (res < 0) {
                if (board_data && board_data->is_rmii)
                        lp->phy_interface = PHY_INTERFACE_MODE_RMII;
@@ -430,7 +394,8 @@ static int __init at91ether_probe(struct platform_device *pdev)
        if (res)
                goto err_disable_clock;
 
-       if (macb_mii_init(lp) != 0)
+       res = macb_mii_init(lp);
+       if (res)
                goto err_out_unregister_netdev;
 
        /* will be enabled in open() */
@@ -519,18 +484,7 @@ static struct platform_driver at91ether_driver = {
        },
 };
 
-static int __init at91ether_init(void)
-{
-       return platform_driver_probe(&at91ether_driver, at91ether_probe);
-}
-
-static void __exit at91ether_exit(void)
-{
-       platform_driver_unregister(&at91ether_driver);
-}
-
-module_init(at91ether_init)
-module_exit(at91ether_exit)
+module_platform_driver_probe(at91ether_driver, at91ether_probe);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("AT91RM9200 EMAC Ethernet driver");
index 79039439bfdc4bc809317dc15f3bb2bcd578e1f9..6be513deb17f69b73be4b321821e99d3ec7a17cd 100644 (file)
@@ -485,6 +485,8 @@ static void macb_tx_interrupt(struct macb *bp)
        status = macb_readl(bp, TSR);
        macb_writel(bp, TSR, status);
 
+       macb_writel(bp, ISR, MACB_BIT(TCOMP));
+
        netdev_vdbg(bp->dev, "macb_tx_interrupt status = 0x%03lx\n",
                (unsigned long)status);
 
@@ -736,6 +738,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
                         * now.
                         */
                        macb_writel(bp, IDR, MACB_RX_INT_FLAGS);
+                       macb_writel(bp, ISR, MACB_BIT(RCOMP));
 
                        if (napi_schedule_prep(&bp->napi)) {
                                netdev_vdbg(bp->dev, "scheduling RX softirq\n");
@@ -1054,6 +1057,7 @@ static void macb_configure_dma(struct macb *bp)
                dmacfg |= GEM_BF(RXBS, RX_BUFFER_SIZE / 64);
                dmacfg |= GEM_BF(FBLDO, 16);
                dmacfg |= GEM_BIT(TXPBMS) | GEM_BF(RXBMS, -1L);
+               dmacfg &= ~GEM_BIT(ENDIA);
                gem_writel(bp, DMACFG, dmacfg);
        }
 }
@@ -1472,41 +1476,7 @@ static const struct of_device_id macb_dt_ids[] = {
        { .compatible = "cdns,gem" },
        { /* sentinel */ }
 };
-
 MODULE_DEVICE_TABLE(of, macb_dt_ids);
-
-static int macb_get_phy_mode_dt(struct platform_device *pdev)
-{
-       struct device_node *np = pdev->dev.of_node;
-
-       if (np)
-               return of_get_phy_mode(np);
-
-       return -ENODEV;
-}
-
-static int macb_get_hwaddr_dt(struct macb *bp)
-{
-       struct device_node *np = bp->pdev->dev.of_node;
-       if (np) {
-               const char *mac = of_get_mac_address(np);
-               if (mac) {
-                       memcpy(bp->dev->dev_addr, mac, ETH_ALEN);
-                       return 0;
-               }
-       }
-
-       return -ENODEV;
-}
-#else
-static int macb_get_phy_mode_dt(struct platform_device *pdev)
-{
-       return -ENODEV;
-}
-static int macb_get_hwaddr_dt(struct macb *bp)
-{
-       return -ENODEV;
-}
 #endif
 
 static int __init macb_probe(struct platform_device *pdev)
@@ -1519,6 +1489,7 @@ static int __init macb_probe(struct platform_device *pdev)
        u32 config;
        int err = -ENXIO;
        struct pinctrl *pinctrl;
+       const char *mac;
 
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!regs) {
@@ -1557,14 +1528,14 @@ static int __init macb_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "failed to get macb_clk\n");
                goto err_out_free_dev;
        }
-       clk_enable(bp->pclk);
+       clk_prepare_enable(bp->pclk);
 
        bp->hclk = clk_get(&pdev->dev, "hclk");
        if (IS_ERR(bp->hclk)) {
                dev_err(&pdev->dev, "failed to get hclk\n");
                goto err_out_put_pclk;
        }
-       clk_enable(bp->hclk);
+       clk_prepare_enable(bp->hclk);
 
        bp->regs = ioremap(regs->start, resource_size(regs));
        if (!bp->regs) {
@@ -1592,11 +1563,13 @@ static int __init macb_probe(struct platform_device *pdev)
        config |= macb_dbw(bp);
        macb_writel(bp, NCFGR, config);
 
-       err = macb_get_hwaddr_dt(bp);
-       if (err < 0)
+       mac = of_get_mac_address(pdev->dev.of_node);
+       if (mac)
+               memcpy(bp->dev->dev_addr, mac, ETH_ALEN);
+       else
                macb_get_hwaddr(bp);
 
-       err = macb_get_phy_mode_dt(pdev);
+       err = of_get_phy_mode(pdev->dev.of_node);
        if (err < 0) {
                pdata = pdev->dev.platform_data;
                if (pdata && pdata->is_rmii)
@@ -1629,9 +1602,9 @@ static int __init macb_probe(struct platform_device *pdev)
                goto err_out_free_irq;
        }
 
-       if (macb_mii_init(bp) != 0) {
+       err = macb_mii_init(bp);
+       if (err)
                goto err_out_unregister_netdev;
-       }
 
        platform_set_drvdata(pdev, dev);
 
@@ -1654,9 +1627,9 @@ err_out_free_irq:
 err_out_iounmap:
        iounmap(bp->regs);
 err_out_disable_clocks:
-       clk_disable(bp->hclk);
+       clk_disable_unprepare(bp->hclk);
        clk_put(bp->hclk);
-       clk_disable(bp->pclk);
+       clk_disable_unprepare(bp->pclk);
 err_out_put_pclk:
        clk_put(bp->pclk);
 err_out_free_dev:
@@ -1683,9 +1656,9 @@ static int __exit macb_remove(struct platform_device *pdev)
                unregister_netdev(dev);
                free_irq(dev->irq, dev);
                iounmap(bp->regs);
-               clk_disable(bp->hclk);
+               clk_disable_unprepare(bp->hclk);
                clk_put(bp->hclk);
-               clk_disable(bp->pclk);
+               clk_disable_unprepare(bp->pclk);
                clk_put(bp->pclk);
                free_netdev(dev);
                platform_set_drvdata(pdev, NULL);
@@ -1703,8 +1676,8 @@ static int macb_suspend(struct platform_device *pdev, pm_message_t state)
        netif_carrier_off(netdev);
        netif_device_detach(netdev);
 
-       clk_disable(bp->hclk);
-       clk_disable(bp->pclk);
+       clk_disable_unprepare(bp->hclk);
+       clk_disable_unprepare(bp->pclk);
 
        return 0;
 }
@@ -1714,8 +1687,8 @@ static int macb_resume(struct platform_device *pdev)
        struct net_device *netdev = platform_get_drvdata(pdev);
        struct macb *bp = netdev_priv(netdev);
 
-       clk_enable(bp->pclk);
-       clk_enable(bp->hclk);
+       clk_prepare_enable(bp->pclk);
+       clk_prepare_enable(bp->hclk);
 
        netif_device_attach(netdev);
 
@@ -1737,18 +1710,7 @@ static struct platform_driver macb_driver = {
        },
 };
 
-static int __init macb_init(void)
-{
-       return platform_driver_probe(&macb_driver, macb_probe);
-}
-
-static void __exit macb_exit(void)
-{
-       platform_driver_unregister(&macb_driver);
-}
-
-module_init(macb_init);
-module_exit(macb_exit);
+module_platform_driver_probe(macb_driver, macb_probe);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cadence MACB/GEM Ethernet driver");
index 570908b935784204e6b85d7f44a81c5a499db409..993d703806885d9f3ebedd4f5c035bfdd03306d8 100644 (file)
 /* Bitfields in DMACFG. */
 #define GEM_FBLDO_OFFSET                       0
 #define GEM_FBLDO_SIZE                         5
+#define GEM_ENDIA_OFFSET                       7
+#define GEM_ENDIA_SIZE                         1
 #define GEM_RXBMS_OFFSET                       8
 #define GEM_RXBMS_SIZE                         2
 #define GEM_TXPBMS_OFFSET                      10
index b0ebc9f6d55e9844e1ebe9367e9340681cba2a72..791e5ff305d8a036600bc4c76ce94a0b4b1ad80a 100644 (file)
@@ -1886,12 +1886,9 @@ static int xgmac_resume(struct device *dev)
 
        return 0;
 }
+#endif /* CONFIG_PM_SLEEP */
 
 static SIMPLE_DEV_PM_OPS(xgmac_pm_ops, xgmac_suspend, xgmac_resume);
-#define XGMAC_PM_OPS (&xgmac_pm_ops)
-#else
-#define XGMAC_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
 
 static const struct of_device_id xgmac_of_match[] = {
        { .compatible = "calxeda,hb-xgmac", },
@@ -1906,7 +1903,7 @@ static struct platform_driver xgmac_driver = {
        },
        .probe = xgmac_probe,
        .remove = xgmac_remove,
-       .driver.pm = XGMAC_PM_OPS,
+       .driver.pm = &xgmac_pm_ops,
 };
 
 module_platform_driver(xgmac_driver);
index 20d2085f61c54639bb6baa6926b9a9a09bf8d82d..9624cfe7df57154a7377f34253e3d42801762d23 100644 (file)
@@ -856,10 +856,10 @@ static netdev_features_t t1_fix_features(struct net_device *dev,
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -869,7 +869,7 @@ static int t1_set_features(struct net_device *dev, netdev_features_t features)
        netdev_features_t changed = dev->features ^ features;
        struct adapter *adapter = dev->ml_priv;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                t1_vlan_mode(adapter, features);
 
        return 0;
@@ -1085,8 +1085,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                        netdev->features |= NETIF_F_HIGHDMA;
                if (vlan_tso_capable(adapter)) {
                        netdev->features |=
-                               NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-                       netdev->hw_features |= NETIF_F_HW_VLAN_RX;
+                               NETIF_F_HW_VLAN_CTAG_TX |
+                               NETIF_F_HW_VLAN_CTAG_RX;
+                       netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
 
                        /* T204: disable TSO */
                        if (!(is_T2(adapter)) || bi->port_number != 4) {
index 482976925154d1dad9d18eebeda003ce1088d2db..8061fb0ef7edd5b8b9d5bca8d8d6a3e270c5314a 100644 (file)
@@ -734,7 +734,7 @@ void t1_vlan_mode(struct adapter *adapter, netdev_features_t features)
 {
        struct sge *sge = adapter->sge;
 
-       if (features & NETIF_F_HW_VLAN_RX)
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
                sge->sge_control |= F_VLAN_XTRACT;
        else
                sge->sge_control &= ~F_VLAN_XTRACT;
@@ -835,7 +835,7 @@ static void refill_free_list(struct sge *sge, struct freelQ *q)
                struct sk_buff *skb;
                dma_addr_t mapping;
 
-               skb = alloc_skb(q->rx_buffer_size, GFP_ATOMIC);
+               skb = dev_alloc_skb(q->rx_buffer_size);
                if (!skb)
                        break;
 
@@ -1046,11 +1046,10 @@ static inline struct sk_buff *get_packet(struct pci_dev *pdev,
        const struct freelQ_ce *ce = &fl->centries[fl->cidx];
 
        if (len < copybreak) {
-               skb = alloc_skb(len + 2, GFP_ATOMIC);
+               skb = netdev_alloc_skb_ip_align(NULL, len);
                if (!skb)
                        goto use_orig_buf;
 
-               skb_reserve(skb, 2);    /* align IP header */
                skb_put(skb, len);
                pci_dma_sync_single_for_cpu(pdev,
                                            dma_unmap_addr(ce, dma_addr),
@@ -1387,7 +1386,7 @@ static void sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len)
 
        if (p->vlan_valid) {
                st->vlan_xtract++;
-               __vlan_hwaccel_put_tag(skb, ntohs(p->vlan));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(p->vlan));
        }
        netif_receive_skb(skb);
 }
index 2b5e62193ceaace56320adadfac566f29f6984b1..71497e835f4258bedc82896e12ead2a71778902d 100644 (file)
@@ -1181,14 +1181,15 @@ static void cxgb_vlan_mode(struct net_device *dev, netdev_features_t features)
 
        if (adapter->params.rev > 0) {
                t3_set_vlan_accel(adapter, 1 << pi->port_id,
-                                 features & NETIF_F_HW_VLAN_RX);
+                                 features & NETIF_F_HW_VLAN_CTAG_RX);
        } else {
                /* single control for all ports */
-               unsigned int i, have_vlans = features & NETIF_F_HW_VLAN_RX;
+               unsigned int i, have_vlans = features & NETIF_F_HW_VLAN_CTAG_RX;
 
                for_each_port(adapter, i)
                        have_vlans |=
-                               adapter->port[i]->features & NETIF_F_HW_VLAN_RX;
+                               adapter->port[i]->features &
+                               NETIF_F_HW_VLAN_CTAG_RX;
 
                t3_set_vlan_accel(adapter, 1, have_vlans);
        }
@@ -2563,10 +2564,10 @@ static netdev_features_t cxgb_fix_features(struct net_device *dev,
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -2575,7 +2576,7 @@ static int cxgb_set_features(struct net_device *dev, netdev_features_t features)
 {
        netdev_features_t changed = dev->features ^ features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                cxgb_vlan_mode(dev, features);
 
        return 0;
@@ -3288,8 +3289,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                netdev->mem_start = mmio_start;
                netdev->mem_end = mmio_start + mmio_len - 1;
                netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
-                       NETIF_F_TSO | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX;
-               netdev->features |= netdev->hw_features | NETIF_F_HW_VLAN_TX;
+                       NETIF_F_TSO | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX;
+               netdev->features |= netdev->hw_features |
+                                   NETIF_F_HW_VLAN_CTAG_TX;
                netdev->vlan_features |= netdev->features & VLAN_FEAT;
                if (pci_using_dac)
                        netdev->features |= NETIF_F_HIGHDMA;
index 4232767862b5857c33d324095e9d6ba1c2d0cc18..0c96e5fe99cc263461d210745c78cafc1123d4a7 100644 (file)
@@ -185,7 +185,7 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter,
                if (!memcmp(dev->dev_addr, mac, ETH_ALEN)) {
                        rcu_read_lock();
                        if (vlan && vlan != VLAN_VID_MASK) {
-                               dev = __vlan_find_dev_deep(dev, vlan);
+                               dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), vlan);
                        } else if (netif_is_bond_slave(dev)) {
                                struct net_device *upper_dev;
 
index 9d67eb794c4baf612fbbab2e2897235cef10a796..f12e6b85a653c3d345c775281a3b7b9ccacdf90f 100644 (file)
@@ -2030,7 +2030,7 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
 
        if (p->vlan_valid) {
                qs->port_stats[SGE_PSTAT_VLANEX]++;
-               __vlan_hwaccel_put_tag(skb, ntohs(p->vlan));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(p->vlan));
        }
        if (rq->polling) {
                if (lro)
@@ -2132,7 +2132,7 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
 
        if (cpl->vlan_valid) {
                qs->port_stats[SGE_PSTAT_VLANEX]++;
-               __vlan_hwaccel_put_tag(skb, ntohs(cpl->vlan));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(cpl->vlan));
        }
        napi_gro_frags(&qs->napi);
 }
index 6db997c78a5fa2cf3b281acb1ed2887f8b62aa72..681804b30a3fef09678b76b40b3cae54ebaa80d1 100644 (file)
 #define FW_VERSION_MINOR 1
 #define FW_VERSION_MICRO 0
 
+#define FW_VERSION_MAJOR_T5 0
+#define FW_VERSION_MINOR_T5 0
+#define FW_VERSION_MICRO_T5 0
+
 #define CH_WARN(adap, fmt, ...) dev_warn(adap->pdev_dev, fmt, ## __VA_ARGS__)
 
 enum {
@@ -66,7 +70,9 @@ enum {
 enum {
        MEM_EDC0,
        MEM_EDC1,
-       MEM_MC
+       MEM_MC,
+       MEM_MC0 = MEM_MC,
+       MEM_MC1
 };
 
 enum {
@@ -74,8 +80,10 @@ enum {
        MEMWIN0_BASE     = 0x1b800,
        MEMWIN1_APERTURE = 32768,
        MEMWIN1_BASE     = 0x28000,
+       MEMWIN1_BASE_T5  = 0x52000,
        MEMWIN2_APERTURE = 65536,
        MEMWIN2_BASE     = 0x30000,
+       MEMWIN2_BASE_T5  = 0x54000,
 };
 
 enum dev_master {
@@ -431,6 +439,7 @@ struct sge_txq {
        spinlock_t db_lock;
        int db_disabled;
        unsigned short db_pidx;
+       u64 udb;
 };
 
 struct sge_eth_txq {                /* state for an SGE Ethernet Tx queue */
@@ -504,13 +513,44 @@ struct sge {
 
 struct l2t_data;
 
+#define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision))
+#define CHELSIO_CHIP_VERSION(code) ((code) >> 4)
+#define CHELSIO_CHIP_RELEASE(code) ((code) & 0xf)
+
+#define CHELSIO_T4             0x4
+#define CHELSIO_T5             0x5
+
+enum chip_type {
+       T4_A1 = CHELSIO_CHIP_CODE(CHELSIO_T4, 0),
+       T4_A2 = CHELSIO_CHIP_CODE(CHELSIO_T4, 1),
+       T4_A3 = CHELSIO_CHIP_CODE(CHELSIO_T4, 2),
+       T4_FIRST_REV    = T4_A1,
+       T4_LAST_REV     = T4_A3,
+
+       T5_A1 = CHELSIO_CHIP_CODE(CHELSIO_T5, 0),
+       T5_FIRST_REV    = T5_A1,
+       T5_LAST_REV     = T5_A1,
+};
+
+#ifdef CONFIG_PCI_IOV
+
+/* T4 supports SRIOV on PF0-3 and T5 on PF0-7.  However, the Serial
+ * Configuration initialization for T5 only has SR-IOV functionality enabled
+ * on PF0-3 in order to simplify everything.
+ */
+#define NUM_OF_PF_WITH_SRIOV 4
+
+#endif
+
 struct adapter {
        void __iomem *regs;
+       void __iomem *bar2;
        struct pci_dev *pdev;
        struct device *pdev_dev;
        unsigned int mbox;
        unsigned int fn;
        unsigned int flags;
+       enum chip_type chip;
 
        int msg_enable;
 
@@ -673,6 +713,16 @@ enum {
        VLAN_REWRITE
 };
 
+static inline int is_t5(enum chip_type chip)
+{
+       return (chip >= T5_FIRST_REV && chip <= T5_LAST_REV);
+}
+
+static inline int is_t4(enum chip_type chip)
+{
+       return (chip >= T4_FIRST_REV && chip <= T4_LAST_REV);
+}
+
 static inline u32 t4_read_reg(struct adapter *adap, u32 reg_addr)
 {
        return readl(adap->regs + reg_addr);
@@ -858,7 +908,8 @@ int t4_config_rss_range(struct adapter *adapter, int mbox, unsigned int viid,
                        int start, int n, const u16 *rspq, unsigned int nrspq);
 int t4_config_glbl_rss(struct adapter *adapter, int mbox, unsigned int mode,
                       unsigned int flags);
-int t4_mc_read(struct adapter *adap, u32 addr, __be32 *data, u64 *parity);
+int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data,
+              u64 *parity);
 int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data,
                u64 *parity);
 
index e707e31abd817a93c9a8d49f7a4ca7d1ef55e32a..6a6a01af75fd6f105adb23b6bd92b8d01eb2d90b 100644 (file)
@@ -68,8 +68,8 @@
 #include "t4fw_api.h"
 #include "l2t.h"
 
-#define DRV_VERSION "1.3.0-ko"
-#define DRV_DESC "Chelsio T4 Network Driver"
+#define DRV_VERSION "2.0.0-ko"
+#define DRV_DESC "Chelsio T4/T5 Network Driver"
 
 /*
  * Max interrupt hold-off timer value in us.  Queues fall back to this value
@@ -229,11 +229,51 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = {
        CH_DEVICE(0x440a, 4),
        CH_DEVICE(0x440d, 4),
        CH_DEVICE(0x440e, 4),
+       CH_DEVICE(0x5001, 5),
+       CH_DEVICE(0x5002, 5),
+       CH_DEVICE(0x5003, 5),
+       CH_DEVICE(0x5004, 5),
+       CH_DEVICE(0x5005, 5),
+       CH_DEVICE(0x5006, 5),
+       CH_DEVICE(0x5007, 5),
+       CH_DEVICE(0x5008, 5),
+       CH_DEVICE(0x5009, 5),
+       CH_DEVICE(0x500A, 5),
+       CH_DEVICE(0x500B, 5),
+       CH_DEVICE(0x500C, 5),
+       CH_DEVICE(0x500D, 5),
+       CH_DEVICE(0x500E, 5),
+       CH_DEVICE(0x500F, 5),
+       CH_DEVICE(0x5010, 5),
+       CH_DEVICE(0x5011, 5),
+       CH_DEVICE(0x5012, 5),
+       CH_DEVICE(0x5013, 5),
+       CH_DEVICE(0x5401, 5),
+       CH_DEVICE(0x5402, 5),
+       CH_DEVICE(0x5403, 5),
+       CH_DEVICE(0x5404, 5),
+       CH_DEVICE(0x5405, 5),
+       CH_DEVICE(0x5406, 5),
+       CH_DEVICE(0x5407, 5),
+       CH_DEVICE(0x5408, 5),
+       CH_DEVICE(0x5409, 5),
+       CH_DEVICE(0x540A, 5),
+       CH_DEVICE(0x540B, 5),
+       CH_DEVICE(0x540C, 5),
+       CH_DEVICE(0x540D, 5),
+       CH_DEVICE(0x540E, 5),
+       CH_DEVICE(0x540F, 5),
+       CH_DEVICE(0x5410, 5),
+       CH_DEVICE(0x5411, 5),
+       CH_DEVICE(0x5412, 5),
+       CH_DEVICE(0x5413, 5),
        { 0, }
 };
 
 #define FW_FNAME "cxgb4/t4fw.bin"
+#define FW5_FNAME "cxgb4/t5fw.bin"
 #define FW_CFNAME "cxgb4/t4-config.txt"
+#define FW5_CFNAME "cxgb4/t5-config.txt"
 
 MODULE_DESCRIPTION(DRV_DESC);
 MODULE_AUTHOR("Chelsio Communications");
@@ -241,6 +281,7 @@ MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION(DRV_VERSION);
 MODULE_DEVICE_TABLE(pci, cxgb4_pci_tbl);
 MODULE_FIRMWARE(FW_FNAME);
+MODULE_FIRMWARE(FW5_FNAME);
 
 /*
  * Normally we're willing to become the firmware's Master PF but will be happy
@@ -319,7 +360,10 @@ static bool vf_acls;
 module_param(vf_acls, bool, 0644);
 MODULE_PARM_DESC(vf_acls, "if set enable virtualization L2 ACL enforcement");
 
-static unsigned int num_vf[4];
+/* Configure the number of PCI-E Virtual Function which are to be instantiated
+ * on SR-IOV Capable Physical Functions.
+ */
+static unsigned int num_vf[NUM_OF_PF_WITH_SRIOV];
 
 module_param_array(num_vf, uint, NULL, 0644);
 MODULE_PARM_DESC(num_vf, "number of VFs for each of PFs 0-3");
@@ -515,7 +559,7 @@ static int link_start(struct net_device *dev)
         * that step explicitly.
         */
        ret = t4_set_rxmode(pi->adapter, mb, pi->viid, dev->mtu, -1, -1, -1,
-                           !!(dev->features & NETIF_F_HW_VLAN_RX), true);
+                           !!(dev->features & NETIF_F_HW_VLAN_CTAG_RX), true);
        if (ret == 0) {
                ret = t4_change_mac(pi->adapter, mb, pi->viid,
                                    pi->xact_addr_filt, dev->dev_addr, true,
@@ -1002,21 +1046,36 @@ freeout:        t4_free_sge_resources(adap);
 static int upgrade_fw(struct adapter *adap)
 {
        int ret;
-       u32 vers;
+       u32 vers, exp_major;
        const struct fw_hdr *hdr;
        const struct firmware *fw;
        struct device *dev = adap->pdev_dev;
+       char *fw_file_name;
 
-       ret = request_firmware(&fw, FW_FNAME, dev);
+       switch (CHELSIO_CHIP_VERSION(adap->chip)) {
+       case CHELSIO_T4:
+               fw_file_name = FW_FNAME;
+               exp_major = FW_VERSION_MAJOR;
+               break;
+       case CHELSIO_T5:
+               fw_file_name = FW5_FNAME;
+               exp_major = FW_VERSION_MAJOR_T5;
+               break;
+       default:
+               dev_err(dev, "Unsupported chip type, %x\n", adap->chip);
+               return -EINVAL;
+       }
+
+       ret = request_firmware(&fw, fw_file_name, dev);
        if (ret < 0) {
-               dev_err(dev, "unable to load firmware image " FW_FNAME
-                       ", error %d\n", ret);
+               dev_err(dev, "unable to load firmware image %s, error %d\n",
+                       fw_file_name, ret);
                return ret;
        }
 
        hdr = (const struct fw_hdr *)fw->data;
        vers = ntohl(hdr->fw_ver);
-       if (FW_HDR_FW_VER_MAJOR_GET(vers) != FW_VERSION_MAJOR) {
+       if (FW_HDR_FW_VER_MAJOR_GET(vers) != exp_major) {
                ret = -EINVAL;              /* wrong major version, won't do */
                goto out;
        }
@@ -1024,18 +1083,15 @@ static int upgrade_fw(struct adapter *adap)
        /*
         * If the flash FW is unusable or we found something newer, load it.
         */
-       if (FW_HDR_FW_VER_MAJOR_GET(adap->params.fw_vers) != FW_VERSION_MAJOR ||
+       if (FW_HDR_FW_VER_MAJOR_GET(adap->params.fw_vers) != exp_major ||
            vers > adap->params.fw_vers) {
                dev_info(dev, "upgrading firmware ...\n");
                ret = t4_fw_upgrade(adap, adap->mbox, fw->data, fw->size,
                                    /*force=*/false);
                if (!ret)
-                       dev_info(dev, "firmware successfully upgraded to "
-                                FW_FNAME " (%d.%d.%d.%d)\n",
-                                FW_HDR_FW_VER_MAJOR_GET(vers),
-                                FW_HDR_FW_VER_MINOR_GET(vers),
-                                FW_HDR_FW_VER_MICRO_GET(vers),
-                                FW_HDR_FW_VER_BUILD_GET(vers));
+                       dev_info(dev,
+                                "firmware upgraded to version %pI4 from %s\n",
+                                &hdr->fw_ver, fw_file_name);
                else
                        dev_err(dev, "firmware upgrade failed! err=%d\n", -ret);
        } else {
@@ -1308,6 +1364,8 @@ static char stats_strings[][ETH_GSTRING_LEN] = {
        "VLANinsertions     ",
        "GROpackets         ",
        "GROmerged          ",
+       "WriteCoalSuccess   ",
+       "WriteCoalFail      ",
 };
 
 static int get_sset_count(struct net_device *dev, int sset)
@@ -1321,10 +1379,15 @@ static int get_sset_count(struct net_device *dev, int sset)
 }
 
 #define T4_REGMAP_SIZE (160 * 1024)
+#define T5_REGMAP_SIZE (332 * 1024)
 
 static int get_regs_len(struct net_device *dev)
 {
-       return T4_REGMAP_SIZE;
+       struct adapter *adap = netdev2adap(dev);
+       if (is_t4(adap->chip))
+               return T4_REGMAP_SIZE;
+       else
+               return T5_REGMAP_SIZE;
 }
 
 static int get_eeprom_len(struct net_device *dev)
@@ -1398,11 +1461,25 @@ static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
 {
        struct port_info *pi = netdev_priv(dev);
        struct adapter *adapter = pi->adapter;
+       u32 val1, val2;
 
        t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data);
 
        data += sizeof(struct port_stats) / sizeof(u64);
        collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data);
+       data += sizeof(struct queue_port_stats) / sizeof(u64);
+       if (!is_t4(adapter->chip)) {
+               t4_write_reg(adapter, SGE_STAT_CFG, STATSOURCE_T5(7));
+               val1 = t4_read_reg(adapter, SGE_STAT_TOTAL);
+               val2 = t4_read_reg(adapter, SGE_STAT_MATCH);
+               *data = val1 - val2;
+               data++;
+               *data = val2;
+               data++;
+       } else {
+               memset(data, 0, 2 * sizeof(u64));
+               *data += 2;
+       }
 }
 
 /*
@@ -1413,7 +1490,8 @@ static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
  */
 static inline unsigned int mk_adap_vers(const struct adapter *ap)
 {
-       return 4 | (ap->params.rev << 10) | (1 << 16);
+       return CHELSIO_CHIP_VERSION(ap->chip) |
+               (CHELSIO_CHIP_RELEASE(ap->chip) << 10) | (1 << 16);
 }
 
 static void reg_block_dump(struct adapter *ap, void *buf, unsigned int start,
@@ -1428,7 +1506,7 @@ static void reg_block_dump(struct adapter *ap, void *buf, unsigned int start,
 static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
                     void *buf)
 {
-       static const unsigned int reg_ranges[] = {
+       static const unsigned int t4_reg_ranges[] = {
                0x1008, 0x1108,
                0x1180, 0x11b4,
                0x11fc, 0x123c,
@@ -1648,13 +1726,452 @@ static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
                0x27e00, 0x27e04
        };
 
+       static const unsigned int t5_reg_ranges[] = {
+               0x1008, 0x1148,
+               0x1180, 0x11b4,
+               0x11fc, 0x123c,
+               0x1280, 0x173c,
+               0x1800, 0x18fc,
+               0x3000, 0x3028,
+               0x3060, 0x30d8,
+               0x30e0, 0x30fc,
+               0x3140, 0x357c,
+               0x35a8, 0x35cc,
+               0x35ec, 0x35ec,
+               0x3600, 0x5624,
+               0x56cc, 0x575c,
+               0x580c, 0x5814,
+               0x5890, 0x58bc,
+               0x5940, 0x59dc,
+               0x59fc, 0x5a18,
+               0x5a60, 0x5a9c,
+               0x5b9c, 0x5bfc,
+               0x6000, 0x6040,
+               0x6058, 0x614c,
+               0x7700, 0x7798,
+               0x77c0, 0x78fc,
+               0x7b00, 0x7c54,
+               0x7d00, 0x7efc,
+               0x8dc0, 0x8de0,
+               0x8df8, 0x8e84,
+               0x8ea0, 0x8f84,
+               0x8fc0, 0x90f8,
+               0x9400, 0x9470,
+               0x9600, 0x96f4,
+               0x9800, 0x9808,
+               0x9820, 0x983c,
+               0x9850, 0x9864,
+               0x9c00, 0x9c6c,
+               0x9c80, 0x9cec,
+               0x9d00, 0x9d6c,
+               0x9d80, 0x9dec,
+               0x9e00, 0x9e6c,
+               0x9e80, 0x9eec,
+               0x9f00, 0x9f6c,
+               0x9f80, 0xa020,
+               0xd004, 0xd03c,
+               0xdfc0, 0xdfe0,
+               0xe000, 0x11088,
+               0x1109c, 0x1117c,
+               0x11190, 0x11204,
+               0x19040, 0x1906c,
+               0x19078, 0x19080,
+               0x1908c, 0x19124,
+               0x19150, 0x191b0,
+               0x191d0, 0x191e8,
+               0x19238, 0x19290,
+               0x193f8, 0x19474,
+               0x19490, 0x194cc,
+               0x194f0, 0x194f8,
+               0x19c00, 0x19c60,
+               0x19c94, 0x19e10,
+               0x19e50, 0x19f34,
+               0x19f40, 0x19f50,
+               0x19f90, 0x19fe4,
+               0x1a000, 0x1a06c,
+               0x1a0b0, 0x1a120,
+               0x1a128, 0x1a138,
+               0x1a190, 0x1a1c4,
+               0x1a1fc, 0x1a1fc,
+               0x1e008, 0x1e00c,
+               0x1e040, 0x1e04c,
+               0x1e284, 0x1e290,
+               0x1e2c0, 0x1e2c0,
+               0x1e2e0, 0x1e2e0,
+               0x1e300, 0x1e384,
+               0x1e3c0, 0x1e3c8,
+               0x1e408, 0x1e40c,
+               0x1e440, 0x1e44c,
+               0x1e684, 0x1e690,
+               0x1e6c0, 0x1e6c0,
+               0x1e6e0, 0x1e6e0,
+               0x1e700, 0x1e784,
+               0x1e7c0, 0x1e7c8,
+               0x1e808, 0x1e80c,
+               0x1e840, 0x1e84c,
+               0x1ea84, 0x1ea90,
+               0x1eac0, 0x1eac0,
+               0x1eae0, 0x1eae0,
+               0x1eb00, 0x1eb84,
+               0x1ebc0, 0x1ebc8,
+               0x1ec08, 0x1ec0c,
+               0x1ec40, 0x1ec4c,
+               0x1ee84, 0x1ee90,
+               0x1eec0, 0x1eec0,
+               0x1eee0, 0x1eee0,
+               0x1ef00, 0x1ef84,
+               0x1efc0, 0x1efc8,
+               0x1f008, 0x1f00c,
+               0x1f040, 0x1f04c,
+               0x1f284, 0x1f290,
+               0x1f2c0, 0x1f2c0,
+               0x1f2e0, 0x1f2e0,
+               0x1f300, 0x1f384,
+               0x1f3c0, 0x1f3c8,
+               0x1f408, 0x1f40c,
+               0x1f440, 0x1f44c,
+               0x1f684, 0x1f690,
+               0x1f6c0, 0x1f6c0,
+               0x1f6e0, 0x1f6e0,
+               0x1f700, 0x1f784,
+               0x1f7c0, 0x1f7c8,
+               0x1f808, 0x1f80c,
+               0x1f840, 0x1f84c,
+               0x1fa84, 0x1fa90,
+               0x1fac0, 0x1fac0,
+               0x1fae0, 0x1fae0,
+               0x1fb00, 0x1fb84,
+               0x1fbc0, 0x1fbc8,
+               0x1fc08, 0x1fc0c,
+               0x1fc40, 0x1fc4c,
+               0x1fe84, 0x1fe90,
+               0x1fec0, 0x1fec0,
+               0x1fee0, 0x1fee0,
+               0x1ff00, 0x1ff84,
+               0x1ffc0, 0x1ffc8,
+               0x30000, 0x30030,
+               0x30100, 0x30144,
+               0x30190, 0x301d0,
+               0x30200, 0x30318,
+               0x30400, 0x3052c,
+               0x30540, 0x3061c,
+               0x30800, 0x30834,
+               0x308c0, 0x30908,
+               0x30910, 0x309ac,
+               0x30a00, 0x30a04,
+               0x30a0c, 0x30a2c,
+               0x30a44, 0x30a50,
+               0x30a74, 0x30c24,
+               0x30d08, 0x30d14,
+               0x30d1c, 0x30d20,
+               0x30d3c, 0x30d50,
+               0x31200, 0x3120c,
+               0x31220, 0x31220,
+               0x31240, 0x31240,
+               0x31600, 0x31600,
+               0x31608, 0x3160c,
+               0x31a00, 0x31a1c,
+               0x31e04, 0x31e20,
+               0x31e38, 0x31e3c,
+               0x31e80, 0x31e80,
+               0x31e88, 0x31ea8,
+               0x31eb0, 0x31eb4,
+               0x31ec8, 0x31ed4,
+               0x31fb8, 0x32004,
+               0x32208, 0x3223c,
+               0x32600, 0x32630,
+               0x32a00, 0x32abc,
+               0x32b00, 0x32b70,
+               0x33000, 0x33048,
+               0x33060, 0x3309c,
+               0x330f0, 0x33148,
+               0x33160, 0x3319c,
+               0x331f0, 0x332e4,
+               0x332f8, 0x333e4,
+               0x333f8, 0x33448,
+               0x33460, 0x3349c,
+               0x334f0, 0x33548,
+               0x33560, 0x3359c,
+               0x335f0, 0x336e4,
+               0x336f8, 0x337e4,
+               0x337f8, 0x337fc,
+               0x33814, 0x33814,
+               0x3382c, 0x3382c,
+               0x33880, 0x3388c,
+               0x338e8, 0x338ec,
+               0x33900, 0x33948,
+               0x33960, 0x3399c,
+               0x339f0, 0x33ae4,
+               0x33af8, 0x33b10,
+               0x33b28, 0x33b28,
+               0x33b3c, 0x33b50,
+               0x33bf0, 0x33c10,
+               0x33c28, 0x33c28,
+               0x33c3c, 0x33c50,
+               0x33cf0, 0x33cfc,
+               0x34000, 0x34030,
+               0x34100, 0x34144,
+               0x34190, 0x341d0,
+               0x34200, 0x34318,
+               0x34400, 0x3452c,
+               0x34540, 0x3461c,
+               0x34800, 0x34834,
+               0x348c0, 0x34908,
+               0x34910, 0x349ac,
+               0x34a00, 0x34a04,
+               0x34a0c, 0x34a2c,
+               0x34a44, 0x34a50,
+               0x34a74, 0x34c24,
+               0x34d08, 0x34d14,
+               0x34d1c, 0x34d20,
+               0x34d3c, 0x34d50,
+               0x35200, 0x3520c,
+               0x35220, 0x35220,
+               0x35240, 0x35240,
+               0x35600, 0x35600,
+               0x35608, 0x3560c,
+               0x35a00, 0x35a1c,
+               0x35e04, 0x35e20,
+               0x35e38, 0x35e3c,
+               0x35e80, 0x35e80,
+               0x35e88, 0x35ea8,
+               0x35eb0, 0x35eb4,
+               0x35ec8, 0x35ed4,
+               0x35fb8, 0x36004,
+               0x36208, 0x3623c,
+               0x36600, 0x36630,
+               0x36a00, 0x36abc,
+               0x36b00, 0x36b70,
+               0x37000, 0x37048,
+               0x37060, 0x3709c,
+               0x370f0, 0x37148,
+               0x37160, 0x3719c,
+               0x371f0, 0x372e4,
+               0x372f8, 0x373e4,
+               0x373f8, 0x37448,
+               0x37460, 0x3749c,
+               0x374f0, 0x37548,
+               0x37560, 0x3759c,
+               0x375f0, 0x376e4,
+               0x376f8, 0x377e4,
+               0x377f8, 0x377fc,
+               0x37814, 0x37814,
+               0x3782c, 0x3782c,
+               0x37880, 0x3788c,
+               0x378e8, 0x378ec,
+               0x37900, 0x37948,
+               0x37960, 0x3799c,
+               0x379f0, 0x37ae4,
+               0x37af8, 0x37b10,
+               0x37b28, 0x37b28,
+               0x37b3c, 0x37b50,
+               0x37bf0, 0x37c10,
+               0x37c28, 0x37c28,
+               0x37c3c, 0x37c50,
+               0x37cf0, 0x37cfc,
+               0x38000, 0x38030,
+               0x38100, 0x38144,
+               0x38190, 0x381d0,
+               0x38200, 0x38318,
+               0x38400, 0x3852c,
+               0x38540, 0x3861c,
+               0x38800, 0x38834,
+               0x388c0, 0x38908,
+               0x38910, 0x389ac,
+               0x38a00, 0x38a04,
+               0x38a0c, 0x38a2c,
+               0x38a44, 0x38a50,
+               0x38a74, 0x38c24,
+               0x38d08, 0x38d14,
+               0x38d1c, 0x38d20,
+               0x38d3c, 0x38d50,
+               0x39200, 0x3920c,
+               0x39220, 0x39220,
+               0x39240, 0x39240,
+               0x39600, 0x39600,
+               0x39608, 0x3960c,
+               0x39a00, 0x39a1c,
+               0x39e04, 0x39e20,
+               0x39e38, 0x39e3c,
+               0x39e80, 0x39e80,
+               0x39e88, 0x39ea8,
+               0x39eb0, 0x39eb4,
+               0x39ec8, 0x39ed4,
+               0x39fb8, 0x3a004,
+               0x3a208, 0x3a23c,
+               0x3a600, 0x3a630,
+               0x3aa00, 0x3aabc,
+               0x3ab00, 0x3ab70,
+               0x3b000, 0x3b048,
+               0x3b060, 0x3b09c,
+               0x3b0f0, 0x3b148,
+               0x3b160, 0x3b19c,
+               0x3b1f0, 0x3b2e4,
+               0x3b2f8, 0x3b3e4,
+               0x3b3f8, 0x3b448,
+               0x3b460, 0x3b49c,
+               0x3b4f0, 0x3b548,
+               0x3b560, 0x3b59c,
+               0x3b5f0, 0x3b6e4,
+               0x3b6f8, 0x3b7e4,
+               0x3b7f8, 0x3b7fc,
+               0x3b814, 0x3b814,
+               0x3b82c, 0x3b82c,
+               0x3b880, 0x3b88c,
+               0x3b8e8, 0x3b8ec,
+               0x3b900, 0x3b948,
+               0x3b960, 0x3b99c,
+               0x3b9f0, 0x3bae4,
+               0x3baf8, 0x3bb10,
+               0x3bb28, 0x3bb28,
+               0x3bb3c, 0x3bb50,
+               0x3bbf0, 0x3bc10,
+               0x3bc28, 0x3bc28,
+               0x3bc3c, 0x3bc50,
+               0x3bcf0, 0x3bcfc,
+               0x3c000, 0x3c030,
+               0x3c100, 0x3c144,
+               0x3c190, 0x3c1d0,
+               0x3c200, 0x3c318,
+               0x3c400, 0x3c52c,
+               0x3c540, 0x3c61c,
+               0x3c800, 0x3c834,
+               0x3c8c0, 0x3c908,
+               0x3c910, 0x3c9ac,
+               0x3ca00, 0x3ca04,
+               0x3ca0c, 0x3ca2c,
+               0x3ca44, 0x3ca50,
+               0x3ca74, 0x3cc24,
+               0x3cd08, 0x3cd14,
+               0x3cd1c, 0x3cd20,
+               0x3cd3c, 0x3cd50,
+               0x3d200, 0x3d20c,
+               0x3d220, 0x3d220,
+               0x3d240, 0x3d240,
+               0x3d600, 0x3d600,
+               0x3d608, 0x3d60c,
+               0x3da00, 0x3da1c,
+               0x3de04, 0x3de20,
+               0x3de38, 0x3de3c,
+               0x3de80, 0x3de80,
+               0x3de88, 0x3dea8,
+               0x3deb0, 0x3deb4,
+               0x3dec8, 0x3ded4,
+               0x3dfb8, 0x3e004,
+               0x3e208, 0x3e23c,
+               0x3e600, 0x3e630,
+               0x3ea00, 0x3eabc,
+               0x3eb00, 0x3eb70,
+               0x3f000, 0x3f048,
+               0x3f060, 0x3f09c,
+               0x3f0f0, 0x3f148,
+               0x3f160, 0x3f19c,
+               0x3f1f0, 0x3f2e4,
+               0x3f2f8, 0x3f3e4,
+               0x3f3f8, 0x3f448,
+               0x3f460, 0x3f49c,
+               0x3f4f0, 0x3f548,
+               0x3f560, 0x3f59c,
+               0x3f5f0, 0x3f6e4,
+               0x3f6f8, 0x3f7e4,
+               0x3f7f8, 0x3f7fc,
+               0x3f814, 0x3f814,
+               0x3f82c, 0x3f82c,
+               0x3f880, 0x3f88c,
+               0x3f8e8, 0x3f8ec,
+               0x3f900, 0x3f948,
+               0x3f960, 0x3f99c,
+               0x3f9f0, 0x3fae4,
+               0x3faf8, 0x3fb10,
+               0x3fb28, 0x3fb28,
+               0x3fb3c, 0x3fb50,
+               0x3fbf0, 0x3fc10,
+               0x3fc28, 0x3fc28,
+               0x3fc3c, 0x3fc50,
+               0x3fcf0, 0x3fcfc,
+               0x40000, 0x4000c,
+               0x40040, 0x40068,
+               0x40080, 0x40144,
+               0x40180, 0x4018c,
+               0x40200, 0x40298,
+               0x402ac, 0x4033c,
+               0x403f8, 0x403fc,
+               0x41300, 0x413c4,
+               0x41400, 0x4141c,
+               0x41480, 0x414d0,
+               0x44000, 0x44078,
+               0x440c0, 0x44278,
+               0x442c0, 0x44478,
+               0x444c0, 0x44678,
+               0x446c0, 0x44878,
+               0x448c0, 0x449fc,
+               0x45000, 0x45068,
+               0x45080, 0x45084,
+               0x450a0, 0x450b0,
+               0x45200, 0x45268,
+               0x45280, 0x45284,
+               0x452a0, 0x452b0,
+               0x460c0, 0x460e4,
+               0x47000, 0x4708c,
+               0x47200, 0x47250,
+               0x47400, 0x47420,
+               0x47600, 0x47618,
+               0x47800, 0x47814,
+               0x48000, 0x4800c,
+               0x48040, 0x48068,
+               0x48080, 0x48144,
+               0x48180, 0x4818c,
+               0x48200, 0x48298,
+               0x482ac, 0x4833c,
+               0x483f8, 0x483fc,
+               0x49300, 0x493c4,
+               0x49400, 0x4941c,
+               0x49480, 0x494d0,
+               0x4c000, 0x4c078,
+               0x4c0c0, 0x4c278,
+               0x4c2c0, 0x4c478,
+               0x4c4c0, 0x4c678,
+               0x4c6c0, 0x4c878,
+               0x4c8c0, 0x4c9fc,
+               0x4d000, 0x4d068,
+               0x4d080, 0x4d084,
+               0x4d0a0, 0x4d0b0,
+               0x4d200, 0x4d268,
+               0x4d280, 0x4d284,
+               0x4d2a0, 0x4d2b0,
+               0x4e0c0, 0x4e0e4,
+               0x4f000, 0x4f08c,
+               0x4f200, 0x4f250,
+               0x4f400, 0x4f420,
+               0x4f600, 0x4f618,
+               0x4f800, 0x4f814,
+               0x50000, 0x500cc,
+               0x50400, 0x50400,
+               0x50800, 0x508cc,
+               0x50c00, 0x50c00,
+               0x51000, 0x5101c,
+               0x51300, 0x51308,
+       };
+
        int i;
        struct adapter *ap = netdev2adap(dev);
+       static const unsigned int *reg_ranges;
+       int arr_size = 0, buf_size = 0;
+
+       if (is_t4(ap->chip)) {
+               reg_ranges = &t4_reg_ranges[0];
+               arr_size = ARRAY_SIZE(t4_reg_ranges);
+               buf_size = T4_REGMAP_SIZE;
+       } else {
+               reg_ranges = &t5_reg_ranges[0];
+               arr_size = ARRAY_SIZE(t5_reg_ranges);
+               buf_size = T5_REGMAP_SIZE;
+       }
 
        regs->version = mk_adap_vers(ap);
 
-       memset(buf, 0, T4_REGMAP_SIZE);
-       for (i = 0; i < ARRAY_SIZE(reg_ranges); i += 2)
+       memset(buf, 0, buf_size);
+       for (i = 0; i < arr_size; i += 2)
                reg_block_dump(ap, buf, reg_ranges[i], reg_ranges[i + 1]);
 }
 
@@ -2205,14 +2722,14 @@ static int cxgb_set_features(struct net_device *dev, netdev_features_t features)
        netdev_features_t changed = dev->features ^ features;
        int err;
 
-       if (!(changed & NETIF_F_HW_VLAN_RX))
+       if (!(changed & NETIF_F_HW_VLAN_CTAG_RX))
                return 0;
 
        err = t4_set_rxmode(pi->adapter, pi->adapter->fn, pi->viid, -1,
                            -1, -1, -1,
-                           !!(features & NETIF_F_HW_VLAN_RX), true);
+                           !!(features & NETIF_F_HW_VLAN_CTAG_RX), true);
        if (unlikely(err))
-               dev->features = features ^ NETIF_F_HW_VLAN_RX;
+               dev->features = features ^ NETIF_F_HW_VLAN_CTAG_RX;
        return err;
 }
 
@@ -2363,8 +2880,8 @@ static ssize_t mem_read(struct file *file, char __user *buf, size_t count,
                int ret, ofst;
                __be32 data[16];
 
-               if (mem == MEM_MC)
-                       ret = t4_mc_read(adap, pos, data, NULL);
+               if ((mem == MEM_MC) || (mem == MEM_MC1))
+                       ret = t4_mc_read(adap, mem % MEM_MC, pos, data, NULL);
                else
                        ret = t4_edc_read(adap, mem, pos, data, NULL);
                if (ret)
@@ -2405,18 +2922,37 @@ static void add_debugfs_mem(struct adapter *adap, const char *name,
 static int setup_debugfs(struct adapter *adap)
 {
        int i;
+       u32 size;
 
        if (IS_ERR_OR_NULL(adap->debugfs_root))
                return -1;
 
        i = t4_read_reg(adap, MA_TARGET_MEM_ENABLE);
-       if (i & EDRAM0_ENABLE)
-               add_debugfs_mem(adap, "edc0", MEM_EDC0, 5);
-       if (i & EDRAM1_ENABLE)
-               add_debugfs_mem(adap, "edc1", MEM_EDC1, 5);
-       if (i & EXT_MEM_ENABLE)
-               add_debugfs_mem(adap, "mc", MEM_MC,
-                       EXT_MEM_SIZE_GET(t4_read_reg(adap, MA_EXT_MEMORY_BAR)));
+       if (i & EDRAM0_ENABLE) {
+               size = t4_read_reg(adap, MA_EDRAM0_BAR);
+               add_debugfs_mem(adap, "edc0", MEM_EDC0, EDRAM_SIZE_GET(size));
+       }
+       if (i & EDRAM1_ENABLE) {
+               size = t4_read_reg(adap, MA_EDRAM1_BAR);
+               add_debugfs_mem(adap, "edc1", MEM_EDC1, EDRAM_SIZE_GET(size));
+       }
+       if (is_t4(adap->chip)) {
+               size = t4_read_reg(adap, MA_EXT_MEMORY_BAR);
+               if (i & EXT_MEM_ENABLE)
+                       add_debugfs_mem(adap, "mc", MEM_MC,
+                                       EXT_MEM_SIZE_GET(size));
+       } else {
+               if (i & EXT_MEM_ENABLE) {
+                       size = t4_read_reg(adap, MA_EXT_MEMORY_BAR);
+                       add_debugfs_mem(adap, "mc0", MEM_MC0,
+                                       EXT_MEM_SIZE_GET(size));
+               }
+               if (i & EXT_MEM1_ENABLE) {
+                       size = t4_read_reg(adap, MA_EXT_MEMORY1_BAR);
+                       add_debugfs_mem(adap, "mc1", MEM_MC1,
+                                       EXT_MEM_SIZE_GET(size));
+               }
+       }
        if (adap->l2t)
                debugfs_create_file("l2t", S_IRUSR, adap->debugfs_root, adap,
                                    &t4_l2t_fops);
@@ -2747,10 +3283,18 @@ EXPORT_SYMBOL(cxgb4_port_chan);
 unsigned int cxgb4_dbfifo_count(const struct net_device *dev, int lpfifo)
 {
        struct adapter *adap = netdev2adap(dev);
-       u32 v;
+       u32 v1, v2, lp_count, hp_count;
 
-       v = t4_read_reg(adap, A_SGE_DBFIFO_STATUS);
-       return lpfifo ? G_LP_COUNT(v) : G_HP_COUNT(v);
+       v1 = t4_read_reg(adap, A_SGE_DBFIFO_STATUS);
+       v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2);
+       if (is_t4(adap->chip)) {
+               lp_count = G_LP_COUNT(v1);
+               hp_count = G_HP_COUNT(v1);
+       } else {
+               lp_count = G_LP_COUNT_T5(v1);
+               hp_count = G_HP_COUNT_T5(v2);
+       }
+       return lpfifo ? lp_count : hp_count;
 }
 EXPORT_SYMBOL(cxgb4_dbfifo_count);
 
@@ -2853,6 +3397,25 @@ out:
 }
 EXPORT_SYMBOL(cxgb4_sync_txq_pidx);
 
+void cxgb4_disable_db_coalescing(struct net_device *dev)
+{
+       struct adapter *adap;
+
+       adap = netdev2adap(dev);
+       t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_NOCOALESCE,
+                        F_NOCOALESCE);
+}
+EXPORT_SYMBOL(cxgb4_disable_db_coalescing);
+
+void cxgb4_enable_db_coalescing(struct net_device *dev)
+{
+       struct adapter *adap;
+
+       adap = netdev2adap(dev);
+       t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_NOCOALESCE, 0);
+}
+EXPORT_SYMBOL(cxgb4_enable_db_coalescing);
+
 static struct pci_driver cxgb4_driver;
 
 static void check_neigh_update(struct neighbour *neigh)
@@ -2888,14 +3451,23 @@ static struct notifier_block cxgb4_netevent_nb = {
 
 static void drain_db_fifo(struct adapter *adap, int usecs)
 {
-       u32 v;
+       u32 v1, v2, lp_count, hp_count;
 
        do {
+               v1 = t4_read_reg(adap, A_SGE_DBFIFO_STATUS);
+               v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2);
+               if (is_t4(adap->chip)) {
+                       lp_count = G_LP_COUNT(v1);
+                       hp_count = G_HP_COUNT(v1);
+               } else {
+                       lp_count = G_LP_COUNT_T5(v1);
+                       hp_count = G_HP_COUNT_T5(v2);
+               }
+
+               if (lp_count == 0 && hp_count == 0)
+                       break;
                set_current_state(TASK_UNINTERRUPTIBLE);
                schedule_timeout(usecs_to_jiffies(usecs));
-               v = t4_read_reg(adap, A_SGE_DBFIFO_STATUS);
-               if (G_LP_COUNT(v) == 0 && G_HP_COUNT(v) == 0)
-                       break;
        } while (1);
 }
 
@@ -3004,24 +3576,62 @@ static void process_db_drop(struct work_struct *work)
 
        adap = container_of(work, struct adapter, db_drop_task);
 
+       if (is_t4(adap->chip)) {
+               disable_dbs(adap);
+               notify_rdma_uld(adap, CXGB4_CONTROL_DB_DROP);
+               drain_db_fifo(adap, 1);
+               recover_all_queues(adap);
+               enable_dbs(adap);
+       } else {
+               u32 dropped_db = t4_read_reg(adap, 0x010ac);
+               u16 qid = (dropped_db >> 15) & 0x1ffff;
+               u16 pidx_inc = dropped_db & 0x1fff;
+               unsigned int s_qpp;
+               unsigned short udb_density;
+               unsigned long qpshift;
+               int page;
+               u32 udb;
+
+               dev_warn(adap->pdev_dev,
+                        "Dropped DB 0x%x qid %d bar2 %d coalesce %d pidx %d\n",
+                        dropped_db, qid,
+                        (dropped_db >> 14) & 1,
+                        (dropped_db >> 13) & 1,
+                        pidx_inc);
+
+               drain_db_fifo(adap, 1);
+
+               s_qpp = QUEUESPERPAGEPF1 * adap->fn;
+               udb_density = 1 << QUEUESPERPAGEPF0_GET(t4_read_reg(adap,
+                               SGE_EGRESS_QUEUES_PER_PAGE_PF) >> s_qpp);
+               qpshift = PAGE_SHIFT - ilog2(udb_density);
+               udb = qid << qpshift;
+               udb &= PAGE_MASK;
+               page = udb / PAGE_SIZE;
+               udb += (qid - (page * udb_density)) * 128;
+
+               writel(PIDX(pidx_inc),  adap->bar2 + udb + 8);
+
+               /* Re-enable BAR2 WC */
+               t4_set_reg_field(adap, 0x10b0, 1<<15, 1<<15);
+       }
+
        t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_DROPPED_DB, 0);
-       disable_dbs(adap);
-       notify_rdma_uld(adap, CXGB4_CONTROL_DB_DROP);
-       drain_db_fifo(adap, 1);
-       recover_all_queues(adap);
-       enable_dbs(adap);
 }
 
 void t4_db_full(struct adapter *adap)
 {
-       t4_set_reg_field(adap, SGE_INT_ENABLE3,
-                        DBFIFO_HP_INT | DBFIFO_LP_INT, 0);
-       queue_work(workq, &adap->db_full_task);
+       if (is_t4(adap->chip)) {
+               t4_set_reg_field(adap, SGE_INT_ENABLE3,
+                                DBFIFO_HP_INT | DBFIFO_LP_INT, 0);
+               queue_work(workq, &adap->db_full_task);
+       }
 }
 
 void t4_db_dropped(struct adapter *adap)
 {
-       queue_work(workq, &adap->db_drop_task);
+       if (is_t4(adap->chip))
+               queue_work(workq, &adap->db_drop_task);
 }
 
 static void uld_attach(struct adapter *adap, unsigned int uld)
@@ -3566,17 +4176,27 @@ void t4_fatal_err(struct adapter *adap)
 
 static void setup_memwin(struct adapter *adap)
 {
-       u32 bar0;
+       u32 bar0, mem_win0_base, mem_win1_base, mem_win2_base;
 
        bar0 = pci_resource_start(adap->pdev, 0);  /* truncation intentional */
+       if (is_t4(adap->chip)) {
+               mem_win0_base = bar0 + MEMWIN0_BASE;
+               mem_win1_base = bar0 + MEMWIN1_BASE;
+               mem_win2_base = bar0 + MEMWIN2_BASE;
+       } else {
+               /* For T5, only relative offset inside the PCIe BAR is passed */
+               mem_win0_base = MEMWIN0_BASE;
+               mem_win1_base = MEMWIN1_BASE_T5;
+               mem_win2_base = MEMWIN2_BASE_T5;
+       }
        t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 0),
-                    (bar0 + MEMWIN0_BASE) | BIR(0) |
+                    mem_win0_base | BIR(0) |
                     WINDOW(ilog2(MEMWIN0_APERTURE) - 10));
        t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 1),
-                    (bar0 + MEMWIN1_BASE) | BIR(0) |
+                    mem_win1_base | BIR(0) |
                     WINDOW(ilog2(MEMWIN1_APERTURE) - 10));
        t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2),
-                    (bar0 + MEMWIN2_BASE) | BIR(0) |
+                    mem_win2_base | BIR(0) |
                     WINDOW(ilog2(MEMWIN2_APERTURE) - 10));
 }
 
@@ -3745,6 +4365,7 @@ static int adap_init0_config(struct adapter *adapter, int reset)
        unsigned long mtype = 0, maddr = 0;
        u32 finiver, finicsum, cfcsum;
        int ret, using_flash;
+       char *fw_config_file, fw_config_file_path[256];
 
        /*
         * Reset device if necessary.
@@ -3761,7 +4382,21 @@ static int adap_init0_config(struct adapter *adapter, int reset)
         * then use that.  Otherwise, use the configuration file stored
         * in the adapter flash ...
         */
-       ret = request_firmware(&cf, FW_CFNAME, adapter->pdev_dev);
+       switch (CHELSIO_CHIP_VERSION(adapter->chip)) {
+       case CHELSIO_T4:
+               fw_config_file = FW_CFNAME;
+               break;
+       case CHELSIO_T5:
+               fw_config_file = FW5_CFNAME;
+               break;
+       default:
+               dev_err(adapter->pdev_dev, "Device %d is not supported\n",
+                      adapter->pdev->device);
+               ret = -EINVAL;
+               goto bye;
+       }
+
+       ret = request_firmware(&cf, fw_config_file, adapter->pdev_dev);
        if (ret < 0) {
                using_flash = 1;
                mtype = FW_MEMTYPE_CF_FLASH;
@@ -3877,6 +4512,7 @@ static int adap_init0_config(struct adapter *adapter, int reset)
        if (ret < 0)
                goto bye;
 
+       sprintf(fw_config_file_path, "/lib/firmware/%s", fw_config_file);
        /*
         * Return successfully and note that we're operating with parameters
         * not supplied by the driver, rather than from hard-wired
@@ -3887,7 +4523,7 @@ static int adap_init0_config(struct adapter *adapter, int reset)
                 "Configuration File %s, version %#x, computed checksum %#x\n",
                 (using_flash
                  ? "in device FLASH"
-                 : "/lib/firmware/" FW_CFNAME),
+                 : fw_config_file_path),
                 finiver, cfcsum);
        return 0;
 
@@ -4814,7 +5450,8 @@ static void print_port_info(const struct net_device *dev)
        sprintf(bufp, "BASE-%s", base[pi->port_type]);
 
        netdev_info(dev, "Chelsio %s rev %d %s %sNIC PCIe x%d%s%s\n",
-                   adap->params.vpd.id, adap->params.rev, buf,
+                   adap->params.vpd.id,
+                   CHELSIO_CHIP_RELEASE(adap->params.rev), buf,
                    is_offload(adap) ? "R" : "", adap->params.pci.width, spd,
                    (adap->flags & USING_MSIX) ? " MSI-X" :
                    (adap->flags & USING_MSI) ? " MSI" : "");
@@ -4854,10 +5491,11 @@ static void free_some_resources(struct adapter *adapter)
 #define TSO_FLAGS (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN)
 #define VLAN_FEAT (NETIF_F_SG | NETIF_F_IP_CSUM | TSO_FLAGS | \
                   NETIF_F_IPV6_CSUM | NETIF_F_HIGHDMA)
+#define SEGMENT_SIZE 128
 
 static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-       int func, i, err;
+       int func, i, err, s_qpp, qpp, num_seg;
        struct port_info *pi;
        bool highdma = false;
        struct adapter *adapter = NULL;
@@ -4934,7 +5572,34 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        err = t4_prep_adapter(adapter);
        if (err)
-               goto out_unmap_bar;
+               goto out_unmap_bar0;
+
+       if (!is_t4(adapter->chip)) {
+               s_qpp = QUEUESPERPAGEPF1 * adapter->fn;
+               qpp = 1 << QUEUESPERPAGEPF0_GET(t4_read_reg(adapter,
+                     SGE_EGRESS_QUEUES_PER_PAGE_PF) >> s_qpp);
+               num_seg = PAGE_SIZE / SEGMENT_SIZE;
+
+               /* Each segment size is 128B. Write coalescing is enabled only
+                * when SGE_EGRESS_QUEUES_PER_PAGE_PF reg value for the
+                * queue is less no of segments that can be accommodated in
+                * a page size.
+                */
+               if (qpp > num_seg) {
+                       dev_err(&pdev->dev,
+                               "Incorrect number of egress queues per page\n");
+                       err = -EINVAL;
+                       goto out_unmap_bar0;
+               }
+               adapter->bar2 = ioremap_wc(pci_resource_start(pdev, 2),
+               pci_resource_len(pdev, 2));
+               if (!adapter->bar2) {
+                       dev_err(&pdev->dev, "cannot map device bar2 region\n");
+                       err = -ENOMEM;
+                       goto out_unmap_bar0;
+               }
+       }
+
        setup_memwin(adapter);
        err = adap_init0(adapter);
        setup_memwin_rdma(adapter);
@@ -4963,7 +5628,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                netdev->hw_features = NETIF_F_SG | TSO_FLAGS |
                        NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                        NETIF_F_RXCSUM | NETIF_F_RXHASH |
-                       NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+                       NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
                if (highdma)
                        netdev->hw_features |= NETIF_F_HIGHDMA;
                netdev->features |= netdev->hw_features;
@@ -5063,6 +5728,9 @@ sriov:
  out_free_dev:
        free_some_resources(adapter);
  out_unmap_bar:
+       if (!is_t4(adapter->chip))
+               iounmap(adapter->bar2);
+ out_unmap_bar0:
        iounmap(adapter->regs);
  out_free_adapter:
        kfree(adapter);
@@ -5113,6 +5781,8 @@ static void remove_one(struct pci_dev *pdev)
 
                free_some_resources(adapter);
                iounmap(adapter->regs);
+               if (!is_t4(adapter->chip))
+                       iounmap(adapter->bar2);
                kfree(adapter);
                pci_disable_pcie_error_reporting(pdev);
                pci_disable_device(pdev);
index e2bbc7f3e2de7cb331e80d7134a9ef64c02c04aa..4faf4d067ee71947c74c0a3e6e234208b315bee2 100644 (file)
@@ -269,4 +269,7 @@ struct sk_buff *cxgb4_pktgl_to_skb(const struct pkt_gl *gl,
                                   unsigned int skb_len, unsigned int pull_len);
 int cxgb4_sync_txq_pidx(struct net_device *dev, u16 qid, u16 pidx, u16 size);
 int cxgb4_flush_eq_cache(struct net_device *dev);
+void cxgb4_disable_db_coalescing(struct net_device *dev);
+void cxgb4_enable_db_coalescing(struct net_device *dev);
+
 #endif  /* !__CXGB4_OFLD_H */
index fe9a2ea3588ba64384c154e18fc75e4372229a50..2bfbb206b35af7c943f489ab5cbae9ebb44b8d7e 100644 (file)
@@ -506,10 +506,14 @@ static void unmap_rx_buf(struct adapter *adap, struct sge_fl *q)
 
 static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q)
 {
+       u32 val;
        if (q->pend_cred >= 8) {
+               val = PIDX(q->pend_cred / 8);
+               if (!is_t4(adap->chip))
+                       val |= DBTYPE(1);
                wmb();
                t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), DBPRIO(1) |
-                            QID(q->cntxt_id) | PIDX(q->pend_cred / 8));
+                            QID(q->cntxt_id) | val);
                q->pend_cred &= 7;
        }
 }
@@ -812,6 +816,22 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *q,
                *end = 0;
 }
 
+/* This function copies 64 byte coalesced work request to
+ * memory mapped BAR2 space(user space writes).
+ * For coalesced WR SGE, fetches data from the FIFO instead of from Host.
+ */
+static void cxgb_pio_copy(u64 __iomem *dst, u64 *src)
+{
+       int count = 8;
+
+       while (count) {
+               writeq(*src, dst);
+               src++;
+               dst++;
+               count--;
+       }
+}
+
 /**
  *     ring_tx_db - check and potentially ring a Tx queue's doorbell
  *     @adap: the adapter
@@ -822,11 +842,25 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *q,
  */
 static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n)
 {
+       unsigned int *wr, index;
+
        wmb();            /* write descriptors before telling HW */
        spin_lock(&q->db_lock);
        if (!q->db_disabled) {
-               t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL),
-                            QID(q->cntxt_id) | PIDX(n));
+               if (is_t4(adap->chip)) {
+                       t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL),
+                                    QID(q->cntxt_id) | PIDX(n));
+               } else {
+                       if (n == 1) {
+                               index = q->pidx ? (q->pidx - 1) : (q->size - 1);
+                               wr = (unsigned int *)&q->desc[index];
+                               cxgb_pio_copy((u64 __iomem *)
+                                             (adap->bar2 + q->udb + 64),
+                                             (u64 *)wr);
+                       } else
+                               writel(n,  adap->bar2 + q->udb + 8);
+                       wmb();
+               }
        }
        q->db_pidx = q->pidx;
        spin_unlock(&q->db_lock);
@@ -1555,7 +1589,6 @@ static noinline int handle_trace_pkt(struct adapter *adap,
                                     const struct pkt_gl *gl)
 {
        struct sk_buff *skb;
-       struct cpl_trace_pkt *p;
 
        skb = cxgb4_pktgl_to_skb(gl, RX_PULL_LEN, RX_PULL_LEN);
        if (unlikely(!skb)) {
@@ -1563,8 +1596,11 @@ static noinline int handle_trace_pkt(struct adapter *adap,
                return 0;
        }
 
-       p = (struct cpl_trace_pkt *)skb->data;
-       __skb_pull(skb, sizeof(*p));
+       if (is_t4(adap->chip))
+               __skb_pull(skb, sizeof(struct cpl_trace_pkt));
+       else
+               __skb_pull(skb, sizeof(struct cpl_t5_trace_pkt));
+
        skb_reset_mac_header(skb);
        skb->protocol = htons(0xffff);
        skb->dev = adap->port[0];
@@ -1597,7 +1633,7 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
                skb->rxhash = (__force u32)pkt->rsshdr.hash_val;
 
        if (unlikely(pkt->vlan_ex)) {
-               __vlan_hwaccel_put_tag(skb, ntohs(pkt->vlan));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(pkt->vlan));
                rxq->stats.vlan_ex++;
        }
        ret = napi_gro_frags(&rxq->rspq.napi);
@@ -1625,8 +1661,10 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
        const struct cpl_rx_pkt *pkt;
        struct sge_eth_rxq *rxq = container_of(q, struct sge_eth_rxq, rspq);
        struct sge *s = &q->adap->sge;
+       int cpl_trace_pkt = is_t4(q->adap->chip) ?
+                           CPL_TRACE_PKT : CPL_TRACE_PKT_T5;
 
-       if (unlikely(*(u8 *)rsp == CPL_TRACE_PKT))
+       if (unlikely(*(u8 *)rsp == cpl_trace_pkt))
                return handle_trace_pkt(q->adap, si);
 
        pkt = (const struct cpl_rx_pkt *)rsp;
@@ -1667,7 +1705,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
                skb_checksum_none_assert(skb);
 
        if (unlikely(pkt->vlan_ex)) {
-               __vlan_hwaccel_put_tag(skb, ntohs(pkt->vlan));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(pkt->vlan));
                rxq->stats.vlan_ex++;
        }
        netif_receive_skb(skb);
@@ -2143,11 +2181,27 @@ err:
 
 static void init_txq(struct adapter *adap, struct sge_txq *q, unsigned int id)
 {
+       q->cntxt_id = id;
+       if (!is_t4(adap->chip)) {
+               unsigned int s_qpp;
+               unsigned short udb_density;
+               unsigned long qpshift;
+               int page;
+
+               s_qpp = QUEUESPERPAGEPF1 * adap->fn;
+               udb_density = 1 << QUEUESPERPAGEPF0_GET((t4_read_reg(adap,
+                               SGE_EGRESS_QUEUES_PER_PAGE_PF) >> s_qpp));
+               qpshift = PAGE_SHIFT - ilog2(udb_density);
+               q->udb = q->cntxt_id << qpshift;
+               q->udb &= PAGE_MASK;
+               page = q->udb / PAGE_SIZE;
+               q->udb += (q->cntxt_id - (page * udb_density)) * 128;
+       }
+
        q->in_use = 0;
        q->cidx = q->pidx = 0;
        q->stops = q->restarts = 0;
        q->stat = (void *)&q->desc[q->size];
-       q->cntxt_id = id;
        spin_lock_init(&q->db_lock);
        adap->sge.egr_map[id - adap->sge.egr_start] = q;
 }
@@ -2587,11 +2641,20 @@ static int t4_sge_init_hard(struct adapter *adap)
         * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows
         * and generate an interrupt when this occurs so we can recover.
         */
-       t4_set_reg_field(adap, A_SGE_DBFIFO_STATUS,
-                       V_HP_INT_THRESH(M_HP_INT_THRESH) |
-                       V_LP_INT_THRESH(M_LP_INT_THRESH),
-                       V_HP_INT_THRESH(dbfifo_int_thresh) |
-                       V_LP_INT_THRESH(dbfifo_int_thresh));
+       if (is_t4(adap->chip)) {
+               t4_set_reg_field(adap, A_SGE_DBFIFO_STATUS,
+                                V_HP_INT_THRESH(M_HP_INT_THRESH) |
+                                V_LP_INT_THRESH(M_LP_INT_THRESH),
+                                V_HP_INT_THRESH(dbfifo_int_thresh) |
+                                V_LP_INT_THRESH(dbfifo_int_thresh));
+       } else {
+               t4_set_reg_field(adap, A_SGE_DBFIFO_STATUS,
+                                V_LP_INT_THRESH_T5(M_LP_INT_THRESH_T5),
+                                V_LP_INT_THRESH_T5(dbfifo_int_thresh));
+               t4_set_reg_field(adap, SGE_DBFIFO_STATUS2,
+                                V_HP_INT_THRESH_T5(M_HP_INT_THRESH_T5),
+                                V_HP_INT_THRESH_T5(dbfifo_int_thresh));
+       }
        t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_ENABLE_DROP,
                        F_ENABLE_DROP);
 
index 8049268ce0f25f50f99a5a0ef7137d0f97c1e763..d02d4e8c4417cf7d78a70586f6c532ef8355a76a 100644 (file)
@@ -282,6 +282,7 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size,
  *     t4_mc_read - read from MC through backdoor accesses
  *     @adap: the adapter
  *     @addr: address of first byte requested
+ *     @idx: which MC to access
  *     @data: 64 bytes of data containing the requested address
  *     @ecc: where to store the corresponding 64-bit ECC word
  *
@@ -289,22 +290,38 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size,
  *     that covers the requested address @addr.  If @parity is not %NULL it
  *     is assigned the 64-bit ECC word for the read data.
  */
-int t4_mc_read(struct adapter *adap, u32 addr, __be32 *data, u64 *ecc)
+int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc)
 {
        int i;
+       u32 mc_bist_cmd, mc_bist_cmd_addr, mc_bist_cmd_len;
+       u32 mc_bist_status_rdata, mc_bist_data_pattern;
+
+       if (is_t4(adap->chip)) {
+               mc_bist_cmd = MC_BIST_CMD;
+               mc_bist_cmd_addr = MC_BIST_CMD_ADDR;
+               mc_bist_cmd_len = MC_BIST_CMD_LEN;
+               mc_bist_status_rdata = MC_BIST_STATUS_RDATA;
+               mc_bist_data_pattern = MC_BIST_DATA_PATTERN;
+       } else {
+               mc_bist_cmd = MC_REG(MC_P_BIST_CMD, idx);
+               mc_bist_cmd_addr = MC_REG(MC_P_BIST_CMD_ADDR, idx);
+               mc_bist_cmd_len = MC_REG(MC_P_BIST_CMD_LEN, idx);
+               mc_bist_status_rdata = MC_REG(MC_P_BIST_STATUS_RDATA, idx);
+               mc_bist_data_pattern = MC_REG(MC_P_BIST_DATA_PATTERN, idx);
+       }
 
-       if (t4_read_reg(adap, MC_BIST_CMD) & START_BIST)
+       if (t4_read_reg(adap, mc_bist_cmd) & START_BIST)
                return -EBUSY;
-       t4_write_reg(adap, MC_BIST_CMD_ADDR, addr & ~0x3fU);
-       t4_write_reg(adap, MC_BIST_CMD_LEN, 64);
-       t4_write_reg(adap, MC_BIST_DATA_PATTERN, 0xc);
-       t4_write_reg(adap, MC_BIST_CMD, BIST_OPCODE(1) | START_BIST |
+       t4_write_reg(adap, mc_bist_cmd_addr, addr & ~0x3fU);
+       t4_write_reg(adap, mc_bist_cmd_len, 64);
+       t4_write_reg(adap, mc_bist_data_pattern, 0xc);
+       t4_write_reg(adap, mc_bist_cmd, BIST_OPCODE(1) | START_BIST |
                     BIST_CMD_GAP(1));
-       i = t4_wait_op_done(adap, MC_BIST_CMD, START_BIST, 0, 10, 1);
+       i = t4_wait_op_done(adap, mc_bist_cmd, START_BIST, 0, 10, 1);
        if (i)
                return i;
 
-#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i)
+#define MC_DATA(i) MC_BIST_STATUS_REG(mc_bist_status_rdata, i)
 
        for (i = 15; i >= 0; i--)
                *data++ = htonl(t4_read_reg(adap, MC_DATA(i)));
@@ -329,20 +346,39 @@ int t4_mc_read(struct adapter *adap, u32 addr, __be32 *data, u64 *ecc)
 int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc)
 {
        int i;
+       u32 edc_bist_cmd, edc_bist_cmd_addr, edc_bist_cmd_len;
+       u32 edc_bist_cmd_data_pattern, edc_bist_status_rdata;
+
+       if (is_t4(adap->chip)) {
+               edc_bist_cmd = EDC_REG(EDC_BIST_CMD, idx);
+               edc_bist_cmd_addr = EDC_REG(EDC_BIST_CMD_ADDR, idx);
+               edc_bist_cmd_len = EDC_REG(EDC_BIST_CMD_LEN, idx);
+               edc_bist_cmd_data_pattern = EDC_REG(EDC_BIST_DATA_PATTERN,
+                                                   idx);
+               edc_bist_status_rdata = EDC_REG(EDC_BIST_STATUS_RDATA,
+                                                   idx);
+       } else {
+               edc_bist_cmd = EDC_REG_T5(EDC_H_BIST_CMD, idx);
+               edc_bist_cmd_addr = EDC_REG_T5(EDC_H_BIST_CMD_ADDR, idx);
+               edc_bist_cmd_len = EDC_REG_T5(EDC_H_BIST_CMD_LEN, idx);
+               edc_bist_cmd_data_pattern =
+                       EDC_REG_T5(EDC_H_BIST_DATA_PATTERN, idx);
+               edc_bist_status_rdata =
+                        EDC_REG_T5(EDC_H_BIST_STATUS_RDATA, idx);
+       }
 
-       idx *= EDC_STRIDE;
-       if (t4_read_reg(adap, EDC_BIST_CMD + idx) & START_BIST)
+       if (t4_read_reg(adap, edc_bist_cmd) & START_BIST)
                return -EBUSY;
-       t4_write_reg(adap, EDC_BIST_CMD_ADDR + idx, addr & ~0x3fU);
-       t4_write_reg(adap, EDC_BIST_CMD_LEN + idx, 64);
-       t4_write_reg(adap, EDC_BIST_DATA_PATTERN + idx, 0xc);
-       t4_write_reg(adap, EDC_BIST_CMD + idx,
+       t4_write_reg(adap, edc_bist_cmd_addr, addr & ~0x3fU);
+       t4_write_reg(adap, edc_bist_cmd_len, 64);
+       t4_write_reg(adap, edc_bist_cmd_data_pattern, 0xc);
+       t4_write_reg(adap, edc_bist_cmd,
                     BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST);
-       i = t4_wait_op_done(adap, EDC_BIST_CMD + idx, START_BIST, 0, 10, 1);
+       i = t4_wait_op_done(adap, edc_bist_cmd, START_BIST, 0, 10, 1);
        if (i)
                return i;
 
-#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx)
+#define EDC_DATA(i) (EDC_BIST_STATUS_REG(edc_bist_status_rdata, i))
 
        for (i = 15; i >= 0; i--)
                *data++ = htonl(t4_read_reg(adap, EDC_DATA(i)));
@@ -366,6 +402,7 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc)
 static int t4_mem_win_rw(struct adapter *adap, u32 addr, __be32 *data, int dir)
 {
        int i;
+       u32 win_pf = is_t4(adap->chip) ? 0 : V_PFNUM(adap->fn);
 
        /*
         * Setup offset into PCIE memory window.  Address must be a
@@ -374,7 +411,7 @@ static int t4_mem_win_rw(struct adapter *adap, u32 addr, __be32 *data, int dir)
         * values.)
         */
        t4_write_reg(adap, PCIE_MEM_ACCESS_OFFSET,
-                    addr & ~(MEMWIN0_APERTURE - 1));
+                    (addr & ~(MEMWIN0_APERTURE - 1)) | win_pf);
        t4_read_reg(adap, PCIE_MEM_ACCESS_OFFSET);
 
        /* Collecting data 4 bytes at a time upto MEMWIN0_APERTURE */
@@ -410,6 +447,7 @@ static int t4_memory_rw(struct adapter *adap, int mtype, u32 addr, u32 len,
                        __be32 *buf, int dir)
 {
        u32 pos, start, end, offset, memoffset;
+       u32 edc_size, mc_size;
        int ret = 0;
        __be32 *data;
 
@@ -423,13 +461,21 @@ static int t4_memory_rw(struct adapter *adap, int mtype, u32 addr, u32 len,
        if (!data)
                return -ENOMEM;
 
-       /*
-        * Offset into the region of memory which is being accessed
+       /* Offset into the region of memory which is being accessed
         * MEM_EDC0 = 0
         * MEM_EDC1 = 1
-        * MEM_MC   = 2
+        * MEM_MC   = 2 -- T4
+        * MEM_MC0  = 2 -- For T5
+        * MEM_MC1  = 3 -- For T5
         */
-       memoffset = (mtype * (5 * 1024 * 1024));
+       edc_size  = EDRAM_SIZE_GET(t4_read_reg(adap, MA_EDRAM0_BAR));
+       if (mtype != MEM_MC1)
+               memoffset = (mtype * (edc_size * 1024 * 1024));
+       else {
+               mc_size = EXT_MEM_SIZE_GET(t4_read_reg(adap,
+                                                      MA_EXT_MEMORY_BAR));
+               memoffset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024;
+       }
 
        /* Determine the PCIE_MEM_ACCESS_OFFSET */
        addr = addr + memoffset;
@@ -497,9 +543,9 @@ int t4_memory_write(struct adapter *adap, int mtype, u32 addr, u32 len,
 }
 
 #define EEPROM_STAT_ADDR   0x7bfc
-#define VPD_LEN            512
 #define VPD_BASE           0x400
 #define VPD_BASE_OLD       0
+#define VPD_LEN            1024
 
 /**
  *     t4_seeprom_wp - enable/disable EEPROM write protection
@@ -856,6 +902,7 @@ int t4_check_fw_version(struct adapter *adapter)
 {
        u32 api_vers[2];
        int ret, major, minor, micro;
+       int exp_major, exp_minor, exp_micro;
 
        ret = get_fw_version(adapter, &adapter->params.fw_vers);
        if (!ret)
@@ -870,17 +917,35 @@ int t4_check_fw_version(struct adapter *adapter)
        major = FW_HDR_FW_VER_MAJOR_GET(adapter->params.fw_vers);
        minor = FW_HDR_FW_VER_MINOR_GET(adapter->params.fw_vers);
        micro = FW_HDR_FW_VER_MICRO_GET(adapter->params.fw_vers);
+
+       switch (CHELSIO_CHIP_VERSION(adapter->chip)) {
+       case CHELSIO_T4:
+               exp_major = FW_VERSION_MAJOR;
+               exp_minor = FW_VERSION_MINOR;
+               exp_micro = FW_VERSION_MICRO;
+               break;
+       case CHELSIO_T5:
+               exp_major = FW_VERSION_MAJOR_T5;
+               exp_minor = FW_VERSION_MINOR_T5;
+               exp_micro = FW_VERSION_MICRO_T5;
+               break;
+       default:
+               dev_err(adapter->pdev_dev, "Unsupported chip type, %x\n",
+                       adapter->chip);
+               return -EINVAL;
+       }
+
        memcpy(adapter->params.api_vers, api_vers,
               sizeof(adapter->params.api_vers));
 
-       if (major != FW_VERSION_MAJOR) {            /* major mismatch - fail */
+       if (major != exp_major) {            /* major mismatch - fail */
                dev_err(adapter->pdev_dev,
                        "card FW has major version %u, driver wants %u\n",
-                       major, FW_VERSION_MAJOR);
+                       major, exp_major);
                return -EINVAL;
        }
 
-       if (minor == FW_VERSION_MINOR && micro == FW_VERSION_MICRO)
+       if (minor == exp_minor && micro == exp_micro)
                return 0;                                   /* perfect match */
 
        /* Minor/micro version mismatch.  Report it but often it's OK. */
@@ -1246,6 +1311,45 @@ static void pcie_intr_handler(struct adapter *adapter)
                { 0 }
        };
 
+       static struct intr_info t5_pcie_intr_info[] = {
+               { MSTGRPPERR, "Master Response Read Queue parity error",
+                 -1, 1 },
+               { MSTTIMEOUTPERR, "Master Timeout FIFO parity error", -1, 1 },
+               { MSIXSTIPERR, "MSI-X STI SRAM parity error", -1, 1 },
+               { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 },
+               { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 },
+               { MSIXDATAPERR, "MSI-X data parity error", -1, 1 },
+               { MSIXDIPERR, "MSI-X DI parity error", -1, 1 },
+               { PIOCPLGRPPERR, "PCI PIO completion Group FIFO parity error",
+                 -1, 1 },
+               { PIOREQGRPPERR, "PCI PIO request Group FIFO parity error",
+                 -1, 1 },
+               { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 },
+               { MSTTAGQPERR, "PCI master tag queue parity error", -1, 1 },
+               { CREQPERR, "PCI CMD channel request parity error", -1, 1 },
+               { CRSPPERR, "PCI CMD channel response parity error", -1, 1 },
+               { DREQWRPERR, "PCI DMA channel write request parity error",
+                 -1, 1 },
+               { DREQPERR, "PCI DMA channel request parity error", -1, 1 },
+               { DRSPPERR, "PCI DMA channel response parity error", -1, 1 },
+               { HREQWRPERR, "PCI HMA channel count parity error", -1, 1 },
+               { HREQPERR, "PCI HMA channel request parity error", -1, 1 },
+               { HRSPPERR, "PCI HMA channel response parity error", -1, 1 },
+               { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 },
+               { FIDPERR, "PCI FID parity error", -1, 1 },
+               { VFIDPERR, "PCI INTx clear parity error", -1, 1 },
+               { MAGRPPERR, "PCI MA group FIFO parity error", -1, 1 },
+               { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 },
+               { IPRXHDRGRPPERR, "PCI IP Rx header group parity error",
+                 -1, 1 },
+               { IPRXDATAGRPPERR, "PCI IP Rx data group parity error", -1, 1 },
+               { RPLPERR, "PCI IP replay buffer parity error", -1, 1 },
+               { IPSOTPERR, "PCI IP SOT buffer parity error", -1, 1 },
+               { TRGT1GRPPERR, "PCI TRGT1 group FIFOs parity error", -1, 1 },
+               { READRSPERR, "Outbound read error", -1, 0 },
+               { 0 }
+       };
+
        int fat;
 
        fat = t4_handle_intr_status(adapter,
@@ -1254,7 +1358,10 @@ static void pcie_intr_handler(struct adapter *adapter)
              t4_handle_intr_status(adapter,
                                    PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS,
                                    pcie_port_intr_info) +
-             t4_handle_intr_status(adapter, PCIE_INT_CAUSE, pcie_intr_info);
+             t4_handle_intr_status(adapter, PCIE_INT_CAUSE,
+                                   is_t4(adapter->chip) ?
+                                   pcie_intr_info : t5_pcie_intr_info);
+
        if (fat)
                t4_fatal_err(adapter);
 }
@@ -1664,7 +1771,14 @@ static void ncsi_intr_handler(struct adapter *adap)
  */
 static void xgmac_intr_handler(struct adapter *adap, int port)
 {
-       u32 v = t4_read_reg(adap, PORT_REG(port, XGMAC_PORT_INT_CAUSE));
+       u32 v, int_cause_reg;
+
+       if (is_t4(adap->chip))
+               int_cause_reg = PORT_REG(port, XGMAC_PORT_INT_CAUSE);
+       else
+               int_cause_reg = T5_PORT_REG(port, MAC_PORT_INT_CAUSE);
+
+       v = t4_read_reg(adap, int_cause_reg);
 
        v &= TXFIFO_PRTY_ERR | RXFIFO_PRTY_ERR;
        if (!v)
@@ -2126,7 +2240,9 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p)
        u32 bgmap = get_mps_bg_map(adap, idx);
 
 #define GET_STAT(name) \
-       t4_read_reg64(adap, PORT_REG(idx, MPS_PORT_STAT_##name##_L))
+       t4_read_reg64(adap, \
+       (is_t4(adap->chip) ? PORT_REG(idx, MPS_PORT_STAT_##name##_L) : \
+       T5_PORT_REG(idx, MPS_PORT_STAT_##name##_L)))
 #define GET_STAT_COM(name) t4_read_reg64(adap, MPS_STAT_##name##_L)
 
        p->tx_octets           = GET_STAT(TX_PORT_BYTES);
@@ -2205,14 +2321,26 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p)
 void t4_wol_magic_enable(struct adapter *adap, unsigned int port,
                         const u8 *addr)
 {
+       u32 mag_id_reg_l, mag_id_reg_h, port_cfg_reg;
+
+       if (is_t4(adap->chip)) {
+               mag_id_reg_l = PORT_REG(port, XGMAC_PORT_MAGIC_MACID_LO);
+               mag_id_reg_h = PORT_REG(port, XGMAC_PORT_MAGIC_MACID_HI);
+               port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2);
+       } else {
+               mag_id_reg_l = T5_PORT_REG(port, MAC_PORT_MAGIC_MACID_LO);
+               mag_id_reg_h = T5_PORT_REG(port, MAC_PORT_MAGIC_MACID_HI);
+               port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2);
+       }
+
        if (addr) {
-               t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_MAGIC_MACID_LO),
+               t4_write_reg(adap, mag_id_reg_l,
                             (addr[2] << 24) | (addr[3] << 16) |
                             (addr[4] << 8) | addr[5]);
-               t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_MAGIC_MACID_HI),
+               t4_write_reg(adap, mag_id_reg_h,
                             (addr[0] << 8) | addr[1]);
        }
-       t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2), MAGICEN,
+       t4_set_reg_field(adap, port_cfg_reg, MAGICEN,
                         addr ? MAGICEN : 0);
 }
 
@@ -2235,16 +2363,23 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map,
                      u64 mask0, u64 mask1, unsigned int crc, bool enable)
 {
        int i;
+       u32 port_cfg_reg;
+
+       if (is_t4(adap->chip))
+               port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2);
+       else
+               port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2);
 
        if (!enable) {
-               t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2),
-                                PATEN, 0);
+               t4_set_reg_field(adap, port_cfg_reg, PATEN, 0);
                return 0;
        }
        if (map > 0xff)
                return -EINVAL;
 
-#define EPIO_REG(name) PORT_REG(port, XGMAC_PORT_EPIO_##name)
+#define EPIO_REG(name) \
+       (is_t4(adap->chip) ? PORT_REG(port, XGMAC_PORT_EPIO_##name) : \
+       T5_PORT_REG(port, MAC_PORT_EPIO_##name))
 
        t4_write_reg(adap, EPIO_REG(DATA1), mask0 >> 32);
        t4_write_reg(adap, EPIO_REG(DATA2), mask1);
@@ -2322,24 +2457,24 @@ int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox,
  *     @addr: address of first byte requested aligned on 32b.
  *     @data: len bytes to hold the data read
  *     @len: amount of data to read from window.  Must be <=
- *            MEMWIN0_APERATURE after adjusting for 16B alignment
- *            requirements of the the memory window.
+ *            MEMWIN0_APERATURE after adjusting for 16B for T4 and
+ *            128B for T5 alignment requirements of the the memory window.
  *
  *     Read len bytes of data from MC starting at @addr.
  */
 int t4_mem_win_read_len(struct adapter *adap, u32 addr, __be32 *data, int len)
 {
-       int i;
-       int off;
+       int i, off;
+       u32 win_pf = is_t4(adap->chip) ? 0 : V_PFNUM(adap->fn);
 
-       /*
-        * Align on a 16B boundary.
+       /* Align on a 2KB boundary.
         */
-       off = addr & 15;
+       off = addr & MEMWIN0_APERTURE;
        if ((addr & 3) || (len + off) > MEMWIN0_APERTURE)
                return -EINVAL;
 
-       t4_write_reg(adap, PCIE_MEM_ACCESS_OFFSET, addr & ~15);
+       t4_write_reg(adap, PCIE_MEM_ACCESS_OFFSET,
+                    (addr & ~MEMWIN0_APERTURE) | win_pf);
        t4_read_reg(adap, PCIE_MEM_ACCESS_OFFSET);
 
        for (i = 0; i < len; i += 4)
@@ -3162,6 +3297,9 @@ int t4_alloc_mac_filt(struct adapter *adap, unsigned int mbox,
        int i, ret;
        struct fw_vi_mac_cmd c;
        struct fw_vi_mac_exact *p;
+       unsigned int max_naddr = is_t4(adap->chip) ?
+                                      NUM_MPS_CLS_SRAM_L_INSTANCES :
+                                      NUM_MPS_T5_CLS_SRAM_L_INSTANCES;
 
        if (naddr > 7)
                return -EINVAL;
@@ -3187,8 +3325,8 @@ int t4_alloc_mac_filt(struct adapter *adap, unsigned int mbox,
                u16 index = FW_VI_MAC_CMD_IDX_GET(ntohs(p->valid_to_idx));
 
                if (idx)
-                       idx[i] = index >= NEXACT_MAC ? 0xffff : index;
-               if (index < NEXACT_MAC)
+                       idx[i] = index >= max_naddr ? 0xffff : index;
+               if (index < max_naddr)
                        ret++;
                else if (hash)
                        *hash |= (1ULL << hash_mac_addr(addr[i]));
@@ -3221,6 +3359,9 @@ int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
        int ret, mode;
        struct fw_vi_mac_cmd c;
        struct fw_vi_mac_exact *p = c.u.exact;
+       unsigned int max_mac_addr = is_t4(adap->chip) ?
+                                   NUM_MPS_CLS_SRAM_L_INSTANCES :
+                                   NUM_MPS_T5_CLS_SRAM_L_INSTANCES;
 
        if (idx < 0)                             /* new allocation */
                idx = persist ? FW_VI_MAC_ADD_PERSIST_MAC : FW_VI_MAC_ADD_MAC;
@@ -3238,7 +3379,7 @@ int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
        ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c);
        if (ret == 0) {
                ret = FW_VI_MAC_CMD_IDX_GET(ntohs(p->valid_to_idx));
-               if (ret >= NEXACT_MAC)
+               if (ret >= max_mac_addr)
                        ret = -ENOMEM;
        }
        return ret;
@@ -3547,7 +3688,8 @@ static int get_flash_params(struct adapter *adap)
  */
 int t4_prep_adapter(struct adapter *adapter)
 {
-       int ret;
+       int ret, ver;
+       uint16_t device_id;
 
        ret = t4_wait_dev_ready(adapter);
        if (ret < 0)
@@ -3562,6 +3704,28 @@ int t4_prep_adapter(struct adapter *adapter)
                return ret;
        }
 
+       /* Retrieve adapter's device ID
+        */
+       pci_read_config_word(adapter->pdev, PCI_DEVICE_ID, &device_id);
+       ver = device_id >> 12;
+       switch (ver) {
+       case CHELSIO_T4:
+               adapter->chip = CHELSIO_CHIP_CODE(CHELSIO_T4,
+                                                 adapter->params.rev);
+               break;
+       case CHELSIO_T5:
+               adapter->chip = CHELSIO_CHIP_CODE(CHELSIO_T5,
+                                                 adapter->params.rev);
+               break;
+       default:
+               dev_err(adapter->pdev_dev, "Device %d is not supported\n",
+                       device_id);
+               return -EINVAL;
+       }
+
+       /* Reassign the updated revision field */
+       adapter->params.rev = adapter->chip;
+
        init_cong_ctrl(adapter->params.a_wnd, adapter->params.b_wnd);
 
        /*
index f534ed7e10e9db34b55a5be07f717d93e860a7df..1d1623be9f1e9814a19ee2b1859597c2cd8ebef5 100644 (file)
@@ -47,7 +47,6 @@ enum {
        TCB_SIZE       = 128,   /* TCB size */
        NMTUS          = 16,    /* size of MTU table */
        NCCTRL_WIN     = 32,    /* # of congestion control windows */
-       NEXACT_MAC     = 336,   /* # of exact MAC address filters */
        L2T_SIZE       = 4096,  /* # of L2T entries */
        MBOX_LEN       = 64,    /* mailbox size in bytes */
        TRACE_LEN      = 112,   /* length of trace data and mask */
index 261d17703adcf72c6b1449c02081552ef1ed3e56..47656ac1ac255a7a83b78291c3188beed86fddfa 100644 (file)
@@ -74,6 +74,7 @@ enum {
        CPL_PASS_ESTABLISH    = 0x41,
        CPL_RX_DATA_DDP       = 0x42,
        CPL_PASS_ACCEPT_REQ   = 0x44,
+       CPL_TRACE_PKT_T5      = 0x48,
 
        CPL_RDMA_READ_REQ     = 0x60,
 
@@ -287,6 +288,23 @@ struct cpl_act_open_req {
        __be32 opt2;
 };
 
+#define S_FILTER_TUPLE  24
+#define M_FILTER_TUPLE  0xFFFFFFFFFF
+#define V_FILTER_TUPLE(x) ((x) << S_FILTER_TUPLE)
+#define G_FILTER_TUPLE(x) (((x) >> S_FILTER_TUPLE) & M_FILTER_TUPLE)
+struct cpl_t5_act_open_req {
+       WR_HDR;
+       union opcode_tid ot;
+       __be16 local_port;
+       __be16 peer_port;
+       __be32 local_ip;
+       __be32 peer_ip;
+       __be64 opt0;
+       __be32 rsvd;
+       __be32 opt2;
+       __be64 params;
+};
+
 struct cpl_act_open_req6 {
        WR_HDR;
        union opcode_tid ot;
@@ -566,6 +584,11 @@ struct cpl_rx_pkt {
 #define V_RX_ETHHDR_LEN(x) ((x) << S_RX_ETHHDR_LEN)
 #define G_RX_ETHHDR_LEN(x) (((x) >> S_RX_ETHHDR_LEN) & M_RX_ETHHDR_LEN)
 
+#define S_RX_T5_ETHHDR_LEN    0
+#define M_RX_T5_ETHHDR_LEN    0x3F
+#define V_RX_T5_ETHHDR_LEN(x) ((x) << S_RX_T5_ETHHDR_LEN)
+#define G_RX_T5_ETHHDR_LEN(x) (((x) >> S_RX_T5_ETHHDR_LEN) & M_RX_T5_ETHHDR_LEN)
+
 #define S_RX_MACIDX    8
 #define M_RX_MACIDX    0x1FF
 #define V_RX_MACIDX(x) ((x) << S_RX_MACIDX)
@@ -612,6 +635,28 @@ struct cpl_trace_pkt {
        __be64 tstamp;
 };
 
+struct cpl_t5_trace_pkt {
+       __u8 opcode;
+       __u8 intf;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+       __u8 runt:4;
+       __u8 filter_hit:4;
+       __u8:6;
+       __u8 err:1;
+       __u8 trunc:1;
+#else
+       __u8 filter_hit:4;
+       __u8 runt:4;
+       __u8 trunc:1;
+       __u8 err:1;
+       __u8:6;
+#endif
+       __be16 rsvd;
+       __be16 len;
+       __be64 tstamp;
+       __be64 rsvd1;
+};
+
 struct cpl_l2t_write_req {
        WR_HDR;
        union opcode_tid ot;
@@ -742,4 +787,12 @@ struct ulp_mem_io {
 #define ULP_MEMIO_LOCK(x) ((x) << 31)
 };
 
+#define S_T5_ULP_MEMIO_IMM    23
+#define V_T5_ULP_MEMIO_IMM(x) ((x) << S_T5_ULP_MEMIO_IMM)
+#define F_T5_ULP_MEMIO_IMM    V_T5_ULP_MEMIO_IMM(1U)
+
+#define S_T5_ULP_MEMIO_ORDER    22
+#define V_T5_ULP_MEMIO_ORDER(x) ((x) << S_T5_ULP_MEMIO_ORDER)
+#define F_T5_ULP_MEMIO_ORDER    V_T5_ULP_MEMIO_ORDER(1U)
+
 #endif  /* __T4_MSG_H */
index 83ec5f7844ac4e11b696a8e8a1ebfd8bd0dd41a0..ef146c0ba4814ef5abff64794075b9d5e806c62b 100644 (file)
 #define  QID_SHIFT   15
 #define  QID(x)      ((x) << QID_SHIFT)
 #define  DBPRIO(x)   ((x) << 14)
+#define  DBTYPE(x)   ((x) << 13)
 #define  PIDX_MASK   0x00003fffU
 #define  PIDX_SHIFT  0
 #define  PIDX(x)     ((x) << PIDX_SHIFT)
+#define  S_PIDX_T5   0
+#define  M_PIDX_T5   0x1fffU
+#define  PIDX_T5(x)  (((x) >> S_PIDX_T5) & M_PIDX_T5)
+
 
 #define SGE_PF_GTS 0x4
 #define  INGRESSQID_MASK   0xffff0000U
 #define  QUEUESPERPAGEPF0_MASK   0x0000000fU
 #define  QUEUESPERPAGEPF0_GET(x) ((x) & QUEUESPERPAGEPF0_MASK)
 
+#define QUEUESPERPAGEPF1    4
+
 #define SGE_INT_CAUSE1 0x1024
 #define SGE_INT_CAUSE2 0x1030
 #define SGE_INT_CAUSE3 0x103c
 #define SGE_DOORBELL_CONTROL 0x10a8
 #define  ENABLE_DROP        (1 << 13)
 
+#define S_NOCOALESCE    26
+#define V_NOCOALESCE(x) ((x) << S_NOCOALESCE)
+#define F_NOCOALESCE    V_NOCOALESCE(1U)
+
 #define SGE_TIMER_VALUE_0_AND_1 0x10b8
 #define  TIMERVALUE0_MASK   0xffff0000U
 #define  TIMERVALUE0_SHIFT  16
 #define S_HP_INT_THRESH    28
 #define M_HP_INT_THRESH 0xfU
 #define V_HP_INT_THRESH(x) ((x) << S_HP_INT_THRESH)
+#define S_LP_INT_THRESH_T5    18
+#define V_LP_INT_THRESH_T5(x) ((x) << S_LP_INT_THRESH_T5)
+#define M_LP_COUNT_T5    0x3ffffU
+#define G_LP_COUNT_T5(x) (((x) >> S_LP_COUNT) & M_LP_COUNT_T5)
 #define M_HP_COUNT 0x7ffU
 #define S_HP_COUNT 16
 #define G_HP_COUNT(x) (((x) >> S_HP_COUNT) & M_HP_COUNT)
 #define S_LP_INT_THRESH    12
 #define M_LP_INT_THRESH 0xfU
+#define M_LP_INT_THRESH_T5    0xfffU
 #define V_LP_INT_THRESH(x) ((x) << S_LP_INT_THRESH)
 #define M_LP_COUNT 0x7ffU
 #define S_LP_COUNT 0
 #define G_LP_COUNT(x) (((x) >> S_LP_COUNT) & M_LP_COUNT)
 #define A_SGE_DBFIFO_STATUS 0x10a4
 
+#define SGE_STAT_TOTAL 0x10e4
+#define SGE_STAT_MATCH 0x10e8
+
+#define SGE_STAT_CFG   0x10ec
+#define S_STATSOURCE_T5    9
+#define STATSOURCE_T5(x) ((x) << S_STATSOURCE_T5)
+
+#define SGE_DBFIFO_STATUS2 0x1118
+#define M_HP_COUNT_T5    0x3ffU
+#define G_HP_COUNT_T5(x) ((x)  & M_HP_COUNT_T5)
+#define S_HP_INT_THRESH_T5    10
+#define M_HP_INT_THRESH_T5    0xfU
+#define V_HP_INT_THRESH_T5(x) ((x) << S_HP_INT_THRESH_T5)
+
 #define S_ENABLE_DROP    13
 #define V_ENABLE_DROP(x) ((x) << S_ENABLE_DROP)
 #define F_ENABLE_DROP    V_ENABLE_DROP(1U)
 #define  MSIADDRHPERR  0x00000002U
 #define  MSIADDRLPERR  0x00000001U
 
+#define  READRSPERR      0x20000000U
+#define  TRGT1GRPPERR    0x10000000U
+#define  IPSOTPERR       0x08000000U
+#define  IPRXDATAGRPPERR 0x02000000U
+#define  IPRXHDRGRPPERR  0x01000000U
+#define  MAGRPPERR       0x00400000U
+#define  VFIDPERR        0x00200000U
+#define  HREQWRPERR      0x00010000U
+#define  DREQWRPERR      0x00002000U
+#define  MSTTAGQPERR     0x00000400U
+#define  PIOREQGRPPERR   0x00000100U
+#define  PIOCPLGRPPERR   0x00000080U
+#define  MSIXSTIPERR     0x00000004U
+#define  MSTTIMEOUTPERR  0x00000002U
+#define  MSTGRPPERR      0x00000001U
+
 #define PCIE_NONFAT_ERR 0x3010
 #define PCIE_MEM_ACCESS_BASE_WIN 0x3068
+#define S_PCIEOFST       10
+#define M_PCIEOFST       0x3fffffU
+#define GET_PCIEOFST(x)  (((x) >> S_PCIEOFST) & M_PCIEOFST)
 #define  PCIEOFST_MASK   0xfffffc00U
 #define  BIR_MASK        0x00000300U
 #define  BIR_SHIFT       8
 #define  WINDOW(x)       ((x) << WINDOW_SHIFT)
 #define PCIE_MEM_ACCESS_OFFSET 0x306c
 
+#define S_PFNUM    0
+#define V_PFNUM(x) ((x) << S_PFNUM)
+
 #define PCIE_FW 0x30b8
 #define  PCIE_FW_ERR           0x80000000U
 #define  PCIE_FW_INIT          0x40000000U
 
 #define MC_BIST_STATUS_RDATA 0x7688
 
+#define MA_EDRAM0_BAR 0x77c0
+#define MA_EDRAM1_BAR 0x77c4
+#define EDRAM_SIZE_MASK   0xfffU
+#define EDRAM_SIZE_GET(x) ((x) & EDRAM_SIZE_MASK)
+
 #define MA_EXT_MEMORY_BAR 0x77c8
 #define  EXT_MEM_SIZE_MASK   0x00000fffU
 #define  EXT_MEM_SIZE_SHIFT  0
 #define  EXT_MEM_SIZE_GET(x) (((x) & EXT_MEM_SIZE_MASK) >> EXT_MEM_SIZE_SHIFT)
 
 #define MA_TARGET_MEM_ENABLE 0x77d8
+#define  EXT_MEM1_ENABLE 0x00000010U
 #define  EXT_MEM_ENABLE 0x00000004U
 #define  EDRAM1_ENABLE  0x00000002U
 #define  EDRAM0_ENABLE  0x00000001U
 #define MA_PCIE_FW 0x30b8
 #define MA_PARITY_ERROR_STATUS 0x77f4
 
+#define MA_EXT_MEMORY1_BAR 0x7808
 #define EDC_0_BASE_ADDR 0x7900
 
 #define EDC_BIST_CMD 0x7904
 #define MPS_PORT_STAT_RX_PORT_PPP7_H 0x60c
 #define MPS_PORT_STAT_RX_PORT_LESS_64B_L 0x610
 #define MPS_PORT_STAT_RX_PORT_LESS_64B_H 0x614
+#define MAC_PORT_CFG2 0x818
+#define MAC_PORT_MAGIC_MACID_LO 0x824
+#define MAC_PORT_MAGIC_MACID_HI 0x828
+#define MAC_PORT_EPIO_DATA0 0x8c0
+#define MAC_PORT_EPIO_DATA1 0x8c4
+#define MAC_PORT_EPIO_DATA2 0x8c8
+#define MAC_PORT_EPIO_DATA3 0x8cc
+#define MAC_PORT_EPIO_OP 0x8d0
+
 #define MPS_CMN_CTL 0x9000
 #define  NUMPORTS_MASK   0x00000003U
 #define  NUMPORTS_SHIFT  0
 #define  ADDRESS_SHIFT  0
 #define  ADDRESS(x)     ((x) << ADDRESS_SHIFT)
 
+#define MAC_PORT_INT_CAUSE 0x8dc
 #define XGMAC_PORT_INT_CAUSE 0x10dc
 
 #define A_TP_TX_MOD_QUEUE_REQ_MAP 0x7e28
 #define V_PORT(x) ((x) << S_PORT)
 #define F_PORT    V_PORT(1U)
 
+#define NUM_MPS_CLS_SRAM_L_INSTANCES 336
+#define NUM_MPS_T5_CLS_SRAM_L_INSTANCES 512
+
+#define T5_PORT0_BASE 0x30000
+#define T5_PORT_STRIDE 0x4000
+#define T5_PORT_BASE(idx) (T5_PORT0_BASE + (idx) * T5_PORT_STRIDE)
+#define T5_PORT_REG(idx, reg) (T5_PORT_BASE(idx) + (reg))
+
+#define MC_0_BASE_ADDR 0x40000
+#define MC_1_BASE_ADDR 0x48000
+#define MC_STRIDE (MC_1_BASE_ADDR - MC_0_BASE_ADDR)
+#define MC_REG(reg, idx) (reg + MC_STRIDE * idx)
+
+#define MC_P_BIST_CMD 0x41400
+#define MC_P_BIST_CMD_ADDR 0x41404
+#define MC_P_BIST_CMD_LEN 0x41408
+#define MC_P_BIST_DATA_PATTERN 0x4140c
+#define MC_P_BIST_STATUS_RDATA 0x41488
+#define EDC_T50_BASE_ADDR 0x50000
+#define EDC_H_BIST_CMD 0x50004
+#define EDC_H_BIST_CMD_ADDR 0x50008
+#define EDC_H_BIST_CMD_LEN 0x5000c
+#define EDC_H_BIST_DATA_PATTERN 0x50010
+#define EDC_H_BIST_STATUS_RDATA 0x50028
+
+#define EDC_T51_BASE_ADDR 0x50800
+#define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR)
+#define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx)
+
 #endif /* __T4_REGS_H */
index a0dcccd846c9a0185b748a0c88ee836bccd48a3e..93444325b1e8e61e0dfa20ff2f3f52614255b7dc 100644 (file)
@@ -574,7 +574,7 @@ struct fw_eth_tx_pkt_vm_wr {
        __be16 vlantci;
 };
 
-#define FW_CMD_MAX_TIMEOUT 3000
+#define FW_CMD_MAX_TIMEOUT 10000
 
 /*
  * If a host driver does a HELLO and discovers that there's already a MASTER
index 68eaa9c88c7d8a77646bd217e00877a281254a59..be5c7ef6ca939654365b6aec6dc108e03e0f74d9 100644 (file)
@@ -344,6 +344,7 @@ struct adapter {
        unsigned long registered_device_map;
        unsigned long open_device_map;
        unsigned long flags;
+       enum chip_type chip;
        struct adapter_params params;
 
        /* queue and interrupt resources */
index 56b46ab2d4c5ae452410da4469ab7b2c86ba1894..73aef76a526cc825b93703c2f7fc97359773b270 100644 (file)
@@ -54,8 +54,8 @@
 /*
  * Generic information about the driver.
  */
-#define DRV_VERSION "1.0.0"
-#define DRV_DESC "Chelsio T4 Virtual Function (VF) Network Driver"
+#define DRV_VERSION "2.0.0-ko"
+#define DRV_DESC "Chelsio T4/T5 Virtual Function (VF) Network Driver"
 
 /*
  * Module Parameters.
@@ -1050,7 +1050,7 @@ static inline unsigned int mk_adap_vers(const struct adapter *adapter)
        /*
         * Chip version 4, revision 0x3f (cxgb4vf).
         */
-       return 4 | (0x3f << 10);
+       return CHELSIO_CHIP_VERSION(adapter->chip) | (0x3f << 10);
 }
 
 /*
@@ -1100,10 +1100,10 @@ static netdev_features_t cxgb4vf_fix_features(struct net_device *dev,
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -1114,9 +1114,9 @@ static int cxgb4vf_set_features(struct net_device *dev,
        struct port_info *pi = netdev_priv(dev);
        netdev_features_t changed = dev->features ^ features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                t4vf_set_rxmode(pi->adapter, pi->viid, -1, -1, -1, -1,
-                               features & NETIF_F_HW_VLAN_TX, 0);
+                               features & NETIF_F_HW_VLAN_CTAG_TX, 0);
 
        return 0;
 }
@@ -2099,6 +2099,15 @@ static int adap_init0(struct adapter *adapter)
                return err;
        }
 
+       switch (adapter->pdev->device >> 12) {
+       case CHELSIO_T4:
+               adapter->chip = CHELSIO_CHIP_CODE(CHELSIO_T4, 0);
+               break;
+       case CHELSIO_T5:
+               adapter->chip = CHELSIO_CHIP_CODE(CHELSIO_T5, 0);
+               break;
+       }
+
        /*
         * Grab basic operational parameters.  These will predominantly have
         * been set up by the Physical Function Driver or will be hard coded
@@ -2614,11 +2623,12 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev,
 
                netdev->hw_features = NETIF_F_SG | TSO_FLAGS |
                        NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
-                       NETIF_F_HW_VLAN_RX | NETIF_F_RXCSUM;
+                       NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXCSUM;
                netdev->vlan_features = NETIF_F_SG | TSO_FLAGS |
                        NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                        NETIF_F_HIGHDMA;
-               netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_TX;
+               netdev->features = netdev->hw_features |
+                                  NETIF_F_HW_VLAN_CTAG_TX;
                if (pci_using_dac)
                        netdev->features |= NETIF_F_HIGHDMA;
 
@@ -2888,6 +2898,26 @@ static struct pci_device_id cxgb4vf_pci_tbl[] = {
        CH_DEVICE(0x480a, 0),   /* T404-bt */
        CH_DEVICE(0x480d, 0),   /* T480-cr */
        CH_DEVICE(0x480e, 0),   /* T440-lp-cr */
+       CH_DEVICE(0x5800, 0),   /* T580-dbg */
+       CH_DEVICE(0x5801, 0),   /* T520-cr */
+       CH_DEVICE(0x5802, 0),   /* T522-cr */
+       CH_DEVICE(0x5803, 0),   /* T540-cr */
+       CH_DEVICE(0x5804, 0),   /* T520-bch */
+       CH_DEVICE(0x5805, 0),   /* T540-bch */
+       CH_DEVICE(0x5806, 0),   /* T540-ch */
+       CH_DEVICE(0x5807, 0),   /* T520-so */
+       CH_DEVICE(0x5808, 0),   /* T520-cx */
+       CH_DEVICE(0x5809, 0),   /* T520-bt */
+       CH_DEVICE(0x580a, 0),   /* T504-bt */
+       CH_DEVICE(0x580b, 0),   /* T520-sr */
+       CH_DEVICE(0x580c, 0),   /* T504-bt */
+       CH_DEVICE(0x580d, 0),   /* T580-cr */
+       CH_DEVICE(0x580e, 0),   /* T540-lp-cr */
+       CH_DEVICE(0x580f, 0),   /* Amsterdam */
+       CH_DEVICE(0x5810, 0),   /* T580-lp-cr */
+       CH_DEVICE(0x5811, 0),   /* T520-lp-cr */
+       CH_DEVICE(0x5812, 0),   /* T560-cr */
+       CH_DEVICE(0x5813, 0),   /* T580-cr */
        { 0, }
 };
 
index 9488032d6d2d568239a0d940a9ca893359d40b4d..df296af20bd52d1052a1c05200841d55dc4acde1 100644 (file)
@@ -528,17 +528,21 @@ static void unmap_rx_buf(struct adapter *adapter, struct sge_fl *fl)
  */
 static inline void ring_fl_db(struct adapter *adapter, struct sge_fl *fl)
 {
+       u32 val;
+
        /*
         * The SGE keeps track of its Producer and Consumer Indices in terms
         * of Egress Queue Units so we can only tell it about integral numbers
         * of multiples of Free List Entries per Egress Queue Units ...
         */
        if (fl->pend_cred >= FL_PER_EQ_UNIT) {
+               val = PIDX(fl->pend_cred / FL_PER_EQ_UNIT);
+               if (!is_t4(adapter->chip))
+                       val |= DBTYPE(1);
                wmb();
                t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_KDOORBELL,
                             DBPRIO(1) |
-                            QID(fl->cntxt_id) |
-                            PIDX(fl->pend_cred / FL_PER_EQ_UNIT));
+                            QID(fl->cntxt_id) | val);
                fl->pend_cred %= FL_PER_EQ_UNIT;
        }
 }
@@ -1478,7 +1482,8 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
        skb_record_rx_queue(skb, rxq->rspq.idx);
 
        if (pkt->vlan_ex) {
-               __vlan_hwaccel_put_tag(skb, be16_to_cpu(pkt->vlan));
+               __vlan_hwaccel_put_tag(skb, cpu_to_be16(ETH_P_8021Q),
+                                       be16_to_cpu(pkt->vlan));
                rxq->stats.vlan_ex++;
        }
        ret = napi_gro_frags(&rxq->rspq.napi);
@@ -1547,7 +1552,7 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
 
        if (pkt->vlan_ex) {
                rxq->stats.vlan_ex++;
-               __vlan_hwaccel_put_tag(skb, be16_to_cpu(pkt->vlan));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), be16_to_cpu(pkt->vlan));
        }
 
        netif_receive_skb(skb);
index 283f9d0d37fd4e4b689e698f965036ba6cda40e2..53cbfed21d0b9871fe257ee75836dc2dadbdc185 100644 (file)
 
 #include "../cxgb4/t4fw_api.h"
 
+#define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision))
+#define CHELSIO_CHIP_VERSION(code) ((code) >> 4)
+#define CHELSIO_CHIP_RELEASE(code) ((code) & 0xf)
+
+#define CHELSIO_T4             0x4
+#define CHELSIO_T5             0x5
+
+enum chip_type {
+       T4_A1 = CHELSIO_CHIP_CODE(CHELSIO_T4, 0),
+       T4_A2 = CHELSIO_CHIP_CODE(CHELSIO_T4, 1),
+       T4_A3 = CHELSIO_CHIP_CODE(CHELSIO_T4, 2),
+       T4_FIRST_REV    = T4_A1,
+       T4_LAST_REV     = T4_A3,
+
+       T5_A1 = CHELSIO_CHIP_CODE(CHELSIO_T5, 0),
+       T5_FIRST_REV    = T5_A1,
+       T5_LAST_REV     = T5_A1,
+};
+
 /*
  * The "len16" field of a Firmware Command Structure ...
  */
@@ -232,6 +251,11 @@ static inline int t4vf_wr_mbox_ns(struct adapter *adapter, const void *cmd,
        return t4vf_wr_mbox_core(adapter, cmd, size, rpl, false);
 }
 
+static inline int is_t4(enum chip_type chip)
+{
+       return (chip >= T4_FIRST_REV && chip <= T4_LAST_REV);
+}
+
 int t4vf_wait_dev_ready(struct adapter *);
 int t4vf_port_init(struct adapter *, int);
 
index 7127c7b9efdeb59474554b70c71b52754a26bc24..9f96dc3bb11203e0781faa785d12ec31df4f76f3 100644 (file)
@@ -1027,8 +1027,11 @@ int t4vf_alloc_mac_filt(struct adapter *adapter, unsigned int viid, bool free,
        unsigned nfilters = 0;
        unsigned int rem = naddr;
        struct fw_vi_mac_cmd cmd, rpl;
+       unsigned int max_naddr = is_t4(adapter->chip) ?
+                                NUM_MPS_CLS_SRAM_L_INSTANCES :
+                                NUM_MPS_T5_CLS_SRAM_L_INSTANCES;
 
-       if (naddr > FW_CLS_TCAM_NUM_ENTRIES)
+       if (naddr > max_naddr)
                return -EINVAL;
 
        for (offset = 0; offset < naddr; /**/) {
@@ -1069,10 +1072,10 @@ int t4vf_alloc_mac_filt(struct adapter *adapter, unsigned int viid, bool free,
 
                        if (idx)
                                idx[offset+i] =
-                                       (index >= FW_CLS_TCAM_NUM_ENTRIES
+                                       (index >= max_naddr
                                         ? 0xffff
                                         : index);
-                       if (index < FW_CLS_TCAM_NUM_ENTRIES)
+                       if (index < max_naddr)
                                nfilters++;
                        else if (hash)
                                *hash |= (1ULL << hash_mac_addr(addr[offset+i]));
@@ -1118,6 +1121,9 @@ int t4vf_change_mac(struct adapter *adapter, unsigned int viid,
        struct fw_vi_mac_exact *p = &cmd.u.exact[0];
        size_t len16 = DIV_ROUND_UP(offsetof(struct fw_vi_mac_cmd,
                                             u.exact[1]), 16);
+       unsigned int max_naddr = is_t4(adapter->chip) ?
+                                NUM_MPS_CLS_SRAM_L_INSTANCES :
+                                NUM_MPS_T5_CLS_SRAM_L_INSTANCES;
 
        /*
         * If this is a new allocation, determine whether it should be
@@ -1140,7 +1146,7 @@ int t4vf_change_mac(struct adapter *adapter, unsigned int viid,
        if (ret == 0) {
                p = &rpl.u.exact[0];
                ret = FW_VI_MAC_CMD_IDX_GET(be16_to_cpu(p->valid_to_idx));
-               if (ret >= FW_CLS_TCAM_NUM_ENTRIES)
+               if (ret >= max_naddr)
                        ret = -ENOMEM;
        }
        return ret;
index 138446957786a9ce0845a7e8221af4887b904ce3..19f642a45f40859874a94663cd3fc503f58014ed 100644 (file)
@@ -101,23 +101,6 @@ static char version[] __initdata =
  * them to system IRQ numbers. This mapping is card specific and is set to
  * the configuration of the Cirrus Eval board for this chip.
  */
-#if defined(CONFIG_MACH_IXDP2351)
-#define CS89x0_NONISA_IRQ
-static unsigned int netcard_portlist[] __used __initdata = {
-       IXDP2351_VIRT_CS8900_BASE, 0
-};
-static unsigned int cs8900_irq_map[] = {
-       IRQ_IXDP2351_CS8900, 0, 0, 0
-};
-#elif defined(CONFIG_ARCH_IXDP2X01)
-#define CS89x0_NONISA_IRQ
-static unsigned int netcard_portlist[] __used __initdata = {
-       IXDP2X01_CS8900_VIRT_BASE, 0
-};
-static unsigned int cs8900_irq_map[] = {
-       IRQ_IXDP2X01_CS8900, 0, 0, 0
-};
-#else
 #ifndef CONFIG_CS89x0_PLATFORM
 static unsigned int netcard_portlist[] __used __initdata = {
        0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240,
@@ -127,7 +110,6 @@ static unsigned int cs8900_irq_map[] = {
        10, 11, 12, 5
 };
 #endif
-#endif
 
 #if DEBUGGING
 static unsigned int net_debug = DEBUGGING;
@@ -210,32 +192,6 @@ static int __init media_fn(char *str)
 __setup("cs89x0_media=", media_fn);
 #endif
 
-#if defined(CONFIG_MACH_IXDP2351)
-static u16
-readword(unsigned long base_addr, int portno)
-{
-       return __raw_readw(base_addr + (portno << 1));
-}
-
-static void
-writeword(unsigned long base_addr, int portno, u16 value)
-{
-       __raw_writew(value, base_addr + (portno << 1));
-}
-#elif defined(CONFIG_ARCH_IXDP2X01)
-static u16
-readword(unsigned long base_addr, int portno)
-{
-       return __raw_readl(base_addr + (portno << 1));
-}
-
-static void
-writeword(unsigned long base_addr, int portno, u16 value)
-{
-       __raw_writel(value, base_addr + (portno << 1));
-}
-#endif
-
 static void readwords(struct net_local *lp, int portno, void *buf, int length)
 {
        u8 *buf8 = (u8 *)buf;
@@ -478,9 +434,6 @@ dma_rx(struct net_device *dev)
        /* Malloc up new buffer. */
        skb = netdev_alloc_skb(dev, length + 2);
        if (skb == NULL) {
-               /* I don't think we want to do this to a stressed system */
-               cs89_dbg(0, err, "%s: Memory squeeze, dropping packet\n",
-                        dev->name);
                dev->stats.rx_dropped++;
 
                /* AKPM: advance bp to the next frame */
@@ -731,9 +684,6 @@ net_rx(struct net_device *dev)
        /* Malloc up new buffer. */
        skb = netdev_alloc_skb(dev, length + 2);
        if (skb == NULL) {
-#if 0          /* Again, this seems a cruel thing to do */
-               pr_warn("%s: Memory squeeze, dropping packet\n", dev->name);
-#endif
                dev->stats.rx_dropped++;
                return;
        }
@@ -908,7 +858,7 @@ net_open(struct net_device *dev)
                        goto bad_out;
                }
        } else {
-#if !defined(CS89x0_NONISA_IRQ) && !defined(CONFIG_CS89x0_PLATFORM)
+#if !defined(CONFIG_CS89x0_PLATFORM)
                if (((1 << dev->irq) & lp->irq_map) == 0) {
                        pr_err("%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
                               dev->name, dev->irq, lp->irq_map);
@@ -1321,9 +1271,7 @@ static const struct net_device_ops net_ops = {
 static void __init reset_chip(struct net_device *dev)
 {
 #if !defined(CONFIG_MACH_MX31ADS)
-#if !defined(CS89x0_NONISA_IRQ)
        struct net_local *lp = netdev_priv(dev);
-#endif /* CS89x0_NONISA_IRQ */
        int reset_start_time;
 
        writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
@@ -1331,7 +1279,6 @@ static void __init reset_chip(struct net_device *dev)
        /* wait 30 ms */
        msleep(30);
 
-#if !defined(CS89x0_NONISA_IRQ)
        if (lp->chip_type != CS8900) {
                /* Hardware problem requires PNP registers to be reconfigured after a reset */
                iowrite16(PP_CS8920_ISAINT, lp->virt_addr + ADD_PORT);
@@ -1344,7 +1291,6 @@ static void __init reset_chip(struct net_device *dev)
                iowrite8((dev->mem_start >> 8) & 0xff,
                         lp->virt_addr + DATA_PORT + 1);
        }
-#endif /* CS89x0_NONISA_IRQ */
 
        /* Wait until the chip is reset */
        reset_start_time = jiffies;
@@ -1579,9 +1525,6 @@ cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular)
                i = lp->isa_config & INT_NO_MASK;
 #ifndef CONFIG_CS89x0_PLATFORM
                if (lp->chip_type == CS8900) {
-#ifdef CS89x0_NONISA_IRQ
-                       i = cs8900_irq_map[0];
-#else
                        /* Translate the IRQ using the IRQ mapping table. */
                        if (i >= ARRAY_SIZE(cs8900_irq_map))
                                pr_err("invalid ISA interrupt number %d\n", i);
@@ -1599,7 +1542,6 @@ cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular)
                                        lp->irq_map = ((irq_map_buff[0] >> 8) |
                                                       (irq_map_buff[1] << 8));
                        }
-#endif
                }
 #endif
                if (!dev->irq)
@@ -1978,18 +1920,6 @@ static struct platform_driver cs89x0_driver = {
        .remove = cs89x0_platform_remove,
 };
 
-static int __init cs89x0_init(void)
-{
-       return platform_driver_probe(&cs89x0_driver, cs89x0_platform_probe);
-}
-
-module_init(cs89x0_init);
-
-static void __exit cs89x0_cleanup(void)
-{
-       platform_driver_unregister(&cs89x0_driver);
-}
-
-module_exit(cs89x0_cleanup);
+module_platform_driver_probe(cs89x0_driver, cs89x0_platform_probe);
 
 #endif /* CONFIG_CS89x0_PLATFORM */
index 354cbb78ed507876c3f7c54f735e2791a4a6d32a..67b0388b6e68223d2281bacb8f69aa330f2193fa 100644 (file)
@@ -887,18 +887,7 @@ static struct platform_driver ep93xx_eth_driver = {
        },
 };
 
-static int __init ep93xx_eth_init_module(void)
-{
-       printk(KERN_INFO DRV_MODULE_NAME " version " DRV_MODULE_VERSION " loading\n");
-       return platform_driver_register(&ep93xx_eth_driver);
-}
-
-static void __exit ep93xx_eth_cleanup_module(void)
-{
-       platform_driver_unregister(&ep93xx_eth_driver);
-}
+module_platform_driver(ep93xx_eth_driver);
 
-module_init(ep93xx_eth_init_module);
-module_exit(ep93xx_eth_cleanup_module);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:ep93xx-eth");
index bf0fc56dba19b70d070edb00e708adea00238878..4b6e5695b263995be898f7fbd6f44108063755d5 100644 (file)
@@ -212,7 +212,7 @@ int enic_dev_deinit_done(struct enic *enic, int *status)
 }
 
 /* rtnl lock is held */
-int enic_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+int enic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct enic *enic = netdev_priv(netdev);
        int err;
@@ -225,7 +225,7 @@ int enic_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
 }
 
 /* rtnl lock is held */
-int enic_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+int enic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct enic *enic = netdev_priv(netdev);
        int err;
index da1cba3c410ec0d1bb9e8acfe99d7876712a577a..08bded051b93c1c0b3436056f20655fc08691a24 100644 (file)
@@ -46,8 +46,8 @@ int enic_dev_packet_filter(struct enic *enic, int directed, int multicast,
        int broadcast, int promisc, int allmulti);
 int enic_dev_add_addr(struct enic *enic, u8 *addr);
 int enic_dev_del_addr(struct enic *enic, u8 *addr);
-int enic_vlan_rx_add_vid(struct net_device *netdev, u16 vid);
-int enic_vlan_rx_kill_vid(struct net_device *netdev, u16 vid);
+int enic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid);
+int enic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid);
 int enic_dev_notify_unset(struct enic *enic);
 int enic_dev_hang_notify(struct enic *enic);
 int enic_dev_set_ig_vlan_rewrite_mode(struct enic *enic);
index ec1a233622c6ae704f9b37bb4a2f4805d2860b1a..635f55992d7e8f2788a5411974e448d7af20f3ec 100644 (file)
@@ -1300,7 +1300,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
                }
 
                if (vlan_stripped)
-                       __vlan_hwaccel_put_tag(skb, vlan_tci);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci);
 
                if (netdev->features & NETIF_F_GRO)
                        napi_gro_receive(&enic->napi[q_number], skb);
@@ -2496,9 +2496,9 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        netdev->watchdog_timeo = 2 * HZ;
        netdev->ethtool_ops = &enic_ethtool_ops;
 
-       netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+       netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
        if (ENIC_SETTING(enic, LOOP)) {
-               netdev->features &= ~NETIF_F_HW_VLAN_TX;
+               netdev->features &= ~NETIF_F_HW_VLAN_CTAG_TX;
                enic->loop_enable = 1;
                enic->loop_tag = enic->config.loop_tag;
                dev_info(dev, "loopback tag=0x%04x\n", enic->loop_tag);
index 605b22283be1b0db0dfc66f8311674411c1723b6..97455c573db540a1963e91a30fda2d69268fe351 100644 (file)
@@ -308,6 +308,9 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 
                        if (status & STAT_ERROR) {
                                err = (int)readq(&devcmd->args[0]);
+                               if (err == ERR_EINVAL &&
+                                   cmd == CMD_CAPABILITY)
+                                       return err;
                                if (err != ERR_ECMDUNKNOWN ||
                                    cmd != CMD_CAPABILITY)
                                        pr_err("Error %d devcmd %d\n",
index 9eada8e86078fe83981223e023bd45f3929b1a70..9105465b2a1a92c5561c99bbda0df7902182ccbb 100644 (file)
@@ -1693,22 +1693,7 @@ static struct platform_driver dm9000_driver = {
        .remove  = dm9000_drv_remove,
 };
 
-static int __init
-dm9000_init(void)
-{
-       printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);
-
-       return platform_driver_register(&dm9000_driver);
-}
-
-static void __exit
-dm9000_cleanup(void)
-{
-       platform_driver_unregister(&dm9000_driver);
-}
-
-module_init(dm9000_init);
-module_exit(dm9000_cleanup);
+module_platform_driver(dm9000_driver);
 
 MODULE_AUTHOR("Sascha Hauer, Ben Dooks");
 MODULE_DESCRIPTION("Davicom DM9000 network driver");
index 88feced9a6293432e3dd5316a8ec48ab284ca3e7..cdbcd16431411bf48ad3cf459c8cb12f3adbf537 100644 (file)
@@ -236,17 +236,14 @@ static int xircom_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        private->rx_buffer = dma_alloc_coherent(d, 8192,
                                                &private->rx_dma_handle,
                                                GFP_KERNEL);
-       if (private->rx_buffer == NULL) {
-               pr_err("%s: no memory for rx buffer\n", __func__);
+       if (private->rx_buffer == NULL)
                goto rx_buf_fail;
-       }
+
        private->tx_buffer = dma_alloc_coherent(d, 8192,
                                                &private->tx_dma_handle,
                                                GFP_KERNEL);
-       if (private->tx_buffer == NULL) {
-               pr_err("%s: no memory for tx buffer\n", __func__);
+       if (private->tx_buffer == NULL)
                goto tx_buf_fail;
-       }
 
        SET_NETDEV_DEV(dev, &pdev->dev);
 
index 110d26f4c6028f37d773576d4dd3a5a2d13fd5b3..afa8e3af2c4d6840a9eee8c9c46d0e3f6df22a02 100644 (file)
@@ -580,12 +580,9 @@ alloc_list (struct net_device *dev)
 
                skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz);
                np->rx_skbuff[i] = skb;
-               if (skb == NULL) {
-                       printk (KERN_ERR
-                               "%s: alloc_list: allocate Rx buffer error! ",
-                               dev->name);
+               if (skb == NULL)
                        break;
-               }
+
                /* Rubicon now supports 40 bits of addressing space. */
                np->rx_ring[i].fraginfo =
                    cpu_to_le64 ( pci_map_single (
index 29aff55f2eea414bf8dec52d61bee70a034fc12c..e2d5ced7e733779fb74c2aafc5e73a46ba45da5c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -214,6 +214,7 @@ struct be_tx_stats {
 };
 
 struct be_tx_obj {
+       u32 db_offset;
        struct be_queue_info q;
        struct be_queue_info cq;
        /* Remember the skbs that were transmitted */
@@ -434,6 +435,7 @@ struct be_adapter {
        u8 wol_cap;
        bool wol;
        u32 uc_macs;            /* Count of secondary UC MAC programmed */
+       u16 asic_rev;
        u32 msg_enable;
        int be_get_temp_freq;
        u16 max_mcast_mac;
index 3c9b4f12e3e516ff8775c208a3b138c9acd5c109..d6291aba25241bcae04ac8362ac733573fd080b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -687,10 +687,8 @@ static struct be_mcc_wrb *wrb_from_mccq(struct be_adapter *adapter)
        if (!mccq->created)
                return NULL;
 
-       if (atomic_read(&mccq->used) >= mccq->len) {
-               dev_err(&adapter->pdev->dev, "Out of MCCQ wrbs\n");
+       if (atomic_read(&mccq->used) >= mccq->len)
                return NULL;
-       }
 
        wrb = queue_head_node(mccq);
        queue_head_inc(mccq);
@@ -1095,15 +1093,14 @@ int be_cmd_mccq_create(struct be_adapter *adapter,
        return status;
 }
 
-int be_cmd_txq_create(struct be_adapter *adapter,
-                       struct be_queue_info *txq,
-                       struct be_queue_info *cq)
+int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_eth_tx_create *req;
+       struct be_queue_info *txq = &txo->q;
+       struct be_queue_info *cq = &txo->cq;
        struct be_dma_mem *q_mem = &txq->dma_mem;
-       void *ctxt;
-       int status;
+       int status, ver = 0;
 
        spin_lock_bh(&adapter->mcc_lock);
 
@@ -1114,34 +1111,37 @@ int be_cmd_txq_create(struct be_adapter *adapter,
        }
 
        req = embedded_payload(wrb);
-       ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
                OPCODE_ETH_TX_CREATE, sizeof(*req), wrb, NULL);
 
        if (lancer_chip(adapter)) {
                req->hdr.version = 1;
-               AMAP_SET_BITS(struct amap_tx_context, if_id, ctxt,
-                                       adapter->if_handle);
+               req->if_id = cpu_to_le16(adapter->if_handle);
+       } else if (BEx_chip(adapter)) {
+               if (adapter->function_caps & BE_FUNCTION_CAPS_SUPER_NIC)
+                       req->hdr.version = 2;
+       } else { /* For SH */
+               req->hdr.version = 2;
        }
 
        req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
        req->ulp_num = BE_ULP1_NUM;
        req->type = BE_ETH_TX_RING_TYPE_STANDARD;
-
-       AMAP_SET_BITS(struct amap_tx_context, tx_ring_size, ctxt,
-               be_encoded_q_len(txq->len));
-       AMAP_SET_BITS(struct amap_tx_context, ctx_valid, ctxt, 1);
-       AMAP_SET_BITS(struct amap_tx_context, cq_id_send, ctxt, cq->id);
-
-       be_dws_cpu_to_le(ctxt, sizeof(req->context));
-
+       req->cq_id = cpu_to_le16(cq->id);
+       req->queue_size = be_encoded_q_len(txq->len);
        be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
 
+       ver = req->hdr.version;
+
        status = be_mcc_notify_wait(adapter);
        if (!status) {
                struct be_cmd_resp_eth_tx_create *resp = embedded_payload(wrb);
                txq->id = le16_to_cpu(resp->cid);
+               if (ver == 2)
+                       txo->db_offset = le32_to_cpu(resp->db_offset);
+               else
+                       txo->db_offset = DB_TXULP1_OFFSET;
                txq->created = true;
        }
 
@@ -1834,7 +1834,7 @@ err:
 
 /* Uses mbox */
 int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
-               u32 *mode, u32 *caps)
+                       u32 *mode, u32 *caps, u16 *asic_rev)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_query_fw_cfg *req;
@@ -1855,6 +1855,7 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
                *port_num = le32_to_cpu(resp->phys_port);
                *mode = le32_to_cpu(resp->function_mode);
                *caps = le32_to_cpu(resp->function_caps);
+               *asic_rev = le32_to_cpu(resp->asic_revision) & 0xFF;
        }
 
        mutex_unlock(&adapter->mbox_lock);
@@ -2343,7 +2344,6 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_seeprom_read *req;
-       struct be_sge *sge;
        int status;
 
        spin_lock_bh(&adapter->mcc_lock);
@@ -2354,7 +2354,6 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
                goto err;
        }
        req = nonemb_cmd->va;
-       sge = nonembedded_sgl(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
                        OPCODE_COMMON_SEEPROM_READ, sizeof(*req), wrb,
@@ -2667,10 +2666,8 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
        cmd.size = sizeof(struct be_cmd_req_set_mac_list);
        cmd.va = dma_alloc_coherent(&adapter->pdev->dev, cmd.size,
                        &cmd.dma, GFP_KERNEL);
-       if (!cmd.va) {
-               dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
+       if (!cmd.va)
                return -ENOMEM;
-       }
 
        spin_lock_bh(&adapter->mcc_lock);
 
@@ -2949,7 +2946,8 @@ static struct be_nic_resource_desc *be_get_nic_desc(u8 *buf, u32 desc_count,
                        break;
                }
 
-               if (desc->desc_type == NIC_RESOURCE_DESC_TYPE_ID)
+               if (desc->desc_type == NIC_RESOURCE_DESC_TYPE_V0 ||
+                   desc->desc_type == NIC_RESOURCE_DESC_TYPE_V1)
                        break;
 
                desc = (void *)desc + desc->desc_len;
@@ -3023,23 +3021,41 @@ err:
        return status;
 }
 
- /* Uses sync mcc */
-int be_cmd_get_profile_config(struct be_adapter *adapter, u32 *cap_flags,
-                             u8 domain)
+/* Uses mbox */
+int be_cmd_get_profile_config_mbox(struct be_adapter *adapter,
+                                  u8 domain, struct be_dma_mem *cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_get_profile_config *req;
        int status;
-       struct be_dma_mem cmd;
 
-       memset(&cmd, 0, sizeof(struct be_dma_mem));
-       cmd.size = sizeof(struct be_cmd_resp_get_profile_config);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
-                                     &cmd.dma);
-       if (!cmd.va) {
-               dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
-               return -ENOMEM;
-       }
+       if (mutex_lock_interruptible(&adapter->mbox_lock))
+               return -1;
+       wrb = wrb_from_mbox(adapter);
+
+       req = cmd->va;
+       be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+                              OPCODE_COMMON_GET_PROFILE_CONFIG,
+                              cmd->size, wrb, cmd);
+
+       req->type = ACTIVE_PROFILE_TYPE;
+       req->hdr.domain = domain;
+       if (!lancer_chip(adapter))
+               req->hdr.version = 1;
+
+       status = be_mbox_notify_wait(adapter);
+
+       mutex_unlock(&adapter->mbox_lock);
+       return status;
+}
+
+/* Uses sync mcc */
+int be_cmd_get_profile_config_mccq(struct be_adapter *adapter,
+                                  u8 domain, struct be_dma_mem *cmd)
+{
+       struct be_mcc_wrb *wrb;
+       struct be_cmd_req_get_profile_config *req;
+       int status;
 
        spin_lock_bh(&adapter->mcc_lock);
 
@@ -3049,16 +3065,47 @@ int be_cmd_get_profile_config(struct be_adapter *adapter, u32 *cap_flags,
                goto err;
        }
 
-       req = cmd.va;
-
+       req = cmd->va;
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
                               OPCODE_COMMON_GET_PROFILE_CONFIG,
-                              cmd.size, wrb, &cmd);
+                              cmd->size, wrb, cmd);
 
        req->type = ACTIVE_PROFILE_TYPE;
        req->hdr.domain = domain;
+       if (!lancer_chip(adapter))
+               req->hdr.version = 1;
 
        status = be_mcc_notify_wait(adapter);
+
+err:
+       spin_unlock_bh(&adapter->mcc_lock);
+       return status;
+}
+
+/* Uses sync mcc, if MCCQ is already created otherwise mbox */
+int be_cmd_get_profile_config(struct be_adapter *adapter, u32 *cap_flags,
+                             u16 *txq_count, u8 domain)
+{
+       struct be_queue_info *mccq = &adapter->mcc_obj.q;
+       struct be_dma_mem cmd;
+       int status;
+
+       memset(&cmd, 0, sizeof(struct be_dma_mem));
+       if (!lancer_chip(adapter))
+               cmd.size = sizeof(struct be_cmd_resp_get_profile_config_v1);
+       else
+               cmd.size = sizeof(struct be_cmd_resp_get_profile_config);
+       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
+                                     &cmd.dma);
+       if (!cmd.va) {
+               dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
+               return -ENOMEM;
+       }
+
+       if (!mccq->created)
+               status = be_cmd_get_profile_config_mbox(adapter, domain, &cmd);
+       else
+               status = be_cmd_get_profile_config_mccq(adapter, domain, &cmd);
        if (!status) {
                struct be_cmd_resp_get_profile_config *resp = cmd.va;
                u32 desc_count = le32_to_cpu(resp->desc_count);
@@ -3071,12 +3118,15 @@ int be_cmd_get_profile_config(struct be_adapter *adapter, u32 *cap_flags,
                        status = -EINVAL;
                        goto err;
                }
-               *cap_flags = le32_to_cpu(desc->cap_flags);
+               if (cap_flags)
+                       *cap_flags = le32_to_cpu(desc->cap_flags);
+               if (txq_count)
+                       *txq_count = le32_to_cpu(desc->txq_count);
        }
 err:
-       spin_unlock_bh(&adapter->mcc_lock);
-       pci_free_consistent(adapter->pdev, cmd.size,
-                           cmd.va, cmd.dma);
+       if (cmd.va)
+               pci_free_consistent(adapter->pdev, cmd.size,
+                                   cmd.va, cmd.dma);
        return status;
 }
 
@@ -3105,7 +3155,7 @@ int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps,
        req->hdr.domain = domain;
        req->desc_count = cpu_to_le32(1);
 
-       req->nic_desc.desc_type = NIC_RESOURCE_DESC_TYPE_ID;
+       req->nic_desc.desc_type = NIC_RESOURCE_DESC_TYPE_V0;
        req->nic_desc.desc_len = RESOURCE_DESC_SIZE;
        req->nic_desc.flags = (1 << QUN) | (1 << IMM) | (1 << NOSV);
        req->nic_desc.pf_num = adapter->pf_number;
@@ -3202,6 +3252,31 @@ err:
        return status;
 }
 
+int be_cmd_intr_set(struct be_adapter *adapter, bool intr_enable)
+{
+       struct be_mcc_wrb *wrb;
+       struct be_cmd_req_intr_set *req;
+       int status;
+
+       if (mutex_lock_interruptible(&adapter->mbox_lock))
+               return -1;
+
+       wrb = wrb_from_mbox(adapter);
+
+       req = embedded_payload(wrb);
+
+       be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+                              OPCODE_COMMON_SET_INTERRUPT_ENABLE, sizeof(*req),
+                              wrb, NULL);
+
+       req->intr_enabled = intr_enable;
+
+       status = be_mbox_notify_wait(adapter);
+
+       mutex_unlock(&adapter->mbox_lock);
+       return status;
+}
+
 int be_roce_mcc_cmd(void *netdev_handle, void *wrb_payload,
                        int wrb_payload_size, u16 *cmd_status, u16 *ext_status)
 {
index 96970860c9150853570f89268095c25b6d04b2ba..460332021590ad36d780df518d3269520000fba6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -188,6 +188,7 @@ struct be_mcc_mailbox {
 #define OPCODE_COMMON_GET_BEACON_STATE                 70
 #define OPCODE_COMMON_READ_TRANSRECV_DATA              73
 #define OPCODE_COMMON_GET_PORT_NAME                    77
+#define OPCODE_COMMON_SET_INTERRUPT_ENABLE             89
 #define OPCODE_COMMON_GET_PHY_DETAILS                  102
 #define OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP          103
 #define OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES   121
@@ -473,46 +474,27 @@ struct be_cmd_resp_mcc_create {
 #define BE_ETH_TX_RING_TYPE_STANDARD           2
 #define BE_ULP1_NUM                            1
 
-/* Pseudo amap definition in which each bit of the actual structure is defined
- * as a byte: used to calculate offset/shift/mask of each field */
-struct amap_tx_context {
-       u8 if_id[16];           /* dword 0 */
-       u8 tx_ring_size[4];     /* dword 0 */
-       u8 rsvd1[26];           /* dword 0 */
-       u8 pci_func_id[8];      /* dword 1 */
-       u8 rsvd2[9];            /* dword 1 */
-       u8 ctx_valid;           /* dword 1 */
-       u8 cq_id_send[16];      /* dword 2 */
-       u8 rsvd3[16];           /* dword 2 */
-       u8 rsvd4[32];           /* dword 3 */
-       u8 rsvd5[32];           /* dword 4 */
-       u8 rsvd6[32];           /* dword 5 */
-       u8 rsvd7[32];           /* dword 6 */
-       u8 rsvd8[32];           /* dword 7 */
-       u8 rsvd9[32];           /* dword 8 */
-       u8 rsvd10[32];          /* dword 9 */
-       u8 rsvd11[32];          /* dword 10 */
-       u8 rsvd12[32];          /* dword 11 */
-       u8 rsvd13[32];          /* dword 12 */
-       u8 rsvd14[32];          /* dword 13 */
-       u8 rsvd15[32];          /* dword 14 */
-       u8 rsvd16[32];          /* dword 15 */
-} __packed;
-
 struct be_cmd_req_eth_tx_create {
        struct be_cmd_req_hdr hdr;
        u8 num_pages;
        u8 ulp_num;
-       u8 type;
-       u8 bound_port;
-       u8 context[sizeof(struct amap_tx_context) / 8];
+       u16 type;
+       u16 if_id;
+       u8 queue_size;
+       u8 rsvd0;
+       u32 rsvd1;
+       u16 cq_id;
+       u16 rsvd2;
+       u32 rsvd3[13];
        struct phys_addr pages[8];
 } __packed;
 
 struct be_cmd_resp_eth_tx_create {
        struct be_cmd_resp_hdr hdr;
        u16 cid;
-       u16 rsvd0;
+       u16 rid;
+       u32 db_offset;
+       u32 rsvd0[4];
 } __packed;
 
 /******************** Create RxQ ***************************/
@@ -1066,7 +1048,6 @@ struct be_cmd_resp_modify_eq_delay {
 } __packed;
 
 /******************** Get FW Config *******************/
-#define BE_FUNCTION_CAPS_RSS                   0x2
 /* The HW can come up in either of the following multi-channel modes
  * based on the skew/IPL.
  */
@@ -1706,9 +1687,11 @@ struct be_cmd_req_set_ext_fat_caps {
        struct be_fat_conf_params set_params;
 };
 
-#define RESOURCE_DESC_SIZE                     72
-#define NIC_RESOURCE_DESC_TYPE_ID              0x41
+#define RESOURCE_DESC_SIZE                     88
+#define NIC_RESOURCE_DESC_TYPE_V0              0x41
+#define NIC_RESOURCE_DESC_TYPE_V1              0x51
 #define MAX_RESOURCE_DESC                      4
+#define MAX_RESOURCE_DESC_V1                   32
 
 /* QOS unit number */
 #define QUN                                    4
@@ -1774,6 +1757,12 @@ struct be_cmd_resp_get_profile_config {
        u8 func_param[MAX_RESOURCE_DESC * RESOURCE_DESC_SIZE];
 };
 
+struct be_cmd_resp_get_profile_config_v1 {
+       struct be_cmd_req_hdr hdr;
+       u32 desc_count;
+       u8 func_param[MAX_RESOURCE_DESC_V1 * RESOURCE_DESC_SIZE];
+};
+
 struct be_cmd_req_set_profile_config {
        struct be_cmd_req_hdr hdr;
        u32 rsvd;
@@ -1791,6 +1780,12 @@ struct be_cmd_enable_disable_vf {
        u8 rsvd[3];
 };
 
+struct be_cmd_req_intr_set {
+       struct be_cmd_req_hdr hdr;
+       u8 intr_enabled;
+       u8 rsvd[3];
+};
+
 static inline bool check_privilege(struct be_adapter *adapter, u32 flags)
 {
        return flags & adapter->cmd_privileges ? true : false;
@@ -1834,8 +1829,7 @@ extern int be_cmd_mccq_create(struct be_adapter *adapter,
                        struct be_queue_info *mccq,
                        struct be_queue_info *cq);
 extern int be_cmd_txq_create(struct be_adapter *adapter,
-                       struct be_queue_info *txq,
-                       struct be_queue_info *cq);
+                       struct be_tx_obj *txo);
 extern int be_cmd_rxq_create(struct be_adapter *adapter,
                        struct be_queue_info *rxq, u16 cq_id,
                        u16 frag_size, u32 if_id, u32 rss, u8 *rss_id);
@@ -1862,8 +1856,8 @@ extern int be_cmd_set_flow_control(struct be_adapter *adapter,
                        u32 tx_fc, u32 rx_fc);
 extern int be_cmd_get_flow_control(struct be_adapter *adapter,
                        u32 *tx_fc, u32 *rx_fc);
-extern int be_cmd_query_fw_cfg(struct be_adapter *adapter,
-                       u32 *port_num, u32 *function_mode, u32 *function_caps);
+extern int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
+                       u32 *function_mode, u32 *function_caps, u16 *asic_rev);
 extern int be_cmd_reset_function(struct be_adapter *adapter);
 extern int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable,
                        u16 table_size);
@@ -1931,10 +1925,11 @@ extern int lancer_test_and_set_rdy_state(struct be_adapter *adapter);
 extern int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name);
 extern int be_cmd_get_func_config(struct be_adapter *adapter);
 extern int be_cmd_get_profile_config(struct be_adapter *adapter, u32 *cap_flags,
-                                    u8 domain);
+                                    u16 *txq_count, u8 domain);
 
 extern int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps,
                                     u8 domain);
 extern int be_cmd_get_if_id(struct be_adapter *adapter,
                            struct be_vf_cfg *vf_cfg, int vf_num);
 extern int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain);
+extern int be_cmd_intr_set(struct be_adapter *adapter, bool intr_enable);
index 76b302f30c8727fe638a8e9783f8246c037f1db6..07b7f27cb0b90885c965e05fbb3bc4803885e344 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -719,10 +719,8 @@ be_test_ddr_dma(struct be_adapter *adapter)
        ddrdma_cmd.size = sizeof(struct be_cmd_req_ddrdma_test);
        ddrdma_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, ddrdma_cmd.size,
                                           &ddrdma_cmd.dma, GFP_KERNEL);
-       if (!ddrdma_cmd.va) {
-               dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
+       if (!ddrdma_cmd.va)
                return -ENOMEM;
-       }
 
        for (i = 0; i < 2; i++) {
                ret = be_cmd_ddr_dma_test(adapter, pattern[i],
@@ -845,11 +843,8 @@ be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
        eeprom_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, eeprom_cmd.size,
                                           &eeprom_cmd.dma, GFP_KERNEL);
 
-       if (!eeprom_cmd.va) {
-               dev_err(&adapter->pdev->dev,
-                       "Memory allocation failure. Could not read eeprom\n");
+       if (!eeprom_cmd.va)
                return -ENOMEM;
-       }
 
        status = be_cmd_get_seeprom_data(adapter, &eeprom_cmd);
 
index 62dc220695f724999c8df269531274ba38ec0b4a..3c1099b47f2a7013da331c9e5b00dca0f0b80946 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
  */
 #define MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK     (1 << 29) /* bit 29 */
 
+/********* PCI Function Capability *********/
+#define BE_FUNCTION_CAPS_RSS                   0x2
+#define BE_FUNCTION_CAPS_SUPER_NIC             0x40
+
 /********* Power management (WOL) **********/
 #define PCICFG_PM_CONTROL_OFFSET               0x44
 #define PCICFG_PM_CONTROL_MASK                 0x108   /* bits 3 & 8 */
@@ -495,7 +499,8 @@ struct flash_file_hdr_g3 {
        u32 antidote;
        u32 num_imgs;
        u8 build[24];
-       u8 rsvd[32];
+       u8 asic_type_rev;
+       u8 rsvd[31];
 };
 
 struct flash_section_hdr {
index 2886c9b63f9099059d3c8c0fe7a86cddc912854f..654e7820daa0705d0a15812d13f5d0f49d4583a5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -146,20 +146,16 @@ static int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q,
        q->entry_size = entry_size;
        mem->size = len * entry_size;
        mem->va = dma_alloc_coherent(&adapter->pdev->dev, mem->size, &mem->dma,
-                                    GFP_KERNEL);
+                                    GFP_KERNEL | __GFP_ZERO);
        if (!mem->va)
                return -ENOMEM;
-       memset(mem->va, 0, mem->size);
        return 0;
 }
 
-static void be_intr_set(struct be_adapter *adapter, bool enable)
+static void be_reg_intr_set(struct be_adapter *adapter, bool enable)
 {
        u32 reg, enabled;
 
-       if (adapter->eeh_error)
-               return;
-
        pci_read_config_dword(adapter->pdev, PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET,
                                &reg);
        enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
@@ -175,6 +171,22 @@ static void be_intr_set(struct be_adapter *adapter, bool enable)
                        PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, reg);
 }
 
+static void be_intr_set(struct be_adapter *adapter, bool enable)
+{
+       int status = 0;
+
+       /* On lancer interrupts can't be controlled via this register */
+       if (lancer_chip(adapter))
+               return;
+
+       if (adapter->eeh_error)
+               return;
+
+       status = be_cmd_intr_set(adapter, enable);
+       if (status)
+               be_reg_intr_set(adapter, enable);
+}
+
 static void be_rxq_notify(struct be_adapter *adapter, u16 qid, u16 posted)
 {
        u32 val = 0;
@@ -185,14 +197,15 @@ static void be_rxq_notify(struct be_adapter *adapter, u16 qid, u16 posted)
        iowrite32(val, adapter->db + DB_RQ_OFFSET);
 }
 
-static void be_txq_notify(struct be_adapter *adapter, u16 qid, u16 posted)
+static void be_txq_notify(struct be_adapter *adapter, struct be_tx_obj *txo,
+                         u16 posted)
 {
        u32 val = 0;
-       val |= qid & DB_TXULP_RING_ID_MASK;
+       val |= txo->q.id & DB_TXULP_RING_ID_MASK;
        val |= (posted & DB_TXULP_NUM_POSTED_MASK) << DB_TXULP_NUM_POSTED_SHIFT;
 
        wmb();
-       iowrite32(val, adapter->db + DB_TXULP1_OFFSET);
+       iowrite32(val, adapter->db + txo->db_offset);
 }
 
 static void be_eq_notify(struct be_adapter *adapter, u16 qid,
@@ -759,7 +772,7 @@ static struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter,
 
        if (vlan_tx_tag_present(skb)) {
                vlan_tag = be_get_tx_vlan_tag(adapter, skb);
-               skb = __vlan_put_tag(skb, vlan_tag);
+               skb = __vlan_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
                if (skb)
                        skb->vlan_tci = 0;
        }
@@ -822,7 +835,7 @@ static netdev_tx_t be_xmit(struct sk_buff *skb,
                        stopped = true;
                }
 
-               be_txq_notify(adapter, txq->id, wrb_cnt);
+               be_txq_notify(adapter, txo, wrb_cnt);
 
                be_tx_stats_update(txo, wrb_cnt, copied, gso_segs, stopped);
        } else {
@@ -891,7 +904,7 @@ set_vlan_promisc:
        return status;
 }
 
-static int be_vlan_add_vid(struct net_device *netdev, u16 vid)
+static int be_vlan_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        int status = 0;
@@ -917,7 +930,7 @@ ret:
        return status;
 }
 
-static int be_vlan_rem_vid(struct net_device *netdev, u16 vid)
+static int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        int status = 0;
@@ -1372,7 +1385,7 @@ static void be_rx_compl_process(struct be_rx_obj *rxo,
 
 
        if (rxcp->vlanf)
-               __vlan_hwaccel_put_tag(skb, rxcp->vlan_tag);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag);
 
        netif_receive_skb(skb);
 }
@@ -1428,7 +1441,7 @@ void be_rx_compl_process_gro(struct be_rx_obj *rxo, struct napi_struct *napi,
                skb->rxhash = rxcp->rss_hash;
 
        if (rxcp->vlanf)
-               __vlan_hwaccel_put_tag(skb, rxcp->vlan_tag);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag);
 
        napi_gro_frags(napi);
 }
@@ -1958,7 +1971,7 @@ static int be_tx_qs_create(struct be_adapter *adapter)
                if (status)
                        return status;
 
-               status = be_cmd_txq_create(adapter, &txo->q, &txo->cq);
+               status = be_cmd_txq_create(adapter, txo);
                if (status)
                        return status;
        }
@@ -2436,9 +2449,6 @@ static int be_close(struct net_device *netdev)
 
        be_roce_dev_close(adapter);
 
-       if (!lancer_chip(adapter))
-               be_intr_set(adapter, false);
-
        for_all_evt_queues(adapter, eqo, i)
                napi_disable(&eqo->napi);
 
@@ -2526,9 +2536,6 @@ static int be_open(struct net_device *netdev)
 
        be_irq_register(adapter);
 
-       if (!lancer_chip(adapter))
-               be_intr_set(adapter, true);
-
        for_all_rx_queues(adapter, rxo, i)
                be_cq_notify(adapter, rxo->cq.id, true, 0);
 
@@ -2563,10 +2570,9 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable)
 
        cmd.size = sizeof(struct be_cmd_req_acpi_wol_magic_config);
        cmd.va = dma_alloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
-                                   GFP_KERNEL);
+                                   GFP_KERNEL | __GFP_ZERO);
        if (cmd.va == NULL)
                return -1;
-       memset(cmd.va, 0, cmd.size);
 
        if (enable) {
                status = pci_write_config_dword(adapter->pdev,
@@ -2714,7 +2720,8 @@ static int be_vfs_if_create(struct be_adapter *adapter)
 
        for_all_vfs(adapter, vf_cfg, vf) {
                if (!BE3_chip(adapter))
-                       be_cmd_get_profile_config(adapter, &cap_flags, vf + 1);
+                       be_cmd_get_profile_config(adapter, &cap_flags,
+                                                 NULL, vf + 1);
 
                /* If a FW profile exists, then cap_flags are updated */
                en_flags = cap_flags & (BE_IF_FLAGS_UNTAGGED |
@@ -2878,11 +2885,14 @@ static void be_get_resources(struct be_adapter *adapter)
        u16 dev_num_vfs;
        int pos, status;
        bool profile_present = false;
+       u16 txq_count = 0;
 
        if (!BEx_chip(adapter)) {
                status = be_cmd_get_func_config(adapter);
                if (!status)
                        profile_present = true;
+       } else if (BE3_chip(adapter) && be_physfn(adapter)) {
+               be_cmd_get_profile_config(adapter, NULL, &txq_count, 0);
        }
 
        if (profile_present) {
@@ -2920,7 +2930,9 @@ static void be_get_resources(struct be_adapter *adapter)
                        adapter->max_vlans = BE_NUM_VLANS_SUPPORTED;
 
                adapter->max_mcast_mac = BE_MAX_MC;
-               adapter->max_tx_queues = MAX_TX_QS;
+               adapter->max_tx_queues = txq_count ? txq_count : MAX_TX_QS;
+               adapter->max_tx_queues = min_t(u16, adapter->max_tx_queues,
+                                              MAX_TX_QS);
                adapter->max_rss_queues = (adapter->be3_native) ?
                                           BE3_MAX_RSS_QS : BE2_MAX_RSS_QS;
                adapter->max_event_queues = BE3_MAX_RSS_QS;
@@ -2954,7 +2966,8 @@ static int be_get_config(struct be_adapter *adapter)
 
        status = be_cmd_query_fw_cfg(adapter, &adapter->port_num,
                                     &adapter->function_mode,
-                                    &adapter->function_caps);
+                                    &adapter->function_caps,
+                                    &adapter->asic_rev);
        if (status)
                goto err;
 
@@ -3215,7 +3228,7 @@ static int be_flash(struct be_adapter *adapter, const u8 *img,
        return 0;
 }
 
-/* For BE2 and BE3 */
+/* 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,
@@ -3458,11 +3471,9 @@ static int lancer_fw_download(struct be_adapter *adapter,
        flash_cmd.size = sizeof(struct lancer_cmd_req_write_object)
                                + LANCER_FW_DOWNLOAD_CHUNK;
        flash_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, flash_cmd.size,
-                                               &flash_cmd.dma, GFP_KERNEL);
+                                         &flash_cmd.dma, GFP_KERNEL);
        if (!flash_cmd.va) {
                status = -ENOMEM;
-               dev_err(&adapter->pdev->dev,
-                       "Memory allocation failure while flashing\n");
                goto lancer_fw_exit;
        }
 
@@ -3530,18 +3541,22 @@ lancer_fw_exit:
 
 #define UFI_TYPE2              2
 #define UFI_TYPE3              3
+#define UFI_TYPE3R             10
 #define UFI_TYPE4              4
 static int be_get_ufi_type(struct be_adapter *adapter,
-                          struct flash_file_hdr_g2 *fhdr)
+                          struct flash_file_hdr_g3 *fhdr)
 {
        if (fhdr == NULL)
                goto be_get_ufi_exit;
 
        if (skyhawk_chip(adapter) && fhdr->build[0] == '4')
                return UFI_TYPE4;
-       else if (BE3_chip(adapter) && fhdr->build[0] == '3')
-               return UFI_TYPE3;
-       else if (BE2_chip(adapter) && fhdr->build[0] == '2')
+       else if (BE3_chip(adapter) && fhdr->build[0] == '3') {
+               if (fhdr->asic_type_rev == 0x10)
+                       return UFI_TYPE3R;
+               else
+                       return UFI_TYPE3;
+       } else if (BE2_chip(adapter) && fhdr->build[0] == '2')
                return UFI_TYPE2;
 
 be_get_ufi_exit:
@@ -3552,7 +3567,6 @@ be_get_ufi_exit:
 
 static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw)
 {
-       struct flash_file_hdr_g2 *fhdr;
        struct flash_file_hdr_g3 *fhdr3;
        struct image_hdr *img_hdr_ptr = NULL;
        struct be_dma_mem flash_cmd;
@@ -3564,29 +3578,41 @@ static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw)
                                          &flash_cmd.dma, GFP_KERNEL);
        if (!flash_cmd.va) {
                status = -ENOMEM;
-               dev_err(&adapter->pdev->dev,
-                       "Memory allocation failure while flashing\n");
                goto be_fw_exit;
        }
 
        p = fw->data;
-       fhdr = (struct flash_file_hdr_g2 *)p;
+       fhdr3 = (struct flash_file_hdr_g3 *)p;
 
-       ufi_type = be_get_ufi_type(adapter, fhdr);
+       ufi_type = be_get_ufi_type(adapter, fhdr3);
 
-       fhdr3 = (struct flash_file_hdr_g3 *)fw->data;
        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 (le32_to_cpu(img_hdr_ptr->imageid) == 1) {
-                       if (ufi_type == UFI_TYPE4)
+                       switch (ufi_type) {
+                       case UFI_TYPE4:
                                status = be_flash_skyhawk(adapter, fw,
                                                        &flash_cmd, num_imgs);
-                       else if (ufi_type == UFI_TYPE3)
+                               break;
+                       case UFI_TYPE3R:
                                status = be_flash_BEx(adapter, fw, &flash_cmd,
                                                      num_imgs);
+                               break;
+                       case UFI_TYPE3:
+                               /* Do not flash this ufi on BE3-R cards */
+                               if (adapter->asic_rev < 0x10)
+                                       status = be_flash_BEx(adapter, fw,
+                                                             &flash_cmd,
+                                                             num_imgs);
+                               else {
+                                       status = -1;
+                                       dev_err(&adapter->pdev->dev,
+                                               "Can't load BE3 UFI on BE3R\n");
+                               }
+                       }
                }
        }
 
@@ -3663,12 +3689,12 @@ static void be_netdev_init(struct net_device *netdev)
 
        netdev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
                NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |
-               NETIF_F_HW_VLAN_TX;
+               NETIF_F_HW_VLAN_CTAG_TX;
        if (be_multi_rxq(adapter))
                netdev->hw_features |= NETIF_F_RXHASH;
 
        netdev->features |= netdev->hw_features |
-               NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
+               NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
 
        netdev->vlan_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
                NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
@@ -3792,12 +3818,13 @@ static int be_ctrl_init(struct be_adapter *adapter)
 
        rx_filter->size = sizeof(struct be_cmd_req_rx_filter);
        rx_filter->va = dma_alloc_coherent(&adapter->pdev->dev, rx_filter->size,
-                                       &rx_filter->dma, GFP_KERNEL);
+                                          &rx_filter->dma,
+                                          GFP_KERNEL | __GFP_ZERO);
        if (rx_filter->va == NULL) {
                status = -ENOMEM;
                goto free_mbox;
        }
-       memset(rx_filter->va, 0, rx_filter->size);
+
        mutex_init(&adapter->mbox_lock);
        spin_lock_init(&adapter->mcc_lock);
        spin_lock_init(&adapter->mcc_cq_lock);
@@ -3839,10 +3866,9 @@ static int be_stats_init(struct be_adapter *adapter)
                cmd->size = sizeof(struct be_cmd_req_get_stats_v1);
 
        cmd->va = dma_alloc_coherent(&adapter->pdev->dev, cmd->size, &cmd->dma,
-                                    GFP_KERNEL);
+                                    GFP_KERNEL | __GFP_ZERO);
        if (cmd->va == NULL)
                return -1;
-       memset(cmd->va, 0, cmd->size);
        return 0;
 }
 
@@ -3854,6 +3880,7 @@ static void be_remove(struct pci_dev *pdev)
                return;
 
        be_roce_dev_remove(adapter);
+       be_intr_set(adapter, false);
 
        cancel_delayed_work_sync(&adapter->func_recovery_work);
 
@@ -4108,6 +4135,11 @@ static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id)
 
        status = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
        if (!status) {
+               status = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
+               if (status < 0) {
+                       dev_err(&pdev->dev, "dma_set_coherent_mask failed\n");
+                       goto free_netdev;
+               }
                netdev->features |= NETIF_F_HIGHDMA;
        } else {
                status = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
@@ -4143,11 +4175,11 @@ static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id)
                        goto ctrl_clean;
        }
 
-       /* The INTR bit may be set in the card when probed by a kdump kernel
-        * after a crash.
-        */
-       if (!lancer_chip(adapter))
-               be_intr_set(adapter, false);
+       /* Wait for interrupts to quiesce after an FLR */
+       msleep(100);
+
+       /* Allow interrupts for other ULPs running on NIC function */
+       be_intr_set(adapter, true);
 
        status = be_stats_init(adapter);
        if (status)
index 55d32aa0a093afa74de8c26661468a97b31d5cc7..f3d126dcc104fa88c8385cb235449fdbf0cdd99e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
index db4ea8081c076771db8ca356f2083d7993265db8..27657299846337eafec3321f3b8e9c3afde72d8b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
index 7c361d1db94cbccdb937368044b30913d891ba7f..21b85fb7d05f68da25f7980b937e61b1c4d9de56 100644 (file)
@@ -780,12 +780,11 @@ static int ftgmac100_alloc_buffers(struct ftgmac100 *priv)
 
        priv->descs = dma_alloc_coherent(priv->dev,
                                         sizeof(struct ftgmac100_descs),
-                                        &priv->descs_dma_addr, GFP_KERNEL);
+                                        &priv->descs_dma_addr,
+                                        GFP_KERNEL | __GFP_ZERO);
        if (!priv->descs)
                return -ENOMEM;
 
-       memset(priv->descs, 0, sizeof(struct ftgmac100_descs));
-
        /* initialize RX ring */
        ftgmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
 
@@ -1350,22 +1349,7 @@ static struct platform_driver ftgmac100_driver = {
        },
 };
 
-/******************************************************************************
- * initialization / finalization
- *****************************************************************************/
-static int __init ftgmac100_init(void)
-{
-       pr_info("Loading version " DRV_VERSION " ...\n");
-       return platform_driver_register(&ftgmac100_driver);
-}
-
-static void __exit ftgmac100_exit(void)
-{
-       platform_driver_unregister(&ftgmac100_driver);
-}
-
-module_init(ftgmac100_init);
-module_exit(ftgmac100_exit);
+module_platform_driver(ftgmac100_driver);
 
 MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
 MODULE_DESCRIPTION("FTGMAC100 driver");
index b5ea8fbd8a76d19597714b8e35772e9037647a86..a6eda8d83138bd791769ba9e45bccf5c1f88c00e 100644 (file)
@@ -732,13 +732,13 @@ static int ftmac100_alloc_buffers(struct ftmac100 *priv)
 {
        int i;
 
-       priv->descs = dma_alloc_coherent(priv->dev, sizeof(struct ftmac100_descs),
-                                        &priv->descs_dma_addr, GFP_KERNEL);
+       priv->descs = dma_alloc_coherent(priv->dev,
+                                        sizeof(struct ftmac100_descs),
+                                        &priv->descs_dma_addr,
+                                        GFP_KERNEL | __GFP_ZERO);
        if (!priv->descs)
                return -ENOMEM;
 
-       memset(priv->descs, 0, sizeof(struct ftmac100_descs));
-
        /* initialize RX ring */
        ftmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
 
index b7d58fe6f531635acebc3d4d7c00e80fc63414e7..549ce13b92acee78b6559e360c427c8720b7493b 100644 (file)
@@ -2,7 +2,8 @@
 # Makefile for the Freescale network device drivers.
 #
 
-obj-$(CONFIG_FEC) += fec.o fec_ptp.o
+obj-$(CONFIG_FEC) += fec.o
+fec-objs :=fec_main.o fec_ptp.o
 obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
 ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
        obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c
deleted file mode 100644 (file)
index 73195f6..0000000
+++ /dev/null
@@ -1,2005 +0,0 @@
-/*
- * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
- * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
- *
- * Right now, I am very wasteful with the buffers.  I allocate memory
- * pages and then divide them into 2K frame buffers.  This way I know I
- * have buffers large enough to hold one frame within one buffer descriptor.
- * Once I get this working, I will use 64 or 128 byte CPM buffers, which
- * will be much more memory efficient and will easily handle lots of
- * small packets.
- *
- * Much better multiple PHY support by Magnus Damm.
- * Copyright (c) 2000 Ericsson Radio Systems AB.
- *
- * Support for FEC controller of ColdFire processors.
- * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
- *
- * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
- * Copyright (c) 2004-2006 Macq Electronique SA.
- *
- * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/bitops.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/phy.h>
-#include <linux/fec.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/of_net.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/regulator/consumer.h>
-
-#include <asm/cacheflush.h>
-
-#ifndef CONFIG_ARM
-#include <asm/coldfire.h>
-#include <asm/mcfsim.h>
-#endif
-
-#include "fec.h"
-
-#if defined(CONFIG_ARM)
-#define FEC_ALIGNMENT  0xf
-#else
-#define FEC_ALIGNMENT  0x3
-#endif
-
-#define DRIVER_NAME    "fec"
-#define FEC_NAPI_WEIGHT        64
-
-/* Pause frame feild and FIFO threshold */
-#define FEC_ENET_FCE   (1 << 5)
-#define FEC_ENET_RSEM_V        0x84
-#define FEC_ENET_RSFL_V        16
-#define FEC_ENET_RAEM_V        0x8
-#define FEC_ENET_RAFL_V        0x8
-#define FEC_ENET_OPD_V 0xFFF0
-
-/* Controller is ENET-MAC */
-#define FEC_QUIRK_ENET_MAC             (1 << 0)
-/* Controller needs driver to swap frame */
-#define FEC_QUIRK_SWAP_FRAME           (1 << 1)
-/* Controller uses gasket */
-#define FEC_QUIRK_USE_GASKET           (1 << 2)
-/* Controller has GBIT support */
-#define FEC_QUIRK_HAS_GBIT             (1 << 3)
-/* Controller has extend desc buffer */
-#define FEC_QUIRK_HAS_BUFDESC_EX       (1 << 4)
-
-static struct platform_device_id fec_devtype[] = {
-       {
-               /* keep it for coldfire */
-               .name = DRIVER_NAME,
-               .driver_data = 0,
-       }, {
-               .name = "imx25-fec",
-               .driver_data = FEC_QUIRK_USE_GASKET,
-       }, {
-               .name = "imx27-fec",
-               .driver_data = 0,
-       }, {
-               .name = "imx28-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
-       }, {
-               .name = "imx6q-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
-                               FEC_QUIRK_HAS_BUFDESC_EX,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(platform, fec_devtype);
-
-enum imx_fec_type {
-       IMX25_FEC = 1,  /* runs on i.mx25/50/53 */
-       IMX27_FEC,      /* runs on i.mx27/35/51 */
-       IMX28_FEC,
-       IMX6Q_FEC,
-};
-
-static const struct of_device_id fec_dt_ids[] = {
-       { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
-       { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
-       { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
-       { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, fec_dt_ids);
-
-static unsigned char macaddr[ETH_ALEN];
-module_param_array(macaddr, byte, NULL, 0);
-MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
-
-#if defined(CONFIG_M5272)
-/*
- * Some hardware gets it MAC address out of local flash memory.
- * if this is non-zero then assume it is the address to get MAC from.
- */
-#if defined(CONFIG_NETtel)
-#define        FEC_FLASHMAC    0xf0006006
-#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
-#define        FEC_FLASHMAC    0xf0006000
-#elif defined(CONFIG_CANCam)
-#define        FEC_FLASHMAC    0xf0020000
-#elif defined (CONFIG_M5272C3)
-#define        FEC_FLASHMAC    (0xffe04000 + 4)
-#elif defined(CONFIG_MOD5272)
-#define FEC_FLASHMAC   0xffc0406b
-#else
-#define        FEC_FLASHMAC    0
-#endif
-#endif /* CONFIG_M5272 */
-
-#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
-#error "FEC: descriptor ring size constants too large"
-#endif
-
-/* Interrupt events/masks. */
-#define FEC_ENET_HBERR ((uint)0x80000000)      /* Heartbeat error */
-#define FEC_ENET_BABR  ((uint)0x40000000)      /* Babbling receiver */
-#define FEC_ENET_BABT  ((uint)0x20000000)      /* Babbling transmitter */
-#define FEC_ENET_GRA   ((uint)0x10000000)      /* Graceful stop complete */
-#define FEC_ENET_TXF   ((uint)0x08000000)      /* Full frame transmitted */
-#define FEC_ENET_TXB   ((uint)0x04000000)      /* A buffer was transmitted */
-#define FEC_ENET_RXF   ((uint)0x02000000)      /* Full frame received */
-#define FEC_ENET_RXB   ((uint)0x01000000)      /* A buffer was received */
-#define FEC_ENET_MII   ((uint)0x00800000)      /* MII interrupt */
-#define FEC_ENET_EBERR ((uint)0x00400000)      /* SDMA bus error */
-
-#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
-#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
-
-/* The FEC stores dest/src/type, data, and checksum for receive packets.
- */
-#define PKT_MAXBUF_SIZE                1518
-#define PKT_MINBUF_SIZE                64
-#define PKT_MAXBLR_SIZE                1520
-
-/*
- * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
- * size bits. Other FEC hardware does not, so we need to take that into
- * account when setting it.
- */
-#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
-    defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM)
-#define        OPT_FRAME_SIZE  (PKT_MAXBUF_SIZE << 16)
-#else
-#define        OPT_FRAME_SIZE  0
-#endif
-
-/* FEC MII MMFR bits definition */
-#define FEC_MMFR_ST            (1 << 30)
-#define FEC_MMFR_OP_READ       (2 << 28)
-#define FEC_MMFR_OP_WRITE      (1 << 28)
-#define FEC_MMFR_PA(v)         ((v & 0x1f) << 23)
-#define FEC_MMFR_RA(v)         ((v & 0x1f) << 18)
-#define FEC_MMFR_TA            (2 << 16)
-#define FEC_MMFR_DATA(v)       (v & 0xffff)
-
-#define FEC_MII_TIMEOUT                30000 /* us */
-
-/* Transmitter timeout */
-#define TX_TIMEOUT (2 * HZ)
-
-#define FEC_PAUSE_FLAG_AUTONEG 0x1
-#define FEC_PAUSE_FLAG_ENABLE  0x2
-
-static int mii_cnt;
-
-static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, int is_ex)
-{
-       struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
-       if (is_ex)
-               return (struct bufdesc *)(ex + 1);
-       else
-               return bdp + 1;
-}
-
-static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, int is_ex)
-{
-       struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
-       if (is_ex)
-               return (struct bufdesc *)(ex - 1);
-       else
-               return bdp - 1;
-}
-
-static void *swap_buffer(void *bufaddr, int len)
-{
-       int i;
-       unsigned int *buf = bufaddr;
-
-       for (i = 0; i < (len + 3) / 4; i++, buf++)
-               *buf = cpu_to_be32(*buf);
-
-       return bufaddr;
-}
-
-static netdev_tx_t
-fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       const struct platform_device_id *id_entry =
-                               platform_get_device_id(fep->pdev);
-       struct bufdesc *bdp;
-       void *bufaddr;
-       unsigned short  status;
-       unsigned int index;
-
-       if (!fep->link) {
-               /* Link is down or autonegotiation is in progress. */
-               return NETDEV_TX_BUSY;
-       }
-
-       /* Fill in a Tx ring entry */
-       bdp = fep->cur_tx;
-
-       status = bdp->cbd_sc;
-
-       if (status & BD_ENET_TX_READY) {
-               /* Ooops.  All transmit buffers are full.  Bail out.
-                * This should not happen, since ndev->tbusy should be set.
-                */
-               printk("%s: tx queue full!.\n", ndev->name);
-               return NETDEV_TX_BUSY;
-       }
-
-       /* Clear all of the status flags */
-       status &= ~BD_ENET_TX_STATS;
-
-       /* Set buffer length and buffer pointer */
-       bufaddr = skb->data;
-       bdp->cbd_datlen = skb->len;
-
-       /*
-        * On some FEC implementations data must be aligned on
-        * 4-byte boundaries. Use bounce buffers to copy data
-        * and get it aligned. Ugh.
-        */
-       if (fep->bufdesc_ex)
-               index = (struct bufdesc_ex *)bdp -
-                       (struct bufdesc_ex *)fep->tx_bd_base;
-       else
-               index = bdp - fep->tx_bd_base;
-
-       if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
-               memcpy(fep->tx_bounce[index], skb->data, skb->len);
-               bufaddr = fep->tx_bounce[index];
-       }
-
-       /*
-        * Some design made an incorrect assumption on endian mode of
-        * the system that it's running on. As the result, driver has to
-        * swap every frame going to and coming from the controller.
-        */
-       if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
-               swap_buffer(bufaddr, skb->len);
-
-       /* Save skb pointer */
-       fep->tx_skbuff[index] = skb;
-
-       /* Push the data cache so the CPM does not get stale memory
-        * data.
-        */
-       bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
-                       FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
-
-       /* Send it on its way.  Tell FEC it's ready, interrupt when done,
-        * it's the last BD of the frame, and to put the CRC on the end.
-        */
-       status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
-                       | BD_ENET_TX_LAST | BD_ENET_TX_TC);
-       bdp->cbd_sc = status;
-
-       if (fep->bufdesc_ex) {
-
-               struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-               ebdp->cbd_bdu = 0;
-               if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
-                       fep->hwts_tx_en)) {
-                       ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
-                       skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
-               } else {
-
-                       ebdp->cbd_esc = BD_ENET_TX_INT;
-               }
-       }
-       /* If this was the last BD in the ring, start at the beginning again. */
-       if (status & BD_ENET_TX_WRAP)
-               bdp = fep->tx_bd_base;
-       else
-               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-
-       fep->cur_tx = bdp;
-
-       if (fep->cur_tx == fep->dirty_tx)
-               netif_stop_queue(ndev);
-
-       /* Trigger transmission start */
-       writel(0, fep->hwp + FEC_X_DES_ACTIVE);
-
-       skb_tx_timestamp(skb);
-
-       return NETDEV_TX_OK;
-}
-
-/* Init RX & TX buffer descriptors
- */
-static void fec_enet_bd_init(struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-       struct bufdesc *bdp;
-       unsigned int i;
-
-       /* Initialize the receive buffer descriptors. */
-       bdp = fep->rx_bd_base;
-       for (i = 0; i < RX_RING_SIZE; i++) {
-
-               /* Initialize the BD for every fragment in the page. */
-               if (bdp->cbd_bufaddr)
-                       bdp->cbd_sc = BD_ENET_RX_EMPTY;
-               else
-                       bdp->cbd_sc = 0;
-               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-       }
-
-       /* Set the last buffer to wrap */
-       bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
-       bdp->cbd_sc |= BD_SC_WRAP;
-
-       fep->cur_rx = fep->rx_bd_base;
-
-       /* ...and the same for transmit */
-       bdp = fep->tx_bd_base;
-       fep->cur_tx = bdp;
-       for (i = 0; i < TX_RING_SIZE; i++) {
-
-               /* Initialize the BD for every fragment in the page. */
-               bdp->cbd_sc = 0;
-               if (bdp->cbd_bufaddr && fep->tx_skbuff[i]) {
-                       dev_kfree_skb_any(fep->tx_skbuff[i]);
-                       fep->tx_skbuff[i] = NULL;
-               }
-               bdp->cbd_bufaddr = 0;
-               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-       }
-
-       /* Set the last buffer to wrap */
-       bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
-       bdp->cbd_sc |= BD_SC_WRAP;
-       fep->dirty_tx = bdp;
-}
-
-/* This function is called to start or restart the FEC during a link
- * change.  This only happens when switching between half and full
- * duplex.
- */
-static void
-fec_restart(struct net_device *ndev, int duplex)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       const struct platform_device_id *id_entry =
-                               platform_get_device_id(fep->pdev);
-       int i;
-       u32 temp_mac[2];
-       u32 rcntl = OPT_FRAME_SIZE | 0x04;
-       u32 ecntl = 0x2; /* ETHEREN */
-
-       /* Whack a reset.  We should wait for this. */
-       writel(1, fep->hwp + FEC_ECNTRL);
-       udelay(10);
-
-       /*
-        * enet-mac reset will reset mac address registers too,
-        * so need to reconfigure it.
-        */
-       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
-               memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
-               writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
-               writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
-       }
-
-       /* Clear any outstanding interrupt. */
-       writel(0xffc00000, fep->hwp + FEC_IEVENT);
-
-       /* Reset all multicast. */
-       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
-       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-#ifndef CONFIG_M5272
-       writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
-       writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
-#endif
-
-       /* Set maximum receive buffer size. */
-       writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
-
-       fec_enet_bd_init(ndev);
-
-       /* Set receive and transmit descriptor base. */
-       writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
-       if (fep->bufdesc_ex)
-               writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
-                       * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
-       else
-               writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
-                       * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
-
-
-       for (i = 0; i <= TX_RING_MOD_MASK; i++) {
-               if (fep->tx_skbuff[i]) {
-                       dev_kfree_skb_any(fep->tx_skbuff[i]);
-                       fep->tx_skbuff[i] = NULL;
-               }
-       }
-
-       /* Enable MII mode */
-       if (duplex) {
-               /* FD enable */
-               writel(0x04, fep->hwp + FEC_X_CNTRL);
-       } else {
-               /* No Rcv on Xmit */
-               rcntl |= 0x02;
-               writel(0x0, fep->hwp + FEC_X_CNTRL);
-       }
-
-       fep->full_duplex = duplex;
-
-       /* Set MII speed */
-       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
-
-       /*
-        * The phy interface and speed need to get configured
-        * differently on enet-mac.
-        */
-       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
-               /* Enable flow control and length check */
-               rcntl |= 0x40000000 | 0x00000020;
-
-               /* RGMII, RMII or MII */
-               if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
-                       rcntl |= (1 << 6);
-               else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
-                       rcntl |= (1 << 8);
-               else
-                       rcntl &= ~(1 << 8);
-
-               /* 1G, 100M or 10M */
-               if (fep->phy_dev) {
-                       if (fep->phy_dev->speed == SPEED_1000)
-                               ecntl |= (1 << 5);
-                       else if (fep->phy_dev->speed == SPEED_100)
-                               rcntl &= ~(1 << 9);
-                       else
-                               rcntl |= (1 << 9);
-               }
-       } else {
-#ifdef FEC_MIIGSK_ENR
-               if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
-                       u32 cfgr;
-                       /* disable the gasket and wait */
-                       writel(0, fep->hwp + FEC_MIIGSK_ENR);
-                       while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
-                               udelay(1);
-
-                       /*
-                        * configure the gasket:
-                        *   RMII, 50 MHz, no loopback, no echo
-                        *   MII, 25 MHz, no loopback, no echo
-                        */
-                       cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
-                               ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
-                       if (fep->phy_dev && fep->phy_dev->speed == SPEED_10)
-                               cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
-                       writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
-
-                       /* re-enable the gasket */
-                       writel(2, fep->hwp + FEC_MIIGSK_ENR);
-               }
-#endif
-       }
-
-       /* enable pause frame*/
-       if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
-           ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
-            fep->phy_dev && fep->phy_dev->pause)) {
-               rcntl |= FEC_ENET_FCE;
-
-               /* set FIFO thresh hold parameter to reduce overrun */
-               writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
-               writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
-               writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
-               writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
-
-               /* OPD */
-               writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
-       } else {
-               rcntl &= ~FEC_ENET_FCE;
-       }
-
-       writel(rcntl, fep->hwp + FEC_R_CNTRL);
-
-       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
-               /* enable ENET endian swap */
-               ecntl |= (1 << 8);
-               /* enable ENET store and forward mode */
-               writel(1 << 8, fep->hwp + FEC_X_WMRK);
-       }
-
-       if (fep->bufdesc_ex)
-               ecntl |= (1 << 4);
-
-       /* And last, enable the transmit and receive processing */
-       writel(ecntl, fep->hwp + FEC_ECNTRL);
-       writel(0, fep->hwp + FEC_R_DES_ACTIVE);
-
-       if (fep->bufdesc_ex)
-               fec_ptp_start_cyclecounter(ndev);
-
-       /* Enable interrupts we wish to service */
-       writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
-}
-
-static void
-fec_stop(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       const struct platform_device_id *id_entry =
-                               platform_get_device_id(fep->pdev);
-       u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
-
-       /* We cannot expect a graceful transmit stop without link !!! */
-       if (fep->link) {
-               writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
-               udelay(10);
-               if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
-                       printk("fec_stop : Graceful transmit stop did not complete !\n");
-       }
-
-       /* Whack a reset.  We should wait for this. */
-       writel(1, fep->hwp + FEC_ECNTRL);
-       udelay(10);
-       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
-       writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
-
-       /* We have to keep ENET enabled to have MII interrupt stay working */
-       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
-               writel(2, fep->hwp + FEC_ECNTRL);
-               writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
-       }
-}
-
-
-static void
-fec_timeout(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       ndev->stats.tx_errors++;
-
-       fec_restart(ndev, fep->full_duplex);
-       netif_wake_queue(ndev);
-}
-
-static void
-fec_enet_tx(struct net_device *ndev)
-{
-       struct  fec_enet_private *fep;
-       struct bufdesc *bdp;
-       unsigned short status;
-       struct  sk_buff *skb;
-       int     index = 0;
-
-       fep = netdev_priv(ndev);
-       bdp = fep->dirty_tx;
-
-       /* get next bdp of dirty_tx */
-       if (bdp->cbd_sc & BD_ENET_TX_WRAP)
-               bdp = fep->tx_bd_base;
-       else
-               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-
-       while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
-
-               /* current queue is empty */
-               if (bdp == fep->cur_tx)
-                       break;
-
-               if (fep->bufdesc_ex)
-                       index = (struct bufdesc_ex *)bdp -
-                               (struct bufdesc_ex *)fep->tx_bd_base;
-               else
-                       index = bdp - fep->tx_bd_base;
-
-               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
-                               FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
-               bdp->cbd_bufaddr = 0;
-
-               skb = fep->tx_skbuff[index];
-
-               /* Check for errors. */
-               if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
-                                  BD_ENET_TX_RL | BD_ENET_TX_UN |
-                                  BD_ENET_TX_CSL)) {
-                       ndev->stats.tx_errors++;
-                       if (status & BD_ENET_TX_HB)  /* No heartbeat */
-                               ndev->stats.tx_heartbeat_errors++;
-                       if (status & BD_ENET_TX_LC)  /* Late collision */
-                               ndev->stats.tx_window_errors++;
-                       if (status & BD_ENET_TX_RL)  /* Retrans limit */
-                               ndev->stats.tx_aborted_errors++;
-                       if (status & BD_ENET_TX_UN)  /* Underrun */
-                               ndev->stats.tx_fifo_errors++;
-                       if (status & BD_ENET_TX_CSL) /* Carrier lost */
-                               ndev->stats.tx_carrier_errors++;
-               } else {
-                       ndev->stats.tx_packets++;
-               }
-
-               if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
-                       fep->bufdesc_ex) {
-                       struct skb_shared_hwtstamps shhwtstamps;
-                       unsigned long flags;
-                       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-
-                       memset(&shhwtstamps, 0, sizeof(shhwtstamps));
-                       spin_lock_irqsave(&fep->tmreg_lock, flags);
-                       shhwtstamps.hwtstamp = ns_to_ktime(
-                               timecounter_cyc2time(&fep->tc, ebdp->ts));
-                       spin_unlock_irqrestore(&fep->tmreg_lock, flags);
-                       skb_tstamp_tx(skb, &shhwtstamps);
-               }
-
-               if (status & BD_ENET_TX_READY)
-                       printk("HEY! Enet xmit interrupt and TX_READY.\n");
-
-               /* Deferred means some collisions occurred during transmit,
-                * but we eventually sent the packet OK.
-                */
-               if (status & BD_ENET_TX_DEF)
-                       ndev->stats.collisions++;
-
-               /* Free the sk buffer associated with this last transmit */
-               dev_kfree_skb_any(skb);
-               fep->tx_skbuff[index] = NULL;
-
-               fep->dirty_tx = bdp;
-
-               /* Update pointer to next buffer descriptor to be transmitted */
-               if (status & BD_ENET_TX_WRAP)
-                       bdp = fep->tx_bd_base;
-               else
-                       bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-
-               /* Since we have freed up a buffer, the ring is no longer full
-                */
-               if (fep->dirty_tx != fep->cur_tx) {
-                       if (netif_queue_stopped(ndev))
-                               netif_wake_queue(ndev);
-               }
-       }
-       return;
-}
-
-
-/* During a receive, the cur_rx points to the current incoming buffer.
- * When we update through the ring, if the next incoming buffer has
- * not been given to the system, we just set the empty indicator,
- * effectively tossing the packet.
- */
-static int
-fec_enet_rx(struct net_device *ndev, int budget)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       const struct platform_device_id *id_entry =
-                               platform_get_device_id(fep->pdev);
-       struct bufdesc *bdp;
-       unsigned short status;
-       struct  sk_buff *skb;
-       ushort  pkt_len;
-       __u8 *data;
-       int     pkt_received = 0;
-
-#ifdef CONFIG_M532x
-       flush_cache_all();
-#endif
-
-       /* First, grab all of the stats for the incoming packet.
-        * These get messed up if we get called due to a busy condition.
-        */
-       bdp = fep->cur_rx;
-
-       while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
-
-               if (pkt_received >= budget)
-                       break;
-               pkt_received++;
-
-               /* Since we have allocated space to hold a complete frame,
-                * the last indicator should be set.
-                */
-               if ((status & BD_ENET_RX_LAST) == 0)
-                       printk("FEC ENET: rcv is not +last\n");
-
-               if (!fep->opened)
-                       goto rx_processing_done;
-
-               /* Check for errors. */
-               if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
-                          BD_ENET_RX_CR | BD_ENET_RX_OV)) {
-                       ndev->stats.rx_errors++;
-                       if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
-                               /* Frame too long or too short. */
-                               ndev->stats.rx_length_errors++;
-                       }
-                       if (status & BD_ENET_RX_NO)     /* Frame alignment */
-                               ndev->stats.rx_frame_errors++;
-                       if (status & BD_ENET_RX_CR)     /* CRC Error */
-                               ndev->stats.rx_crc_errors++;
-                       if (status & BD_ENET_RX_OV)     /* FIFO overrun */
-                               ndev->stats.rx_fifo_errors++;
-               }
-
-               /* Report late collisions as a frame error.
-                * On this error, the BD is closed, but we don't know what we
-                * have in the buffer.  So, just drop this frame on the floor.
-                */
-               if (status & BD_ENET_RX_CL) {
-                       ndev->stats.rx_errors++;
-                       ndev->stats.rx_frame_errors++;
-                       goto rx_processing_done;
-               }
-
-               /* Process the incoming frame. */
-               ndev->stats.rx_packets++;
-               pkt_len = bdp->cbd_datlen;
-               ndev->stats.rx_bytes += pkt_len;
-               data = (__u8*)__va(bdp->cbd_bufaddr);
-
-               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
-                               FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
-
-               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
-                       swap_buffer(data, pkt_len);
-
-               /* This does 16 byte alignment, exactly what we need.
-                * The packet length includes FCS, but we don't want to
-                * include that when passing upstream as it messes up
-                * bridging applications.
-                */
-               skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN);
-
-               if (unlikely(!skb)) {
-                       printk("%s: Memory squeeze, dropping packet.\n",
-                                       ndev->name);
-                       ndev->stats.rx_dropped++;
-               } else {
-                       skb_reserve(skb, NET_IP_ALIGN);
-                       skb_put(skb, pkt_len - 4);      /* Make room */
-                       skb_copy_to_linear_data(skb, data, pkt_len - 4);
-                       skb->protocol = eth_type_trans(skb, ndev);
-
-                       /* Get receive timestamp from the skb */
-                       if (fep->hwts_rx_en && fep->bufdesc_ex) {
-                               struct skb_shared_hwtstamps *shhwtstamps =
-                                                           skb_hwtstamps(skb);
-                               unsigned long flags;
-                               struct bufdesc_ex *ebdp =
-                                       (struct bufdesc_ex *)bdp;
-
-                               memset(shhwtstamps, 0, sizeof(*shhwtstamps));
-
-                               spin_lock_irqsave(&fep->tmreg_lock, flags);
-                               shhwtstamps->hwtstamp = ns_to_ktime(
-                                   timecounter_cyc2time(&fep->tc, ebdp->ts));
-                               spin_unlock_irqrestore(&fep->tmreg_lock, flags);
-                       }
-
-                       if (!skb_defer_rx_timestamp(skb))
-                               napi_gro_receive(&fep->napi, skb);
-               }
-
-               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
-                               FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
-rx_processing_done:
-               /* Clear the status flags for this buffer */
-               status &= ~BD_ENET_RX_STATS;
-
-               /* Mark the buffer empty */
-               status |= BD_ENET_RX_EMPTY;
-               bdp->cbd_sc = status;
-
-               if (fep->bufdesc_ex) {
-                       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-
-                       ebdp->cbd_esc = BD_ENET_RX_INT;
-                       ebdp->cbd_prot = 0;
-                       ebdp->cbd_bdu = 0;
-               }
-
-               /* Update BD pointer to next entry */
-               if (status & BD_ENET_RX_WRAP)
-                       bdp = fep->rx_bd_base;
-               else
-                       bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-               /* Doing this here will keep the FEC running while we process
-                * incoming frames.  On a heavily loaded network, we should be
-                * able to keep up at the expense of system resources.
-                */
-               writel(0, fep->hwp + FEC_R_DES_ACTIVE);
-       }
-       fep->cur_rx = bdp;
-
-       return pkt_received;
-}
-
-static irqreturn_t
-fec_enet_interrupt(int irq, void *dev_id)
-{
-       struct net_device *ndev = dev_id;
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       uint int_events;
-       irqreturn_t ret = IRQ_NONE;
-
-       do {
-               int_events = readl(fep->hwp + FEC_IEVENT);
-               writel(int_events, fep->hwp + FEC_IEVENT);
-
-               if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
-                       ret = IRQ_HANDLED;
-
-                       /* Disable the RX interrupt */
-                       if (napi_schedule_prep(&fep->napi)) {
-                               writel(FEC_RX_DISABLED_IMASK,
-                                       fep->hwp + FEC_IMASK);
-                               __napi_schedule(&fep->napi);
-                       }
-               }
-
-               if (int_events & FEC_ENET_MII) {
-                       ret = IRQ_HANDLED;
-                       complete(&fep->mdio_done);
-               }
-       } while (int_events);
-
-       return ret;
-}
-
-static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
-{
-       struct net_device *ndev = napi->dev;
-       int pkts = fec_enet_rx(ndev, budget);
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       fec_enet_tx(ndev);
-
-       if (pkts < budget) {
-               napi_complete(napi);
-               writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
-       }
-       return pkts;
-}
-
-/* ------------------------------------------------------------------------- */
-static void fec_get_mac(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
-       unsigned char *iap, tmpaddr[ETH_ALEN];
-
-       /*
-        * try to get mac address in following order:
-        *
-        * 1) module parameter via kernel command line in form
-        *    fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
-        */
-       iap = macaddr;
-
-#ifdef CONFIG_OF
-       /*
-        * 2) from device tree data
-        */
-       if (!is_valid_ether_addr(iap)) {
-               struct device_node *np = fep->pdev->dev.of_node;
-               if (np) {
-                       const char *mac = of_get_mac_address(np);
-                       if (mac)
-                               iap = (unsigned char *) mac;
-               }
-       }
-#endif
-
-       /*
-        * 3) from flash or fuse (via platform data)
-        */
-       if (!is_valid_ether_addr(iap)) {
-#ifdef CONFIG_M5272
-               if (FEC_FLASHMAC)
-                       iap = (unsigned char *)FEC_FLASHMAC;
-#else
-               if (pdata)
-                       iap = (unsigned char *)&pdata->mac;
-#endif
-       }
-
-       /*
-        * 4) FEC mac registers set by bootloader
-        */
-       if (!is_valid_ether_addr(iap)) {
-               *((unsigned long *) &tmpaddr[0]) =
-                       be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW));
-               *((unsigned short *) &tmpaddr[4]) =
-                       be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
-               iap = &tmpaddr[0];
-       }
-
-       memcpy(ndev->dev_addr, iap, ETH_ALEN);
-
-       /* Adjust MAC if using macaddr */
-       if (iap == macaddr)
-                ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/*
- * Phy section
- */
-static void fec_enet_adjust_link(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct phy_device *phy_dev = fep->phy_dev;
-       unsigned long flags;
-
-       int status_change = 0;
-
-       spin_lock_irqsave(&fep->hw_lock, flags);
-
-       /* Prevent a state halted on mii error */
-       if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
-               phy_dev->state = PHY_RESUMING;
-               goto spin_unlock;
-       }
-
-       if (phy_dev->link) {
-               if (!fep->link) {
-                       fep->link = phy_dev->link;
-                       status_change = 1;
-               }
-
-               if (fep->full_duplex != phy_dev->duplex)
-                       status_change = 1;
-
-               if (phy_dev->speed != fep->speed) {
-                       fep->speed = phy_dev->speed;
-                       status_change = 1;
-               }
-
-               /* if any of the above changed restart the FEC */
-               if (status_change)
-                       fec_restart(ndev, phy_dev->duplex);
-       } else {
-               if (fep->link) {
-                       fec_stop(ndev);
-                       fep->link = phy_dev->link;
-                       status_change = 1;
-               }
-       }
-
-spin_unlock:
-       spin_unlock_irqrestore(&fep->hw_lock, flags);
-
-       if (status_change)
-               phy_print_status(phy_dev);
-}
-
-static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
-{
-       struct fec_enet_private *fep = bus->priv;
-       unsigned long time_left;
-
-       fep->mii_timeout = 0;
-       init_completion(&fep->mdio_done);
-
-       /* start a read op */
-       writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
-               FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
-               FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
-
-       /* wait for end of transfer */
-       time_left = wait_for_completion_timeout(&fep->mdio_done,
-                       usecs_to_jiffies(FEC_MII_TIMEOUT));
-       if (time_left == 0) {
-               fep->mii_timeout = 1;
-               printk(KERN_ERR "FEC: MDIO read timeout\n");
-               return -ETIMEDOUT;
-       }
-
-       /* return value */
-       return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
-}
-
-static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
-                          u16 value)
-{
-       struct fec_enet_private *fep = bus->priv;
-       unsigned long time_left;
-
-       fep->mii_timeout = 0;
-       init_completion(&fep->mdio_done);
-
-       /* start a write op */
-       writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
-               FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
-               FEC_MMFR_TA | FEC_MMFR_DATA(value),
-               fep->hwp + FEC_MII_DATA);
-
-       /* wait for end of transfer */
-       time_left = wait_for_completion_timeout(&fep->mdio_done,
-                       usecs_to_jiffies(FEC_MII_TIMEOUT));
-       if (time_left == 0) {
-               fep->mii_timeout = 1;
-               printk(KERN_ERR "FEC: MDIO write timeout\n");
-               return -ETIMEDOUT;
-       }
-
-       return 0;
-}
-
-static int fec_enet_mdio_reset(struct mii_bus *bus)
-{
-       return 0;
-}
-
-static int fec_enet_mii_probe(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       const struct platform_device_id *id_entry =
-                               platform_get_device_id(fep->pdev);
-       struct phy_device *phy_dev = NULL;
-       char mdio_bus_id[MII_BUS_ID_SIZE];
-       char phy_name[MII_BUS_ID_SIZE + 3];
-       int phy_id;
-       int dev_id = fep->dev_id;
-
-       fep->phy_dev = NULL;
-
-       /* 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)
-                       continue;
-               if (dev_id--)
-                       continue;
-               strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
-               break;
-       }
-
-       if (phy_id >= PHY_MAX_ADDR) {
-               printk(KERN_INFO
-                       "%s: no PHY, assuming direct connection to switch\n",
-                       ndev->name);
-               strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
-               phy_id = 0;
-       }
-
-       snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id);
-       phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
-                             fep->phy_interface);
-       if (IS_ERR(phy_dev)) {
-               printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name);
-               return PTR_ERR(phy_dev);
-       }
-
-       /* mask with MAC supported features */
-       if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
-               phy_dev->supported &= PHY_GBIT_FEATURES;
-               phy_dev->supported |= SUPPORTED_Pause;
-       }
-       else
-               phy_dev->supported &= PHY_BASIC_FEATURES;
-
-       phy_dev->advertising = phy_dev->supported;
-
-       fep->phy_dev = phy_dev;
-       fep->link = 0;
-       fep->full_duplex = 0;
-
-       printk(KERN_INFO
-               "%s: Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
-               ndev->name,
-               fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
-               fep->phy_dev->irq);
-
-       return 0;
-}
-
-static int fec_enet_mii_init(struct platform_device *pdev)
-{
-       static struct mii_bus *fec0_mii_bus;
-       struct net_device *ndev = platform_get_drvdata(pdev);
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       const struct platform_device_id *id_entry =
-                               platform_get_device_id(fep->pdev);
-       int err = -ENXIO, i;
-
-       /*
-        * The dual fec interfaces are not equivalent with enet-mac.
-        * Here are the differences:
-        *
-        *  - fec0 supports MII & RMII modes while fec1 only supports RMII
-        *  - fec0 acts as the 1588 time master while fec1 is slave
-        *  - external phys can only be configured by fec0
-        *
-        * That is to say fec1 can not work independently. It only works
-        * when fec0 is working. The reason behind this design is that the
-        * second interface is added primarily for Switch mode.
-        *
-        * Because of the last point above, both phys are attached on fec0
-        * mdio interface in board design, and need to be configured by
-        * fec0 mii_bus.
-        */
-       if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
-               /* fec1 uses fec0 mii_bus */
-               if (mii_cnt && fec0_mii_bus) {
-                       fep->mii_bus = fec0_mii_bus;
-                       mii_cnt++;
-                       return 0;
-               }
-               return -ENOENT;
-       }
-
-       fep->mii_timeout = 0;
-
-       /*
-        * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
-        *
-        * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
-        * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'.  The i.MX28
-        * Reference Manual has an error on this, and gets fixed on i.MX6Q
-        * document.
-        */
-       fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
-       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
-               fep->phy_speed--;
-       fep->phy_speed <<= 1;
-       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
-
-       fep->mii_bus = mdiobus_alloc();
-       if (fep->mii_bus == NULL) {
-               err = -ENOMEM;
-               goto err_out;
-       }
-
-       fep->mii_bus->name = "fec_enet_mii_bus";
-       fep->mii_bus->read = fec_enet_mdio_read;
-       fep->mii_bus->write = fec_enet_mdio_write;
-       fep->mii_bus->reset = fec_enet_mdio_reset;
-       snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
-               pdev->name, fep->dev_id + 1);
-       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;
-
-       if (mdiobus_register(fep->mii_bus))
-               goto err_out_free_mdio_irq;
-
-       mii_cnt++;
-
-       /* save fec0 mii_bus */
-       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
-               fec0_mii_bus = fep->mii_bus;
-
-       return 0;
-
-err_out_free_mdio_irq:
-       kfree(fep->mii_bus->irq);
-err_out_free_mdiobus:
-       mdiobus_free(fep->mii_bus);
-err_out:
-       return err;
-}
-
-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);
-       }
-}
-
-static int fec_enet_get_settings(struct net_device *ndev,
-                                 struct ethtool_cmd *cmd)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct phy_device *phydev = fep->phy_dev;
-
-       if (!phydev)
-               return -ENODEV;
-
-       return phy_ethtool_gset(phydev, cmd);
-}
-
-static int fec_enet_set_settings(struct net_device *ndev,
-                                struct ethtool_cmd *cmd)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct phy_device *phydev = fep->phy_dev;
-
-       if (!phydev)
-               return -ENODEV;
-
-       return phy_ethtool_sset(phydev, cmd);
-}
-
-static void fec_enet_get_drvinfo(struct net_device *ndev,
-                                struct ethtool_drvinfo *info)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       strlcpy(info->driver, fep->pdev->dev.driver->name,
-               sizeof(info->driver));
-       strlcpy(info->version, "Revision: 1.0", sizeof(info->version));
-       strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info));
-}
-
-static int fec_enet_get_ts_info(struct net_device *ndev,
-                               struct ethtool_ts_info *info)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       if (fep->bufdesc_ex) {
-
-               info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
-                                       SOF_TIMESTAMPING_RX_SOFTWARE |
-                                       SOF_TIMESTAMPING_SOFTWARE |
-                                       SOF_TIMESTAMPING_TX_HARDWARE |
-                                       SOF_TIMESTAMPING_RX_HARDWARE |
-                                       SOF_TIMESTAMPING_RAW_HARDWARE;
-               if (fep->ptp_clock)
-                       info->phc_index = ptp_clock_index(fep->ptp_clock);
-               else
-                       info->phc_index = -1;
-
-               info->tx_types = (1 << HWTSTAMP_TX_OFF) |
-                                (1 << HWTSTAMP_TX_ON);
-
-               info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
-                                  (1 << HWTSTAMP_FILTER_ALL);
-               return 0;
-       } else {
-               return ethtool_op_get_ts_info(ndev, info);
-       }
-}
-
-static void fec_enet_get_pauseparam(struct net_device *ndev,
-                                   struct ethtool_pauseparam *pause)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
-       pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
-       pause->rx_pause = pause->tx_pause;
-}
-
-static int fec_enet_set_pauseparam(struct net_device *ndev,
-                                  struct ethtool_pauseparam *pause)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       if (pause->tx_pause != pause->rx_pause) {
-               netdev_info(ndev,
-                       "hardware only support enable/disable both tx and rx");
-               return -EINVAL;
-       }
-
-       fep->pause_flag = 0;
-
-       /* tx pause must be same as rx pause */
-       fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
-       fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
-
-       if (pause->rx_pause || pause->autoneg) {
-               fep->phy_dev->supported |= ADVERTISED_Pause;
-               fep->phy_dev->advertising |= ADVERTISED_Pause;
-       } else {
-               fep->phy_dev->supported &= ~ADVERTISED_Pause;
-               fep->phy_dev->advertising &= ~ADVERTISED_Pause;
-       }
-
-       if (pause->autoneg) {
-               if (netif_running(ndev))
-                       fec_stop(ndev);
-               phy_start_aneg(fep->phy_dev);
-       }
-       if (netif_running(ndev))
-               fec_restart(ndev, 0);
-
-       return 0;
-}
-
-static const struct ethtool_ops fec_enet_ethtool_ops = {
-       .get_pauseparam         = fec_enet_get_pauseparam,
-       .set_pauseparam         = fec_enet_set_pauseparam,
-       .get_settings           = fec_enet_get_settings,
-       .set_settings           = fec_enet_set_settings,
-       .get_drvinfo            = fec_enet_get_drvinfo,
-       .get_link               = ethtool_op_get_link,
-       .get_ts_info            = fec_enet_get_ts_info,
-};
-
-static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct phy_device *phydev = fep->phy_dev;
-
-       if (!netif_running(ndev))
-               return -EINVAL;
-
-       if (!phydev)
-               return -ENODEV;
-
-       if (cmd == SIOCSHWTSTAMP && fep->bufdesc_ex)
-               return fec_ptp_ioctl(ndev, rq, cmd);
-
-       return phy_mii_ioctl(phydev, rq, cmd);
-}
-
-static void fec_enet_free_buffers(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       unsigned int i;
-       struct sk_buff *skb;
-       struct bufdesc  *bdp;
-
-       bdp = fep->rx_bd_base;
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               skb = fep->rx_skbuff[i];
-
-               if (bdp->cbd_bufaddr)
-                       dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
-                                       FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
-               if (skb)
-                       dev_kfree_skb(skb);
-               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-       }
-
-       bdp = fep->tx_bd_base;
-       for (i = 0; i < TX_RING_SIZE; i++)
-               kfree(fep->tx_bounce[i]);
-}
-
-static int fec_enet_alloc_buffers(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       unsigned int i;
-       struct sk_buff *skb;
-       struct bufdesc  *bdp;
-
-       bdp = fep->rx_bd_base;
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
-               if (!skb) {
-                       fec_enet_free_buffers(ndev);
-                       return -ENOMEM;
-               }
-               fep->rx_skbuff[i] = skb;
-
-               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
-                               FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
-               bdp->cbd_sc = BD_ENET_RX_EMPTY;
-
-               if (fep->bufdesc_ex) {
-                       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-                       ebdp->cbd_esc = BD_ENET_RX_INT;
-               }
-
-               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-       }
-
-       /* Set the last buffer to wrap. */
-       bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
-       bdp->cbd_sc |= BD_SC_WRAP;
-
-       bdp = fep->tx_bd_base;
-       for (i = 0; i < TX_RING_SIZE; i++) {
-               fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
-
-               bdp->cbd_sc = 0;
-               bdp->cbd_bufaddr = 0;
-
-               if (fep->bufdesc_ex) {
-                       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-                       ebdp->cbd_esc = BD_ENET_RX_INT;
-               }
-
-               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-       }
-
-       /* Set the last buffer to wrap. */
-       bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
-       bdp->cbd_sc |= BD_SC_WRAP;
-
-       return 0;
-}
-
-static int
-fec_enet_open(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       int ret;
-
-       napi_enable(&fep->napi);
-
-       /* I should reset the ring buffers here, but I don't yet know
-        * a simple way to do that.
-        */
-
-       ret = fec_enet_alloc_buffers(ndev);
-       if (ret)
-               return ret;
-
-       /* Probe and connect to PHY when open the interface */
-       ret = fec_enet_mii_probe(ndev);
-       if (ret) {
-               fec_enet_free_buffers(ndev);
-               return ret;
-       }
-       phy_start(fep->phy_dev);
-       netif_start_queue(ndev);
-       fep->opened = 1;
-       return 0;
-}
-
-static int
-fec_enet_close(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       /* Don't know what to do yet. */
-       napi_disable(&fep->napi);
-       fep->opened = 0;
-       netif_stop_queue(ndev);
-       fec_stop(ndev);
-
-       if (fep->phy_dev) {
-               phy_stop(fep->phy_dev);
-               phy_disconnect(fep->phy_dev);
-       }
-
-       fec_enet_free_buffers(ndev);
-
-       return 0;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- * Skeleton taken from sunlance driver.
- * The CPM Ethernet implementation allows Multicast as well as individual
- * MAC address filtering.  Some of the drivers check to make sure it is
- * a group multicast address, and discard those that are not.  I guess I
- * will do the same for now, but just remove the test if you want
- * individual filtering as well (do the upper net layers want or support
- * this kind of feature?).
- */
-
-#define HASH_BITS      6               /* #bits in hash */
-#define CRC32_POLY     0xEDB88320
-
-static void set_multicast_list(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct netdev_hw_addr *ha;
-       unsigned int i, bit, data, crc, tmp;
-       unsigned char hash;
-
-       if (ndev->flags & IFF_PROMISC) {
-               tmp = readl(fep->hwp + FEC_R_CNTRL);
-               tmp |= 0x8;
-               writel(tmp, fep->hwp + FEC_R_CNTRL);
-               return;
-       }
-
-       tmp = readl(fep->hwp + FEC_R_CNTRL);
-       tmp &= ~0x8;
-       writel(tmp, fep->hwp + FEC_R_CNTRL);
-
-       if (ndev->flags & IFF_ALLMULTI) {
-               /* Catch all multicast addresses, so set the
-                * filter to all 1's
-                */
-               writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
-               writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-
-               return;
-       }
-
-       /* Clear filter and add the addresses in hash register
-        */
-       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
-       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-
-       netdev_for_each_mc_addr(ha, ndev) {
-               /* calculate crc32 value of mac address */
-               crc = 0xffffffff;
-
-               for (i = 0; i < ndev->addr_len; i++) {
-                       data = ha->addr[i];
-                       for (bit = 0; bit < 8; bit++, data >>= 1) {
-                               crc = (crc >> 1) ^
-                               (((crc ^ data) & 1) ? CRC32_POLY : 0);
-                       }
-               }
-
-               /* only upper 6 bits (HASH_BITS) are used
-                * which point to specific bit in he hash registers
-                */
-               hash = (crc >> (32 - HASH_BITS)) & 0x3f;
-
-               if (hash > 31) {
-                       tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
-                       tmp |= 1 << (hash - 32);
-                       writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
-               } else {
-                       tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-                       tmp |= 1 << hash;
-                       writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-               }
-       }
-}
-
-/* Set a MAC change in hardware. */
-static int
-fec_set_mac_address(struct net_device *ndev, void *p)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct sockaddr *addr = p;
-
-       if (!is_valid_ether_addr(addr->sa_data))
-               return -EADDRNOTAVAIL;
-
-       memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
-
-       writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
-               (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
-               fep->hwp + FEC_ADDR_LOW);
-       writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
-               fep->hwp + FEC_ADDR_HIGH);
-       return 0;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/**
- * fec_poll_controller - FEC Poll controller function
- * @dev: The FEC network adapter
- *
- * Polled functionality used by netconsole and others in non interrupt mode
- *
- */
-void fec_poll_controller(struct net_device *dev)
-{
-       int i;
-       struct fec_enet_private *fep = netdev_priv(dev);
-
-       for (i = 0; i < FEC_IRQ_NUM; i++) {
-               if (fep->irq[i] > 0) {
-                       disable_irq(fep->irq[i]);
-                       fec_enet_interrupt(fep->irq[i], dev);
-                       enable_irq(fep->irq[i]);
-               }
-       }
-}
-#endif
-
-static const struct net_device_ops fec_netdev_ops = {
-       .ndo_open               = fec_enet_open,
-       .ndo_stop               = fec_enet_close,
-       .ndo_start_xmit         = fec_enet_start_xmit,
-       .ndo_set_rx_mode        = set_multicast_list,
-       .ndo_change_mtu         = eth_change_mtu,
-       .ndo_validate_addr      = eth_validate_addr,
-       .ndo_tx_timeout         = fec_timeout,
-       .ndo_set_mac_address    = fec_set_mac_address,
-       .ndo_do_ioctl           = fec_enet_ioctl,
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       .ndo_poll_controller    = fec_poll_controller,
-#endif
-};
-
- /*
-  * XXX:  We need to clean up on failure exits here.
-  *
-  */
-static int fec_enet_init(struct net_device *ndev)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct bufdesc *cbd_base;
-
-       /* Allocate memory for buffer descriptors. */
-       cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
-                       GFP_KERNEL);
-       if (!cbd_base) {
-               printk("FEC: allocate descriptor memory failed?\n");
-               return -ENOMEM;
-       }
-
-       memset(cbd_base, 0, PAGE_SIZE);
-       spin_lock_init(&fep->hw_lock);
-
-       fep->netdev = ndev;
-
-       /* Get the Ethernet address */
-       fec_get_mac(ndev);
-
-       /* Set receive and transmit descriptor base. */
-       fep->rx_bd_base = cbd_base;
-       if (fep->bufdesc_ex)
-               fep->tx_bd_base = (struct bufdesc *)
-                       (((struct bufdesc_ex *)cbd_base) + RX_RING_SIZE);
-       else
-               fep->tx_bd_base = cbd_base + RX_RING_SIZE;
-
-       /* The FEC Ethernet specific entries in the device structure */
-       ndev->watchdog_timeo = TX_TIMEOUT;
-       ndev->netdev_ops = &fec_netdev_ops;
-       ndev->ethtool_ops = &fec_enet_ethtool_ops;
-
-       writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
-       netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
-
-       fec_restart(ndev, 0);
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static int fec_get_phy_mode_dt(struct platform_device *pdev)
-{
-       struct device_node *np = pdev->dev.of_node;
-
-       if (np)
-               return of_get_phy_mode(np);
-
-       return -ENODEV;
-}
-
-static void fec_reset_phy(struct platform_device *pdev)
-{
-       int err, phy_reset;
-       int msec = 1;
-       struct device_node *np = pdev->dev.of_node;
-
-       if (!np)
-               return;
-
-       of_property_read_u32(np, "phy-reset-duration", &msec);
-       /* A sane reset duration should not be longer than 1s */
-       if (msec > 1000)
-               msec = 1;
-
-       phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
-       if (!gpio_is_valid(phy_reset))
-               return;
-
-       err = devm_gpio_request_one(&pdev->dev, phy_reset,
-                                   GPIOF_OUT_INIT_LOW, "phy-reset");
-       if (err) {
-               dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
-               return;
-       }
-       msleep(msec);
-       gpio_set_value(phy_reset, 1);
-}
-#else /* CONFIG_OF */
-static int fec_get_phy_mode_dt(struct platform_device *pdev)
-{
-       return -ENODEV;
-}
-
-static void fec_reset_phy(struct platform_device *pdev)
-{
-       /*
-        * In case of platform probe, the reset has been done
-        * by machine code.
-        */
-}
-#endif /* CONFIG_OF */
-
-static int
-fec_probe(struct platform_device *pdev)
-{
-       struct fec_enet_private *fep;
-       struct fec_platform_data *pdata;
-       struct net_device *ndev;
-       int i, irq, ret = 0;
-       struct resource *r;
-       const struct of_device_id *of_id;
-       static int dev_id;
-       struct pinctrl *pinctrl;
-       struct regulator *reg_phy;
-
-       of_id = of_match_device(fec_dt_ids, &pdev->dev);
-       if (of_id)
-               pdev->id_entry = of_id->data;
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!r)
-               return -ENXIO;
-
-       r = request_mem_region(r->start, resource_size(r), pdev->name);
-       if (!r)
-               return -EBUSY;
-
-       /* Init network device */
-       ndev = alloc_etherdev(sizeof(struct fec_enet_private));
-       if (!ndev) {
-               ret = -ENOMEM;
-               goto failed_alloc_etherdev;
-       }
-
-       SET_NETDEV_DEV(ndev, &pdev->dev);
-
-       /* setup board info structure */
-       fep = netdev_priv(ndev);
-
-       /* default enable pause frame auto negotiation */
-       if (pdev->id_entry &&
-           (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT))
-               fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
-
-       fep->hwp = ioremap(r->start, resource_size(r));
-       fep->pdev = pdev;
-       fep->dev_id = dev_id++;
-
-       fep->bufdesc_ex = 0;
-
-       if (!fep->hwp) {
-               ret = -ENOMEM;
-               goto failed_ioremap;
-       }
-
-       platform_set_drvdata(pdev, ndev);
-
-       ret = fec_get_phy_mode_dt(pdev);
-       if (ret < 0) {
-               pdata = pdev->dev.platform_data;
-               if (pdata)
-                       fep->phy_interface = pdata->phy;
-               else
-                       fep->phy_interface = PHY_INTERFACE_MODE_MII;
-       } else {
-               fep->phy_interface = ret;
-       }
-
-       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-       if (IS_ERR(pinctrl)) {
-               ret = PTR_ERR(pinctrl);
-               goto failed_pin;
-       }
-
-       fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
-       if (IS_ERR(fep->clk_ipg)) {
-               ret = PTR_ERR(fep->clk_ipg);
-               goto failed_clk;
-       }
-
-       fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
-       if (IS_ERR(fep->clk_ahb)) {
-               ret = PTR_ERR(fep->clk_ahb);
-               goto failed_clk;
-       }
-
-       fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
-       fep->bufdesc_ex =
-               pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
-       if (IS_ERR(fep->clk_ptp)) {
-               ret = PTR_ERR(fep->clk_ptp);
-               fep->bufdesc_ex = 0;
-       }
-
-       clk_prepare_enable(fep->clk_ahb);
-       clk_prepare_enable(fep->clk_ipg);
-       if (!IS_ERR(fep->clk_ptp))
-               clk_prepare_enable(fep->clk_ptp);
-
-       reg_phy = devm_regulator_get(&pdev->dev, "phy");
-       if (!IS_ERR(reg_phy)) {
-               ret = regulator_enable(reg_phy);
-               if (ret) {
-                       dev_err(&pdev->dev,
-                               "Failed to enable phy regulator: %d\n", ret);
-                       goto failed_regulator;
-               }
-       }
-
-       fec_reset_phy(pdev);
-
-       if (fep->bufdesc_ex)
-               fec_ptp_init(ndev, pdev);
-
-       ret = fec_enet_init(ndev);
-       if (ret)
-               goto failed_init;
-
-       for (i = 0; i < FEC_IRQ_NUM; i++) {
-               irq = platform_get_irq(pdev, i);
-               if (irq < 0) {
-                       if (i)
-                               break;
-                       ret = irq;
-                       goto failed_irq;
-               }
-               ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
-               if (ret) {
-                       while (--i >= 0) {
-                               irq = platform_get_irq(pdev, i);
-                               free_irq(irq, ndev);
-                       }
-                       goto failed_irq;
-               }
-       }
-
-       ret = fec_enet_mii_init(pdev);
-       if (ret)
-               goto failed_mii_init;
-
-       /* Carrier starts down, phylib will bring it up */
-       netif_carrier_off(ndev);
-
-       ret = register_netdev(ndev);
-       if (ret)
-               goto failed_register;
-
-       return 0;
-
-failed_register:
-       fec_enet_mii_remove(fep);
-failed_mii_init:
-failed_init:
-       for (i = 0; i < FEC_IRQ_NUM; i++) {
-               irq = platform_get_irq(pdev, i);
-               if (irq > 0)
-                       free_irq(irq, ndev);
-       }
-failed_irq:
-failed_regulator:
-       clk_disable_unprepare(fep->clk_ahb);
-       clk_disable_unprepare(fep->clk_ipg);
-       if (!IS_ERR(fep->clk_ptp))
-               clk_disable_unprepare(fep->clk_ptp);
-failed_pin:
-failed_clk:
-       iounmap(fep->hwp);
-failed_ioremap:
-       free_netdev(ndev);
-failed_alloc_etherdev:
-       release_mem_region(r->start, resource_size(r));
-
-       return ret;
-}
-
-static int
-fec_drv_remove(struct platform_device *pdev)
-{
-       struct net_device *ndev = platform_get_drvdata(pdev);
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct resource *r;
-       int i;
-
-       unregister_netdev(ndev);
-       fec_enet_mii_remove(fep);
-       del_timer_sync(&fep->time_keep);
-       clk_disable_unprepare(fep->clk_ptp);
-       if (fep->ptp_clock)
-               ptp_clock_unregister(fep->ptp_clock);
-       clk_disable_unprepare(fep->clk_ahb);
-       clk_disable_unprepare(fep->clk_ipg);
-       for (i = 0; i < FEC_IRQ_NUM; i++) {
-               int irq = platform_get_irq(pdev, i);
-               if (irq > 0)
-                       free_irq(irq, ndev);
-       }
-       iounmap(fep->hwp);
-       free_netdev(ndev);
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       BUG_ON(!r);
-       release_mem_region(r->start, resource_size(r));
-
-       platform_set_drvdata(pdev, NULL);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int
-fec_suspend(struct device *dev)
-{
-       struct net_device *ndev = dev_get_drvdata(dev);
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       if (netif_running(ndev)) {
-               fec_stop(ndev);
-               netif_device_detach(ndev);
-       }
-       clk_disable_unprepare(fep->clk_ahb);
-       clk_disable_unprepare(fep->clk_ipg);
-
-       return 0;
-}
-
-static int
-fec_resume(struct device *dev)
-{
-       struct net_device *ndev = dev_get_drvdata(dev);
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       clk_prepare_enable(fep->clk_ahb);
-       clk_prepare_enable(fep->clk_ipg);
-       if (netif_running(ndev)) {
-               fec_restart(ndev, fep->full_duplex);
-               netif_device_attach(ndev);
-       }
-
-       return 0;
-}
-
-static const struct dev_pm_ops fec_pm_ops = {
-       .suspend        = fec_suspend,
-       .resume         = fec_resume,
-       .freeze         = fec_suspend,
-       .thaw           = fec_resume,
-       .poweroff       = fec_suspend,
-       .restore        = fec_resume,
-};
-#endif
-
-static struct platform_driver fec_driver = {
-       .driver = {
-               .name   = DRIVER_NAME,
-               .owner  = THIS_MODULE,
-#ifdef CONFIG_PM
-               .pm     = &fec_pm_ops,
-#endif
-               .of_match_table = fec_dt_ids,
-       },
-       .id_table = fec_devtype,
-       .probe  = fec_probe,
-       .remove = fec_drv_remove,
-};
-
-module_platform_driver(fec_driver);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
new file mode 100644 (file)
index 0000000..2451ab1
--- /dev/null
@@ -0,0 +1,1958 @@
+/*
+ * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * Right now, I am very wasteful with the buffers.  I allocate memory
+ * pages and then divide them into 2K frame buffers.  This way I know I
+ * have buffers large enough to hold one frame within one buffer descriptor.
+ * Once I get this working, I will use 64 or 128 byte CPM buffers, which
+ * will be much more memory efficient and will easily handle lots of
+ * small packets.
+ *
+ * Much better multiple PHY support by Magnus Damm.
+ * Copyright (c) 2000 Ericsson Radio Systems AB.
+ *
+ * Support for FEC controller of ColdFire processors.
+ * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
+ *
+ * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
+ * Copyright (c) 2004-2006 Macq Electronique SA.
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/fec.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/cacheflush.h>
+
+#include "fec.h"
+
+#if defined(CONFIG_ARM)
+#define FEC_ALIGNMENT  0xf
+#else
+#define FEC_ALIGNMENT  0x3
+#endif
+
+#define DRIVER_NAME    "fec"
+#define FEC_NAPI_WEIGHT        64
+
+/* Pause frame feild and FIFO threshold */
+#define FEC_ENET_FCE   (1 << 5)
+#define FEC_ENET_RSEM_V        0x84
+#define FEC_ENET_RSFL_V        16
+#define FEC_ENET_RAEM_V        0x8
+#define FEC_ENET_RAFL_V        0x8
+#define FEC_ENET_OPD_V 0xFFF0
+
+/* Controller is ENET-MAC */
+#define FEC_QUIRK_ENET_MAC             (1 << 0)
+/* Controller needs driver to swap frame */
+#define FEC_QUIRK_SWAP_FRAME           (1 << 1)
+/* Controller uses gasket */
+#define FEC_QUIRK_USE_GASKET           (1 << 2)
+/* Controller has GBIT support */
+#define FEC_QUIRK_HAS_GBIT             (1 << 3)
+/* Controller has extend desc buffer */
+#define FEC_QUIRK_HAS_BUFDESC_EX       (1 << 4)
+
+static struct platform_device_id fec_devtype[] = {
+       {
+               /* keep it for coldfire */
+               .name = DRIVER_NAME,
+               .driver_data = 0,
+       }, {
+               .name = "imx25-fec",
+               .driver_data = FEC_QUIRK_USE_GASKET,
+       }, {
+               .name = "imx27-fec",
+               .driver_data = 0,
+       }, {
+               .name = "imx28-fec",
+               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
+       }, {
+               .name = "imx6q-fec",
+               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+                               FEC_QUIRK_HAS_BUFDESC_EX,
+       }, {
+               .name = "mvf-fec",
+               .driver_data = FEC_QUIRK_ENET_MAC,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, fec_devtype);
+
+enum imx_fec_type {
+       IMX25_FEC = 1,  /* runs on i.mx25/50/53 */
+       IMX27_FEC,      /* runs on i.mx27/35/51 */
+       IMX28_FEC,
+       IMX6Q_FEC,
+       MVF_FEC,
+};
+
+static const struct of_device_id fec_dt_ids[] = {
+       { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
+       { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
+       { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
+       { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
+       { .compatible = "fsl,mvf-fec", .data = &fec_devtype[MVF_FEC], },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fec_dt_ids);
+
+static unsigned char macaddr[ETH_ALEN];
+module_param_array(macaddr, byte, NULL, 0);
+MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
+
+#if defined(CONFIG_M5272)
+/*
+ * Some hardware gets it MAC address out of local flash memory.
+ * if this is non-zero then assume it is the address to get MAC from.
+ */
+#if defined(CONFIG_NETtel)
+#define        FEC_FLASHMAC    0xf0006006
+#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
+#define        FEC_FLASHMAC    0xf0006000
+#elif defined(CONFIG_CANCam)
+#define        FEC_FLASHMAC    0xf0020000
+#elif defined (CONFIG_M5272C3)
+#define        FEC_FLASHMAC    (0xffe04000 + 4)
+#elif defined(CONFIG_MOD5272)
+#define FEC_FLASHMAC   0xffc0406b
+#else
+#define        FEC_FLASHMAC    0
+#endif
+#endif /* CONFIG_M5272 */
+
+#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
+#error "FEC: descriptor ring size constants too large"
+#endif
+
+/* Interrupt events/masks. */
+#define FEC_ENET_HBERR ((uint)0x80000000)      /* Heartbeat error */
+#define FEC_ENET_BABR  ((uint)0x40000000)      /* Babbling receiver */
+#define FEC_ENET_BABT  ((uint)0x20000000)      /* Babbling transmitter */
+#define FEC_ENET_GRA   ((uint)0x10000000)      /* Graceful stop complete */
+#define FEC_ENET_TXF   ((uint)0x08000000)      /* Full frame transmitted */
+#define FEC_ENET_TXB   ((uint)0x04000000)      /* A buffer was transmitted */
+#define FEC_ENET_RXF   ((uint)0x02000000)      /* Full frame received */
+#define FEC_ENET_RXB   ((uint)0x01000000)      /* A buffer was received */
+#define FEC_ENET_MII   ((uint)0x00800000)      /* MII interrupt */
+#define FEC_ENET_EBERR ((uint)0x00400000)      /* SDMA bus error */
+
+#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
+#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
+
+/* The FEC stores dest/src/type, data, and checksum for receive packets.
+ */
+#define PKT_MAXBUF_SIZE                1518
+#define PKT_MINBUF_SIZE                64
+#define PKT_MAXBLR_SIZE                1520
+
+/*
+ * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
+ * size bits. Other FEC hardware does not, so we need to take that into
+ * account when setting it.
+ */
+#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
+    defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM)
+#define        OPT_FRAME_SIZE  (PKT_MAXBUF_SIZE << 16)
+#else
+#define        OPT_FRAME_SIZE  0
+#endif
+
+/* FEC MII MMFR bits definition */
+#define FEC_MMFR_ST            (1 << 30)
+#define FEC_MMFR_OP_READ       (2 << 28)
+#define FEC_MMFR_OP_WRITE      (1 << 28)
+#define FEC_MMFR_PA(v)         ((v & 0x1f) << 23)
+#define FEC_MMFR_RA(v)         ((v & 0x1f) << 18)
+#define FEC_MMFR_TA            (2 << 16)
+#define FEC_MMFR_DATA(v)       (v & 0xffff)
+
+#define FEC_MII_TIMEOUT                30000 /* us */
+
+/* Transmitter timeout */
+#define TX_TIMEOUT (2 * HZ)
+
+#define FEC_PAUSE_FLAG_AUTONEG 0x1
+#define FEC_PAUSE_FLAG_ENABLE  0x2
+
+static int mii_cnt;
+
+static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, int is_ex)
+{
+       struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
+       if (is_ex)
+               return (struct bufdesc *)(ex + 1);
+       else
+               return bdp + 1;
+}
+
+static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, int is_ex)
+{
+       struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
+       if (is_ex)
+               return (struct bufdesc *)(ex - 1);
+       else
+               return bdp - 1;
+}
+
+static void *swap_buffer(void *bufaddr, int len)
+{
+       int i;
+       unsigned int *buf = bufaddr;
+
+       for (i = 0; i < (len + 3) / 4; i++, buf++)
+               *buf = cpu_to_be32(*buf);
+
+       return bufaddr;
+}
+
+static netdev_tx_t
+fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       struct bufdesc *bdp;
+       void *bufaddr;
+       unsigned short  status;
+       unsigned int index;
+
+       if (!fep->link) {
+               /* Link is down or autonegotiation is in progress. */
+               return NETDEV_TX_BUSY;
+       }
+
+       /* Fill in a Tx ring entry */
+       bdp = fep->cur_tx;
+
+       status = bdp->cbd_sc;
+
+       if (status & BD_ENET_TX_READY) {
+               /* Ooops.  All transmit buffers are full.  Bail out.
+                * This should not happen, since ndev->tbusy should be set.
+                */
+               netdev_err(ndev, "tx queue full!\n");
+               return NETDEV_TX_BUSY;
+       }
+
+       /* Clear all of the status flags */
+       status &= ~BD_ENET_TX_STATS;
+
+       /* Set buffer length and buffer pointer */
+       bufaddr = skb->data;
+       bdp->cbd_datlen = skb->len;
+
+       /*
+        * On some FEC implementations data must be aligned on
+        * 4-byte boundaries. Use bounce buffers to copy data
+        * and get it aligned. Ugh.
+        */
+       if (fep->bufdesc_ex)
+               index = (struct bufdesc_ex *)bdp -
+                       (struct bufdesc_ex *)fep->tx_bd_base;
+       else
+               index = bdp - fep->tx_bd_base;
+
+       if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
+               memcpy(fep->tx_bounce[index], skb->data, skb->len);
+               bufaddr = fep->tx_bounce[index];
+       }
+
+       /*
+        * Some design made an incorrect assumption on endian mode of
+        * the system that it's running on. As the result, driver has to
+        * swap every frame going to and coming from the controller.
+        */
+       if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+               swap_buffer(bufaddr, skb->len);
+
+       /* Save skb pointer */
+       fep->tx_skbuff[index] = skb;
+
+       /* Push the data cache so the CPM does not get stale memory
+        * data.
+        */
+       bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
+                       FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
+
+       /* Send it on its way.  Tell FEC it's ready, interrupt when done,
+        * it's the last BD of the frame, and to put the CRC on the end.
+        */
+       status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
+                       | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+       bdp->cbd_sc = status;
+
+       if (fep->bufdesc_ex) {
+
+               struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+               ebdp->cbd_bdu = 0;
+               if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
+                       fep->hwts_tx_en)) {
+                       ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
+                       skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+               } else {
+
+                       ebdp->cbd_esc = BD_ENET_TX_INT;
+               }
+       }
+       /* If this was the last BD in the ring, start at the beginning again. */
+       if (status & BD_ENET_TX_WRAP)
+               bdp = fep->tx_bd_base;
+       else
+               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+
+       fep->cur_tx = bdp;
+
+       if (fep->cur_tx == fep->dirty_tx)
+               netif_stop_queue(ndev);
+
+       /* Trigger transmission start */
+       writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+
+       skb_tx_timestamp(skb);
+
+       return NETDEV_TX_OK;
+}
+
+/* Init RX & TX buffer descriptors
+ */
+static void fec_enet_bd_init(struct net_device *dev)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
+       struct bufdesc *bdp;
+       unsigned int i;
+
+       /* Initialize the receive buffer descriptors. */
+       bdp = fep->rx_bd_base;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+
+               /* Initialize the BD for every fragment in the page. */
+               if (bdp->cbd_bufaddr)
+                       bdp->cbd_sc = BD_ENET_RX_EMPTY;
+               else
+                       bdp->cbd_sc = 0;
+               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+       }
+
+       /* Set the last buffer to wrap */
+       bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
+       bdp->cbd_sc |= BD_SC_WRAP;
+
+       fep->cur_rx = fep->rx_bd_base;
+
+       /* ...and the same for transmit */
+       bdp = fep->tx_bd_base;
+       fep->cur_tx = bdp;
+       for (i = 0; i < TX_RING_SIZE; i++) {
+
+               /* Initialize the BD for every fragment in the page. */
+               bdp->cbd_sc = 0;
+               if (bdp->cbd_bufaddr && fep->tx_skbuff[i]) {
+                       dev_kfree_skb_any(fep->tx_skbuff[i]);
+                       fep->tx_skbuff[i] = NULL;
+               }
+               bdp->cbd_bufaddr = 0;
+               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+       }
+
+       /* Set the last buffer to wrap */
+       bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
+       bdp->cbd_sc |= BD_SC_WRAP;
+       fep->dirty_tx = bdp;
+}
+
+/* This function is called to start or restart the FEC during a link
+ * change.  This only happens when switching between half and full
+ * duplex.
+ */
+static void
+fec_restart(struct net_device *ndev, int duplex)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       int i;
+       u32 temp_mac[2];
+       u32 rcntl = OPT_FRAME_SIZE | 0x04;
+       u32 ecntl = 0x2; /* ETHEREN */
+
+       /* Whack a reset.  We should wait for this. */
+       writel(1, fep->hwp + FEC_ECNTRL);
+       udelay(10);
+
+       /*
+        * enet-mac reset will reset mac address registers too,
+        * so need to reconfigure it.
+        */
+       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+               memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
+               writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
+               writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
+       }
+
+       /* Clear any outstanding interrupt. */
+       writel(0xffc00000, fep->hwp + FEC_IEVENT);
+
+       /* Reset all multicast. */
+       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+#ifndef CONFIG_M5272
+       writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
+       writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
+#endif
+
+       /* Set maximum receive buffer size. */
+       writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
+
+       fec_enet_bd_init(ndev);
+
+       /* Set receive and transmit descriptor base. */
+       writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
+       if (fep->bufdesc_ex)
+               writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
+                       * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
+       else
+               writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
+                       * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
+
+
+       for (i = 0; i <= TX_RING_MOD_MASK; i++) {
+               if (fep->tx_skbuff[i]) {
+                       dev_kfree_skb_any(fep->tx_skbuff[i]);
+                       fep->tx_skbuff[i] = NULL;
+               }
+       }
+
+       /* Enable MII mode */
+       if (duplex) {
+               /* FD enable */
+               writel(0x04, fep->hwp + FEC_X_CNTRL);
+       } else {
+               /* No Rcv on Xmit */
+               rcntl |= 0x02;
+               writel(0x0, fep->hwp + FEC_X_CNTRL);
+       }
+
+       fep->full_duplex = duplex;
+
+       /* Set MII speed */
+       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+
+       /*
+        * The phy interface and speed need to get configured
+        * differently on enet-mac.
+        */
+       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+               /* Enable flow control and length check */
+               rcntl |= 0x40000000 | 0x00000020;
+
+               /* RGMII, RMII or MII */
+               if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
+                       rcntl |= (1 << 6);
+               else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
+                       rcntl |= (1 << 8);
+               else
+                       rcntl &= ~(1 << 8);
+
+               /* 1G, 100M or 10M */
+               if (fep->phy_dev) {
+                       if (fep->phy_dev->speed == SPEED_1000)
+                               ecntl |= (1 << 5);
+                       else if (fep->phy_dev->speed == SPEED_100)
+                               rcntl &= ~(1 << 9);
+                       else
+                               rcntl |= (1 << 9);
+               }
+       } else {
+#ifdef FEC_MIIGSK_ENR
+               if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
+                       u32 cfgr;
+                       /* disable the gasket and wait */
+                       writel(0, fep->hwp + FEC_MIIGSK_ENR);
+                       while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
+                               udelay(1);
+
+                       /*
+                        * configure the gasket:
+                        *   RMII, 50 MHz, no loopback, no echo
+                        *   MII, 25 MHz, no loopback, no echo
+                        */
+                       cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
+                               ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
+                       if (fep->phy_dev && fep->phy_dev->speed == SPEED_10)
+                               cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
+                       writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
+
+                       /* re-enable the gasket */
+                       writel(2, fep->hwp + FEC_MIIGSK_ENR);
+               }
+#endif
+       }
+
+       /* enable pause frame*/
+       if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
+           ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
+            fep->phy_dev && fep->phy_dev->pause)) {
+               rcntl |= FEC_ENET_FCE;
+
+               /* set FIFO thresh hold parameter to reduce overrun */
+               writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
+               writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
+               writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
+               writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
+
+               /* OPD */
+               writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
+       } else {
+               rcntl &= ~FEC_ENET_FCE;
+       }
+
+       writel(rcntl, fep->hwp + FEC_R_CNTRL);
+
+       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+               /* enable ENET endian swap */
+               ecntl |= (1 << 8);
+               /* enable ENET store and forward mode */
+               writel(1 << 8, fep->hwp + FEC_X_WMRK);
+       }
+
+       if (fep->bufdesc_ex)
+               ecntl |= (1 << 4);
+
+       /* And last, enable the transmit and receive processing */
+       writel(ecntl, fep->hwp + FEC_ECNTRL);
+       writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+
+       if (fep->bufdesc_ex)
+               fec_ptp_start_cyclecounter(ndev);
+
+       /* Enable interrupts we wish to service */
+       writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+}
+
+static void
+fec_stop(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
+
+       /* We cannot expect a graceful transmit stop without link !!! */
+       if (fep->link) {
+               writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
+               udelay(10);
+               if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
+                       netdev_err(ndev, "Graceful transmit stop did not complete!\n");
+       }
+
+       /* Whack a reset.  We should wait for this. */
+       writel(1, fep->hwp + FEC_ECNTRL);
+       udelay(10);
+       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+       writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+
+       /* We have to keep ENET enabled to have MII interrupt stay working */
+       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+               writel(2, fep->hwp + FEC_ECNTRL);
+               writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
+       }
+}
+
+
+static void
+fec_timeout(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       ndev->stats.tx_errors++;
+
+       fec_restart(ndev, fep->full_duplex);
+       netif_wake_queue(ndev);
+}
+
+static void
+fec_enet_tx(struct net_device *ndev)
+{
+       struct  fec_enet_private *fep;
+       struct bufdesc *bdp;
+       unsigned short status;
+       struct  sk_buff *skb;
+       int     index = 0;
+
+       fep = netdev_priv(ndev);
+       bdp = fep->dirty_tx;
+
+       /* get next bdp of dirty_tx */
+       if (bdp->cbd_sc & BD_ENET_TX_WRAP)
+               bdp = fep->tx_bd_base;
+       else
+               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+
+       while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
+
+               /* current queue is empty */
+               if (bdp == fep->cur_tx)
+                       break;
+
+               if (fep->bufdesc_ex)
+                       index = (struct bufdesc_ex *)bdp -
+                               (struct bufdesc_ex *)fep->tx_bd_base;
+               else
+                       index = bdp - fep->tx_bd_base;
+
+               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                               FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
+               bdp->cbd_bufaddr = 0;
+
+               skb = fep->tx_skbuff[index];
+
+               /* Check for errors. */
+               if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
+                                  BD_ENET_TX_RL | BD_ENET_TX_UN |
+                                  BD_ENET_TX_CSL)) {
+                       ndev->stats.tx_errors++;
+                       if (status & BD_ENET_TX_HB)  /* No heartbeat */
+                               ndev->stats.tx_heartbeat_errors++;
+                       if (status & BD_ENET_TX_LC)  /* Late collision */
+                               ndev->stats.tx_window_errors++;
+                       if (status & BD_ENET_TX_RL)  /* Retrans limit */
+                               ndev->stats.tx_aborted_errors++;
+                       if (status & BD_ENET_TX_UN)  /* Underrun */
+                               ndev->stats.tx_fifo_errors++;
+                       if (status & BD_ENET_TX_CSL) /* Carrier lost */
+                               ndev->stats.tx_carrier_errors++;
+               } else {
+                       ndev->stats.tx_packets++;
+               }
+
+               if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
+                       fep->bufdesc_ex) {
+                       struct skb_shared_hwtstamps shhwtstamps;
+                       unsigned long flags;
+                       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+
+                       memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+                       spin_lock_irqsave(&fep->tmreg_lock, flags);
+                       shhwtstamps.hwtstamp = ns_to_ktime(
+                               timecounter_cyc2time(&fep->tc, ebdp->ts));
+                       spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+                       skb_tstamp_tx(skb, &shhwtstamps);
+               }
+
+               if (status & BD_ENET_TX_READY)
+                       netdev_err(ndev, "HEY! Enet xmit interrupt and TX_READY\n");
+
+               /* Deferred means some collisions occurred during transmit,
+                * but we eventually sent the packet OK.
+                */
+               if (status & BD_ENET_TX_DEF)
+                       ndev->stats.collisions++;
+
+               /* Free the sk buffer associated with this last transmit */
+               dev_kfree_skb_any(skb);
+               fep->tx_skbuff[index] = NULL;
+
+               fep->dirty_tx = bdp;
+
+               /* Update pointer to next buffer descriptor to be transmitted */
+               if (status & BD_ENET_TX_WRAP)
+                       bdp = fep->tx_bd_base;
+               else
+                       bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+
+               /* Since we have freed up a buffer, the ring is no longer full
+                */
+               if (fep->dirty_tx != fep->cur_tx) {
+                       if (netif_queue_stopped(ndev))
+                               netif_wake_queue(ndev);
+               }
+       }
+       return;
+}
+
+
+/* During a receive, the cur_rx points to the current incoming buffer.
+ * When we update through the ring, if the next incoming buffer has
+ * not been given to the system, we just set the empty indicator,
+ * effectively tossing the packet.
+ */
+static int
+fec_enet_rx(struct net_device *ndev, int budget)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       struct bufdesc *bdp;
+       unsigned short status;
+       struct  sk_buff *skb;
+       ushort  pkt_len;
+       __u8 *data;
+       int     pkt_received = 0;
+
+#ifdef CONFIG_M532x
+       flush_cache_all();
+#endif
+
+       /* First, grab all of the stats for the incoming packet.
+        * These get messed up if we get called due to a busy condition.
+        */
+       bdp = fep->cur_rx;
+
+       while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
+
+               if (pkt_received >= budget)
+                       break;
+               pkt_received++;
+
+               /* Since we have allocated space to hold a complete frame,
+                * the last indicator should be set.
+                */
+               if ((status & BD_ENET_RX_LAST) == 0)
+                       netdev_err(ndev, "rcv is not +last\n");
+
+               if (!fep->opened)
+                       goto rx_processing_done;
+
+               /* Check for errors. */
+               if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
+                          BD_ENET_RX_CR | BD_ENET_RX_OV)) {
+                       ndev->stats.rx_errors++;
+                       if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
+                               /* Frame too long or too short. */
+                               ndev->stats.rx_length_errors++;
+                       }
+                       if (status & BD_ENET_RX_NO)     /* Frame alignment */
+                               ndev->stats.rx_frame_errors++;
+                       if (status & BD_ENET_RX_CR)     /* CRC Error */
+                               ndev->stats.rx_crc_errors++;
+                       if (status & BD_ENET_RX_OV)     /* FIFO overrun */
+                               ndev->stats.rx_fifo_errors++;
+               }
+
+               /* Report late collisions as a frame error.
+                * On this error, the BD is closed, but we don't know what we
+                * have in the buffer.  So, just drop this frame on the floor.
+                */
+               if (status & BD_ENET_RX_CL) {
+                       ndev->stats.rx_errors++;
+                       ndev->stats.rx_frame_errors++;
+                       goto rx_processing_done;
+               }
+
+               /* Process the incoming frame. */
+               ndev->stats.rx_packets++;
+               pkt_len = bdp->cbd_datlen;
+               ndev->stats.rx_bytes += pkt_len;
+               data = (__u8*)__va(bdp->cbd_bufaddr);
+
+               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                               FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
+
+               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                       swap_buffer(data, pkt_len);
+
+               /* This does 16 byte alignment, exactly what we need.
+                * The packet length includes FCS, but we don't want to
+                * include that when passing upstream as it messes up
+                * bridging applications.
+                */
+               skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN);
+
+               if (unlikely(!skb)) {
+                       ndev->stats.rx_dropped++;
+               } else {
+                       skb_reserve(skb, NET_IP_ALIGN);
+                       skb_put(skb, pkt_len - 4);      /* Make room */
+                       skb_copy_to_linear_data(skb, data, pkt_len - 4);
+                       skb->protocol = eth_type_trans(skb, ndev);
+
+                       /* Get receive timestamp from the skb */
+                       if (fep->hwts_rx_en && fep->bufdesc_ex) {
+                               struct skb_shared_hwtstamps *shhwtstamps =
+                                                           skb_hwtstamps(skb);
+                               unsigned long flags;
+                               struct bufdesc_ex *ebdp =
+                                       (struct bufdesc_ex *)bdp;
+
+                               memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+
+                               spin_lock_irqsave(&fep->tmreg_lock, flags);
+                               shhwtstamps->hwtstamp = ns_to_ktime(
+                                   timecounter_cyc2time(&fep->tc, ebdp->ts));
+                               spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+                       }
+
+                       if (!skb_defer_rx_timestamp(skb))
+                               napi_gro_receive(&fep->napi, skb);
+               }
+
+               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
+                               FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
+rx_processing_done:
+               /* Clear the status flags for this buffer */
+               status &= ~BD_ENET_RX_STATS;
+
+               /* Mark the buffer empty */
+               status |= BD_ENET_RX_EMPTY;
+               bdp->cbd_sc = status;
+
+               if (fep->bufdesc_ex) {
+                       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+
+                       ebdp->cbd_esc = BD_ENET_RX_INT;
+                       ebdp->cbd_prot = 0;
+                       ebdp->cbd_bdu = 0;
+               }
+
+               /* Update BD pointer to next entry */
+               if (status & BD_ENET_RX_WRAP)
+                       bdp = fep->rx_bd_base;
+               else
+                       bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+               /* Doing this here will keep the FEC running while we process
+                * incoming frames.  On a heavily loaded network, we should be
+                * able to keep up at the expense of system resources.
+                */
+               writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+       }
+       fep->cur_rx = bdp;
+
+       return pkt_received;
+}
+
+static irqreturn_t
+fec_enet_interrupt(int irq, void *dev_id)
+{
+       struct net_device *ndev = dev_id;
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       uint int_events;
+       irqreturn_t ret = IRQ_NONE;
+
+       do {
+               int_events = readl(fep->hwp + FEC_IEVENT);
+               writel(int_events, fep->hwp + FEC_IEVENT);
+
+               if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
+                       ret = IRQ_HANDLED;
+
+                       /* Disable the RX interrupt */
+                       if (napi_schedule_prep(&fep->napi)) {
+                               writel(FEC_RX_DISABLED_IMASK,
+                                       fep->hwp + FEC_IMASK);
+                               __napi_schedule(&fep->napi);
+                       }
+               }
+
+               if (int_events & FEC_ENET_MII) {
+                       ret = IRQ_HANDLED;
+                       complete(&fep->mdio_done);
+               }
+       } while (int_events);
+
+       return ret;
+}
+
+static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
+{
+       struct net_device *ndev = napi->dev;
+       int pkts = fec_enet_rx(ndev, budget);
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       fec_enet_tx(ndev);
+
+       if (pkts < budget) {
+               napi_complete(napi);
+               writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+       }
+       return pkts;
+}
+
+/* ------------------------------------------------------------------------- */
+static void fec_get_mac(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
+       unsigned char *iap, tmpaddr[ETH_ALEN];
+
+       /*
+        * try to get mac address in following order:
+        *
+        * 1) module parameter via kernel command line in form
+        *    fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
+        */
+       iap = macaddr;
+
+       /*
+        * 2) from device tree data
+        */
+       if (!is_valid_ether_addr(iap)) {
+               struct device_node *np = fep->pdev->dev.of_node;
+               if (np) {
+                       const char *mac = of_get_mac_address(np);
+                       if (mac)
+                               iap = (unsigned char *) mac;
+               }
+       }
+
+       /*
+        * 3) from flash or fuse (via platform data)
+        */
+       if (!is_valid_ether_addr(iap)) {
+#ifdef CONFIG_M5272
+               if (FEC_FLASHMAC)
+                       iap = (unsigned char *)FEC_FLASHMAC;
+#else
+               if (pdata)
+                       iap = (unsigned char *)&pdata->mac;
+#endif
+       }
+
+       /*
+        * 4) FEC mac registers set by bootloader
+        */
+       if (!is_valid_ether_addr(iap)) {
+               *((unsigned long *) &tmpaddr[0]) =
+                       be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW));
+               *((unsigned short *) &tmpaddr[4]) =
+                       be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
+               iap = &tmpaddr[0];
+       }
+
+       memcpy(ndev->dev_addr, iap, ETH_ALEN);
+
+       /* Adjust MAC if using macaddr */
+       if (iap == macaddr)
+                ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Phy section
+ */
+static void fec_enet_adjust_link(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct phy_device *phy_dev = fep->phy_dev;
+       unsigned long flags;
+
+       int status_change = 0;
+
+       spin_lock_irqsave(&fep->hw_lock, flags);
+
+       /* Prevent a state halted on mii error */
+       if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
+               phy_dev->state = PHY_RESUMING;
+               goto spin_unlock;
+       }
+
+       if (phy_dev->link) {
+               if (!fep->link) {
+                       fep->link = phy_dev->link;
+                       status_change = 1;
+               }
+
+               if (fep->full_duplex != phy_dev->duplex)
+                       status_change = 1;
+
+               if (phy_dev->speed != fep->speed) {
+                       fep->speed = phy_dev->speed;
+                       status_change = 1;
+               }
+
+               /* if any of the above changed restart the FEC */
+               if (status_change)
+                       fec_restart(ndev, phy_dev->duplex);
+       } else {
+               if (fep->link) {
+                       fec_stop(ndev);
+                       fep->link = phy_dev->link;
+                       status_change = 1;
+               }
+       }
+
+spin_unlock:
+       spin_unlock_irqrestore(&fep->hw_lock, flags);
+
+       if (status_change)
+               phy_print_status(phy_dev);
+}
+
+static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+       struct fec_enet_private *fep = bus->priv;
+       unsigned long time_left;
+
+       fep->mii_timeout = 0;
+       init_completion(&fep->mdio_done);
+
+       /* start a read op */
+       writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
+               FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+               FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
+
+       /* wait for end of transfer */
+       time_left = wait_for_completion_timeout(&fep->mdio_done,
+                       usecs_to_jiffies(FEC_MII_TIMEOUT));
+       if (time_left == 0) {
+               fep->mii_timeout = 1;
+               netdev_err(fep->netdev, "MDIO read timeout\n");
+               return -ETIMEDOUT;
+       }
+
+       /* return value */
+       return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
+}
+
+static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+                          u16 value)
+{
+       struct fec_enet_private *fep = bus->priv;
+       unsigned long time_left;
+
+       fep->mii_timeout = 0;
+       init_completion(&fep->mdio_done);
+
+       /* start a write op */
+       writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
+               FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+               FEC_MMFR_TA | FEC_MMFR_DATA(value),
+               fep->hwp + FEC_MII_DATA);
+
+       /* wait for end of transfer */
+       time_left = wait_for_completion_timeout(&fep->mdio_done,
+                       usecs_to_jiffies(FEC_MII_TIMEOUT));
+       if (time_left == 0) {
+               fep->mii_timeout = 1;
+               netdev_err(fep->netdev, "MDIO write timeout\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int fec_enet_mdio_reset(struct mii_bus *bus)
+{
+       return 0;
+}
+
+static int fec_enet_mii_probe(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       struct phy_device *phy_dev = NULL;
+       char mdio_bus_id[MII_BUS_ID_SIZE];
+       char phy_name[MII_BUS_ID_SIZE + 3];
+       int phy_id;
+       int dev_id = fep->dev_id;
+
+       fep->phy_dev = NULL;
+
+       /* 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)
+                       continue;
+               if (dev_id--)
+                       continue;
+               strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
+               break;
+       }
+
+       if (phy_id >= PHY_MAX_ADDR) {
+               netdev_info(ndev, "no PHY, assuming direct connection to switch\n");
+               strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
+               phy_id = 0;
+       }
+
+       snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id);
+       phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
+                             fep->phy_interface);
+       if (IS_ERR(phy_dev)) {
+               netdev_err(ndev, "could not attach to PHY\n");
+               return PTR_ERR(phy_dev);
+       }
+
+       /* mask with MAC supported features */
+       if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
+               phy_dev->supported &= PHY_GBIT_FEATURES;
+               phy_dev->supported |= SUPPORTED_Pause;
+       }
+       else
+               phy_dev->supported &= PHY_BASIC_FEATURES;
+
+       phy_dev->advertising = phy_dev->supported;
+
+       fep->phy_dev = phy_dev;
+       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);
+
+       return 0;
+}
+
+static int fec_enet_mii_init(struct platform_device *pdev)
+{
+       static struct mii_bus *fec0_mii_bus;
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       int err = -ENXIO, i;
+
+       /*
+        * The dual fec interfaces are not equivalent with enet-mac.
+        * Here are the differences:
+        *
+        *  - fec0 supports MII & RMII modes while fec1 only supports RMII
+        *  - fec0 acts as the 1588 time master while fec1 is slave
+        *  - external phys can only be configured by fec0
+        *
+        * That is to say fec1 can not work independently. It only works
+        * when fec0 is working. The reason behind this design is that the
+        * second interface is added primarily for Switch mode.
+        *
+        * Because of the last point above, both phys are attached on fec0
+        * mdio interface in board design, and need to be configured by
+        * fec0 mii_bus.
+        */
+       if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
+               /* fec1 uses fec0 mii_bus */
+               if (mii_cnt && fec0_mii_bus) {
+                       fep->mii_bus = fec0_mii_bus;
+                       mii_cnt++;
+                       return 0;
+               }
+               return -ENOENT;
+       }
+
+       fep->mii_timeout = 0;
+
+       /*
+        * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
+        *
+        * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
+        * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'.  The i.MX28
+        * Reference Manual has an error on this, and gets fixed on i.MX6Q
+        * document.
+        */
+       fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
+       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+               fep->phy_speed--;
+       fep->phy_speed <<= 1;
+       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+
+       fep->mii_bus = mdiobus_alloc();
+       if (fep->mii_bus == NULL) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+
+       fep->mii_bus->name = "fec_enet_mii_bus";
+       fep->mii_bus->read = fec_enet_mdio_read;
+       fep->mii_bus->write = fec_enet_mdio_write;
+       fep->mii_bus->reset = fec_enet_mdio_reset;
+       snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+               pdev->name, fep->dev_id + 1);
+       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;
+
+       if (mdiobus_register(fep->mii_bus))
+               goto err_out_free_mdio_irq;
+
+       mii_cnt++;
+
+       /* save fec0 mii_bus */
+       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+               fec0_mii_bus = fep->mii_bus;
+
+       return 0;
+
+err_out_free_mdio_irq:
+       kfree(fep->mii_bus->irq);
+err_out_free_mdiobus:
+       mdiobus_free(fep->mii_bus);
+err_out:
+       return err;
+}
+
+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);
+       }
+}
+
+static int fec_enet_get_settings(struct net_device *ndev,
+                                 struct ethtool_cmd *cmd)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct phy_device *phydev = fep->phy_dev;
+
+       if (!phydev)
+               return -ENODEV;
+
+       return phy_ethtool_gset(phydev, cmd);
+}
+
+static int fec_enet_set_settings(struct net_device *ndev,
+                                struct ethtool_cmd *cmd)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct phy_device *phydev = fep->phy_dev;
+
+       if (!phydev)
+               return -ENODEV;
+
+       return phy_ethtool_sset(phydev, cmd);
+}
+
+static void fec_enet_get_drvinfo(struct net_device *ndev,
+                                struct ethtool_drvinfo *info)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       strlcpy(info->driver, fep->pdev->dev.driver->name,
+               sizeof(info->driver));
+       strlcpy(info->version, "Revision: 1.0", sizeof(info->version));
+       strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info));
+}
+
+static int fec_enet_get_ts_info(struct net_device *ndev,
+                               struct ethtool_ts_info *info)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       if (fep->bufdesc_ex) {
+
+               info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
+                                       SOF_TIMESTAMPING_RX_SOFTWARE |
+                                       SOF_TIMESTAMPING_SOFTWARE |
+                                       SOF_TIMESTAMPING_TX_HARDWARE |
+                                       SOF_TIMESTAMPING_RX_HARDWARE |
+                                       SOF_TIMESTAMPING_RAW_HARDWARE;
+               if (fep->ptp_clock)
+                       info->phc_index = ptp_clock_index(fep->ptp_clock);
+               else
+                       info->phc_index = -1;
+
+               info->tx_types = (1 << HWTSTAMP_TX_OFF) |
+                                (1 << HWTSTAMP_TX_ON);
+
+               info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
+                                  (1 << HWTSTAMP_FILTER_ALL);
+               return 0;
+       } else {
+               return ethtool_op_get_ts_info(ndev, info);
+       }
+}
+
+static void fec_enet_get_pauseparam(struct net_device *ndev,
+                                   struct ethtool_pauseparam *pause)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
+       pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
+       pause->rx_pause = pause->tx_pause;
+}
+
+static int fec_enet_set_pauseparam(struct net_device *ndev,
+                                  struct ethtool_pauseparam *pause)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       if (pause->tx_pause != pause->rx_pause) {
+               netdev_info(ndev,
+                       "hardware only support enable/disable both tx and rx");
+               return -EINVAL;
+       }
+
+       fep->pause_flag = 0;
+
+       /* tx pause must be same as rx pause */
+       fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
+       fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
+
+       if (pause->rx_pause || pause->autoneg) {
+               fep->phy_dev->supported |= ADVERTISED_Pause;
+               fep->phy_dev->advertising |= ADVERTISED_Pause;
+       } else {
+               fep->phy_dev->supported &= ~ADVERTISED_Pause;
+               fep->phy_dev->advertising &= ~ADVERTISED_Pause;
+       }
+
+       if (pause->autoneg) {
+               if (netif_running(ndev))
+                       fec_stop(ndev);
+               phy_start_aneg(fep->phy_dev);
+       }
+       if (netif_running(ndev))
+               fec_restart(ndev, 0);
+
+       return 0;
+}
+
+static const struct ethtool_ops fec_enet_ethtool_ops = {
+       .get_pauseparam         = fec_enet_get_pauseparam,
+       .set_pauseparam         = fec_enet_set_pauseparam,
+       .get_settings           = fec_enet_get_settings,
+       .set_settings           = fec_enet_set_settings,
+       .get_drvinfo            = fec_enet_get_drvinfo,
+       .get_link               = ethtool_op_get_link,
+       .get_ts_info            = fec_enet_get_ts_info,
+};
+
+static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct phy_device *phydev = fep->phy_dev;
+
+       if (!netif_running(ndev))
+               return -EINVAL;
+
+       if (!phydev)
+               return -ENODEV;
+
+       if (cmd == SIOCSHWTSTAMP && fep->bufdesc_ex)
+               return fec_ptp_ioctl(ndev, rq, cmd);
+
+       return phy_mii_ioctl(phydev, rq, cmd);
+}
+
+static void fec_enet_free_buffers(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       unsigned int i;
+       struct sk_buff *skb;
+       struct bufdesc  *bdp;
+
+       bdp = fep->rx_bd_base;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               skb = fep->rx_skbuff[i];
+
+               if (bdp->cbd_bufaddr)
+                       dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                                       FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+               if (skb)
+                       dev_kfree_skb(skb);
+               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+       }
+
+       bdp = fep->tx_bd_base;
+       for (i = 0; i < TX_RING_SIZE; i++)
+               kfree(fep->tx_bounce[i]);
+}
+
+static int fec_enet_alloc_buffers(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       unsigned int i;
+       struct sk_buff *skb;
+       struct bufdesc  *bdp;
+
+       bdp = fep->rx_bd_base;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
+               if (!skb) {
+                       fec_enet_free_buffers(ndev);
+                       return -ENOMEM;
+               }
+               fep->rx_skbuff[i] = skb;
+
+               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
+                               FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+               bdp->cbd_sc = BD_ENET_RX_EMPTY;
+
+               if (fep->bufdesc_ex) {
+                       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+                       ebdp->cbd_esc = BD_ENET_RX_INT;
+               }
+
+               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+       }
+
+       /* Set the last buffer to wrap. */
+       bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
+       bdp->cbd_sc |= BD_SC_WRAP;
+
+       bdp = fep->tx_bd_base;
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
+
+               bdp->cbd_sc = 0;
+               bdp->cbd_bufaddr = 0;
+
+               if (fep->bufdesc_ex) {
+                       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+                       ebdp->cbd_esc = BD_ENET_TX_INT;
+               }
+
+               bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+       }
+
+       /* Set the last buffer to wrap. */
+       bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
+       bdp->cbd_sc |= BD_SC_WRAP;
+
+       return 0;
+}
+
+static int
+fec_enet_open(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int ret;
+
+       napi_enable(&fep->napi);
+
+       /* I should reset the ring buffers here, but I don't yet know
+        * a simple way to do that.
+        */
+
+       ret = fec_enet_alloc_buffers(ndev);
+       if (ret)
+               return ret;
+
+       /* Probe and connect to PHY when open the interface */
+       ret = fec_enet_mii_probe(ndev);
+       if (ret) {
+               fec_enet_free_buffers(ndev);
+               return ret;
+       }
+       phy_start(fep->phy_dev);
+       netif_start_queue(ndev);
+       fep->opened = 1;
+       return 0;
+}
+
+static int
+fec_enet_close(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       /* Don't know what to do yet. */
+       napi_disable(&fep->napi);
+       fep->opened = 0;
+       netif_stop_queue(ndev);
+       fec_stop(ndev);
+
+       if (fep->phy_dev) {
+               phy_stop(fep->phy_dev);
+               phy_disconnect(fep->phy_dev);
+       }
+
+       fec_enet_free_buffers(ndev);
+
+       return 0;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ * Skeleton taken from sunlance driver.
+ * The CPM Ethernet implementation allows Multicast as well as individual
+ * MAC address filtering.  Some of the drivers check to make sure it is
+ * a group multicast address, and discard those that are not.  I guess I
+ * will do the same for now, but just remove the test if you want
+ * individual filtering as well (do the upper net layers want or support
+ * this kind of feature?).
+ */
+
+#define HASH_BITS      6               /* #bits in hash */
+#define CRC32_POLY     0xEDB88320
+
+static void set_multicast_list(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct netdev_hw_addr *ha;
+       unsigned int i, bit, data, crc, tmp;
+       unsigned char hash;
+
+       if (ndev->flags & IFF_PROMISC) {
+               tmp = readl(fep->hwp + FEC_R_CNTRL);
+               tmp |= 0x8;
+               writel(tmp, fep->hwp + FEC_R_CNTRL);
+               return;
+       }
+
+       tmp = readl(fep->hwp + FEC_R_CNTRL);
+       tmp &= ~0x8;
+       writel(tmp, fep->hwp + FEC_R_CNTRL);
+
+       if (ndev->flags & IFF_ALLMULTI) {
+               /* Catch all multicast addresses, so set the
+                * filter to all 1's
+                */
+               writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+               writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+
+               return;
+       }
+
+       /* Clear filter and add the addresses in hash register
+        */
+       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+
+       netdev_for_each_mc_addr(ha, ndev) {
+               /* calculate crc32 value of mac address */
+               crc = 0xffffffff;
+
+               for (i = 0; i < ndev->addr_len; i++) {
+                       data = ha->addr[i];
+                       for (bit = 0; bit < 8; bit++, data >>= 1) {
+                               crc = (crc >> 1) ^
+                               (((crc ^ data) & 1) ? CRC32_POLY : 0);
+                       }
+               }
+
+               /* only upper 6 bits (HASH_BITS) are used
+                * which point to specific bit in he hash registers
+                */
+               hash = (crc >> (32 - HASH_BITS)) & 0x3f;
+
+               if (hash > 31) {
+                       tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+                       tmp |= 1 << (hash - 32);
+                       writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+               } else {
+                       tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+                       tmp |= 1 << hash;
+                       writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+               }
+       }
+}
+
+/* Set a MAC change in hardware. */
+static int
+fec_set_mac_address(struct net_device *ndev, void *p)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct sockaddr *addr = p;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
+
+       writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
+               (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
+               fep->hwp + FEC_ADDR_LOW);
+       writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
+               fep->hwp + FEC_ADDR_HIGH);
+       return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * fec_poll_controller - FEC Poll controller function
+ * @dev: The FEC network adapter
+ *
+ * Polled functionality used by netconsole and others in non interrupt mode
+ *
+ */
+static void fec_poll_controller(struct net_device *dev)
+{
+       int i;
+       struct fec_enet_private *fep = netdev_priv(dev);
+
+       for (i = 0; i < FEC_IRQ_NUM; i++) {
+               if (fep->irq[i] > 0) {
+                       disable_irq(fep->irq[i]);
+                       fec_enet_interrupt(fep->irq[i], dev);
+                       enable_irq(fep->irq[i]);
+               }
+       }
+}
+#endif
+
+static const struct net_device_ops fec_netdev_ops = {
+       .ndo_open               = fec_enet_open,
+       .ndo_stop               = fec_enet_close,
+       .ndo_start_xmit         = fec_enet_start_xmit,
+       .ndo_set_rx_mode        = set_multicast_list,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_tx_timeout         = fec_timeout,
+       .ndo_set_mac_address    = fec_set_mac_address,
+       .ndo_do_ioctl           = fec_enet_ioctl,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = fec_poll_controller,
+#endif
+};
+
+ /*
+  * XXX:  We need to clean up on failure exits here.
+  *
+  */
+static int fec_enet_init(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct bufdesc *cbd_base;
+
+       /* Allocate memory for buffer descriptors. */
+       cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
+                                     GFP_KERNEL);
+       if (!cbd_base)
+               return -ENOMEM;
+
+       memset(cbd_base, 0, PAGE_SIZE);
+       spin_lock_init(&fep->hw_lock);
+
+       fep->netdev = ndev;
+
+       /* Get the Ethernet address */
+       fec_get_mac(ndev);
+
+       /* Set receive and transmit descriptor base. */
+       fep->rx_bd_base = cbd_base;
+       if (fep->bufdesc_ex)
+               fep->tx_bd_base = (struct bufdesc *)
+                       (((struct bufdesc_ex *)cbd_base) + RX_RING_SIZE);
+       else
+               fep->tx_bd_base = cbd_base + RX_RING_SIZE;
+
+       /* The FEC Ethernet specific entries in the device structure */
+       ndev->watchdog_timeo = TX_TIMEOUT;
+       ndev->netdev_ops = &fec_netdev_ops;
+       ndev->ethtool_ops = &fec_enet_ethtool_ops;
+
+       writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
+       netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
+
+       fec_restart(ndev, 0);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static void fec_reset_phy(struct platform_device *pdev)
+{
+       int err, phy_reset;
+       int msec = 1;
+       struct device_node *np = pdev->dev.of_node;
+
+       if (!np)
+               return;
+
+       of_property_read_u32(np, "phy-reset-duration", &msec);
+       /* A sane reset duration should not be longer than 1s */
+       if (msec > 1000)
+               msec = 1;
+
+       phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
+       if (!gpio_is_valid(phy_reset))
+               return;
+
+       err = devm_gpio_request_one(&pdev->dev, phy_reset,
+                                   GPIOF_OUT_INIT_LOW, "phy-reset");
+       if (err) {
+               dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
+               return;
+       }
+       msleep(msec);
+       gpio_set_value(phy_reset, 1);
+}
+#else /* CONFIG_OF */
+static void fec_reset_phy(struct platform_device *pdev)
+{
+       /*
+        * In case of platform probe, the reset has been done
+        * by machine code.
+        */
+}
+#endif /* CONFIG_OF */
+
+static int
+fec_probe(struct platform_device *pdev)
+{
+       struct fec_enet_private *fep;
+       struct fec_platform_data *pdata;
+       struct net_device *ndev;
+       int i, irq, ret = 0;
+       struct resource *r;
+       const struct of_device_id *of_id;
+       static int dev_id;
+       struct pinctrl *pinctrl;
+       struct regulator *reg_phy;
+
+       of_id = of_match_device(fec_dt_ids, &pdev->dev);
+       if (of_id)
+               pdev->id_entry = of_id->data;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r)
+               return -ENXIO;
+
+       /* Init network device */
+       ndev = alloc_etherdev(sizeof(struct fec_enet_private));
+       if (!ndev)
+               return -ENOMEM;
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       /* setup board info structure */
+       fep = netdev_priv(ndev);
+
+       /* default enable pause frame auto negotiation */
+       if (pdev->id_entry &&
+           (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT))
+               fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
+
+       fep->hwp = devm_request_and_ioremap(&pdev->dev, r);
+       fep->pdev = pdev;
+       fep->dev_id = dev_id++;
+
+       fep->bufdesc_ex = 0;
+
+       if (!fep->hwp) {
+               ret = -ENOMEM;
+               goto failed_ioremap;
+       }
+
+       platform_set_drvdata(pdev, ndev);
+
+       ret = of_get_phy_mode(pdev->dev.of_node);
+       if (ret < 0) {
+               pdata = pdev->dev.platform_data;
+               if (pdata)
+                       fep->phy_interface = pdata->phy;
+               else
+                       fep->phy_interface = PHY_INTERFACE_MODE_MII;
+       } else {
+               fep->phy_interface = ret;
+       }
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl)) {
+               ret = PTR_ERR(pinctrl);
+               goto failed_pin;
+       }
+
+       fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+       if (IS_ERR(fep->clk_ipg)) {
+               ret = PTR_ERR(fep->clk_ipg);
+               goto failed_clk;
+       }
+
+       fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+       if (IS_ERR(fep->clk_ahb)) {
+               ret = PTR_ERR(fep->clk_ahb);
+               goto failed_clk;
+       }
+
+       fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
+       fep->bufdesc_ex =
+               pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
+       if (IS_ERR(fep->clk_ptp)) {
+               ret = PTR_ERR(fep->clk_ptp);
+               fep->bufdesc_ex = 0;
+       }
+
+       clk_prepare_enable(fep->clk_ahb);
+       clk_prepare_enable(fep->clk_ipg);
+       if (!IS_ERR(fep->clk_ptp))
+               clk_prepare_enable(fep->clk_ptp);
+
+       reg_phy = devm_regulator_get(&pdev->dev, "phy");
+       if (!IS_ERR(reg_phy)) {
+               ret = regulator_enable(reg_phy);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "Failed to enable phy regulator: %d\n", ret);
+                       goto failed_regulator;
+               }
+       }
+
+       fec_reset_phy(pdev);
+
+       if (fep->bufdesc_ex)
+               fec_ptp_init(ndev, pdev);
+
+       ret = fec_enet_init(ndev);
+       if (ret)
+               goto failed_init;
+
+       for (i = 0; i < FEC_IRQ_NUM; i++) {
+               irq = platform_get_irq(pdev, i);
+               if (irq < 0) {
+                       if (i)
+                               break;
+                       ret = irq;
+                       goto failed_irq;
+               }
+               ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
+               if (ret) {
+                       while (--i >= 0) {
+                               irq = platform_get_irq(pdev, i);
+                               free_irq(irq, ndev);
+                       }
+                       goto failed_irq;
+               }
+       }
+
+       ret = fec_enet_mii_init(pdev);
+       if (ret)
+               goto failed_mii_init;
+
+       /* Carrier starts down, phylib will bring it up */
+       netif_carrier_off(ndev);
+
+       ret = register_netdev(ndev);
+       if (ret)
+               goto failed_register;
+
+       if (fep->bufdesc_ex && fep->ptp_clock)
+               netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
+
+       return 0;
+
+failed_register:
+       fec_enet_mii_remove(fep);
+failed_mii_init:
+failed_init:
+       for (i = 0; i < FEC_IRQ_NUM; i++) {
+               irq = platform_get_irq(pdev, i);
+               if (irq > 0)
+                       free_irq(irq, ndev);
+       }
+failed_irq:
+failed_regulator:
+       clk_disable_unprepare(fep->clk_ahb);
+       clk_disable_unprepare(fep->clk_ipg);
+       if (!IS_ERR(fep->clk_ptp))
+               clk_disable_unprepare(fep->clk_ptp);
+failed_pin:
+failed_clk:
+failed_ioremap:
+       free_netdev(ndev);
+
+       return ret;
+}
+
+static int
+fec_drv_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int i;
+
+       unregister_netdev(ndev);
+       fec_enet_mii_remove(fep);
+       del_timer_sync(&fep->time_keep);
+       clk_disable_unprepare(fep->clk_ptp);
+       if (fep->ptp_clock)
+               ptp_clock_unregister(fep->ptp_clock);
+       clk_disable_unprepare(fep->clk_ahb);
+       clk_disable_unprepare(fep->clk_ipg);
+       for (i = 0; i < FEC_IRQ_NUM; i++) {
+               int irq = platform_get_irq(pdev, i);
+               if (irq > 0)
+                       free_irq(irq, ndev);
+       }
+       free_netdev(ndev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int
+fec_suspend(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       if (netif_running(ndev)) {
+               fec_stop(ndev);
+               netif_device_detach(ndev);
+       }
+       clk_disable_unprepare(fep->clk_ahb);
+       clk_disable_unprepare(fep->clk_ipg);
+
+       return 0;
+}
+
+static int
+fec_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       clk_prepare_enable(fep->clk_ahb);
+       clk_prepare_enable(fep->clk_ipg);
+       if (netif_running(ndev)) {
+               fec_restart(ndev, fep->full_duplex);
+               netif_device_attach(ndev);
+       }
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(fec_pm_ops, fec_suspend, fec_resume);
+
+static struct platform_driver fec_driver = {
+       .driver = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .pm     = &fec_pm_ops,
+               .of_match_table = fec_dt_ids,
+       },
+       .id_table = fec_devtype,
+       .probe  = fec_probe,
+       .remove = fec_drv_remove,
+};
+
+module_platform_driver(fec_driver);
+
+MODULE_LICENSE("GPL");
index 77943a6a1b8cc4c1ae80e40ba0b76536eac4806d..9bc15e2365bbc924631da31872b10ae94e0062c7 100644 (file)
@@ -14,6 +14,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
 
@@ -858,13 +860,11 @@ static int mpc52xx_fec_probe(struct platform_device *op)
        /* Reserve FEC control zone */
        rv = of_address_to_resource(np, 0, &mem);
        if (rv) {
-               printk(KERN_ERR DRIVER_NAME ": "
-                               "Error while parsing device node resource\n" );
+               pr_err("Error while parsing device node resource\n");
                goto err_netdev;
        }
        if (resource_size(&mem) < sizeof(struct mpc52xx_fec)) {
-               printk(KERN_ERR DRIVER_NAME
-                      " - invalid resource size (%lx < %x), check mpc52xx_devices.c\n",
+               pr_err("invalid resource size (%lx < %x), check mpc52xx_devices.c\n",
                       (unsigned long)resource_size(&mem),
                       sizeof(struct mpc52xx_fec));
                rv = -EINVAL;
@@ -902,7 +902,7 @@ static int mpc52xx_fec_probe(struct platform_device *op)
        priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo);
 
        if (!priv->rx_dmatsk || !priv->tx_dmatsk) {
-               printk(KERN_ERR DRIVER_NAME ": Can not init SDMA tasks\n" );
+               pr_err("Can not init SDMA tasks\n");
                rv = -ENOMEM;
                goto err_rx_tx_dmatsk;
        }
@@ -982,8 +982,8 @@ static int mpc52xx_fec_probe(struct platform_device *op)
 
        /* We're done ! */
        dev_set_drvdata(&op->dev, ndev);
-       printk(KERN_INFO "%s: %s MAC %pM\n",
-              ndev->name, op->dev.of_node->full_name, ndev->dev_addr);
+       netdev_info(ndev, "%s MAC %pM\n",
+                   op->dev.of_node->full_name, ndev->dev_addr);
 
        return 0;
 
@@ -1094,7 +1094,7 @@ mpc52xx_fec_init(void)
        int ret;
        ret = platform_driver_register(&mpc52xx_fec_mdio_driver);
        if (ret) {
-               printk(KERN_ERR DRIVER_NAME ": failed to register mdio driver\n");
+               pr_err("failed to register mdio driver\n");
                return ret;
        }
 #endif
index 0d8df400a4798c06a1345773b9a0f821b4ab7cb2..25fc960cbf0e9bc2e2f9bdd9e01e6f91eb2ebffd 100644 (file)
@@ -17,6 +17,8 @@
  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
@@ -128,7 +130,6 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev)
 
        spin_unlock_irqrestore(&fep->tmreg_lock, flags);
 }
-EXPORT_SYMBOL(fec_ptp_start_cyclecounter);
 
 /**
  * fec_ptp_adjfreq - adjust ptp cycle frequency
@@ -319,7 +320,6 @@ int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
        return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
            -EFAULT : 0;
 }
-EXPORT_SYMBOL(fec_ptp_ioctl);
 
 /**
  * fec_time_keep - call timecounter_read every second to avoid timer overrun
@@ -381,8 +381,5 @@ void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev)
        if (IS_ERR(fep->ptp_clock)) {
                fep->ptp_clock = NULL;
                pr_err("ptp_clock_register failed\n");
-       } else {
-               pr_info("registered PHC device on %s\n", ndev->name);
        }
 }
-EXPORT_SYMBOL(fec_ptp_init);
index 46df28893c10649e0de42354671559b37bfef0c2..edc120094c340bbe61dbd33f4a3cbbf9b9a3e8d8 100644 (file)
@@ -177,8 +177,6 @@ static int fs_enet_rx_napi(struct napi_struct *napi, int budget)
                                received++;
                                netif_receive_skb(skb);
                        } else {
-                               dev_warn(fep->dev,
-                                        "Memory squeeze, dropping packet.\n");
                                fep->stats.rx_dropped++;
                                skbn = skb;
                        }
@@ -309,8 +307,6 @@ static int fs_enet_rx_non_napi(struct net_device *dev)
                                received++;
                                netif_rx(skb);
                        } else {
-                               dev_warn(fep->dev,
-                                        "Memory squeeze, dropping packet.\n");
                                fep->stats.rx_dropped++;
                                skbn = skb;
                        }
@@ -505,11 +501,9 @@ void fs_init_bds(struct net_device *dev)
         */
        for (i = 0, bdp = fep->rx_bd_base; i < fep->rx_ring; i++, bdp++) {
                skb = netdev_alloc_skb(dev, ENET_RX_FRSIZE);
-               if (skb == NULL) {
-                       dev_warn(fep->dev,
-                                "Memory squeeze, unable to allocate skb\n");
+               if (skb == NULL)
                        break;
-               }
+
                skb_align(skb, ENET_RX_ALIGN);
                fep->rx_skbuff[i] = skb;
                CBDW_BUFADDR(bdp,
@@ -593,13 +587,8 @@ static struct sk_buff *tx_skb_align_workaround(struct net_device *dev,
 
        /* Alloc new skb */
        new_skb = netdev_alloc_skb(dev, skb->len + 4);
-       if (!new_skb) {
-               if (net_ratelimit()) {
-                       dev_warn(fep->dev,
-                                "Memory squeeze, dropping tx packet.\n");
-               }
+       if (!new_skb)
                return NULL;
-       }
 
        /* Make sure new skb is properly aligned */
        skb_align(new_skb, 4);
index d2c5441d1bf0a47e1f2d7de789bf1130198da5ee..2375a01715a0e93fa8f78c89dfcc90a88325585e 100644 (file)
@@ -132,7 +132,7 @@ static int gfar_poll(struct napi_struct *napi, int budget);
 static void gfar_netpoll(struct net_device *dev);
 #endif
 int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
-static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
+static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
 static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
                               int amount_pull, struct napi_struct *napi);
 void gfar_halt(struct net_device *dev);
@@ -245,14 +245,13 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
 
        /* Allocate memory for the buffer descriptors */
        vaddr = dma_alloc_coherent(dev,
-                       sizeof(struct txbd8) * priv->total_tx_ring_size +
-                       sizeof(struct rxbd8) * priv->total_rx_ring_size,
-                       &addr, GFP_KERNEL);
-       if (!vaddr) {
-               netif_err(priv, ifup, ndev,
-                         "Could not allocate buffer descriptors!\n");
+                                  (priv->total_tx_ring_size *
+                                   sizeof(struct txbd8)) +
+                                  (priv->total_rx_ring_size *
+                                   sizeof(struct rxbd8)),
+                                  &addr, GFP_KERNEL);
+       if (!vaddr)
                return -ENOMEM;
-       }
 
        for (i = 0; i < priv->num_tx_queues; i++) {
                tx_queue = priv->tx_queue[i];
@@ -342,7 +341,7 @@ static void gfar_init_mac(struct net_device *ndev)
        gfar_init_tx_rx_base(priv);
 
        /* Configure the coalescing support */
-       gfar_configure_coalescing(priv, 0xFF, 0xFF);
+       gfar_configure_coalescing_all(priv);
 
        /* set this when rx hw offload (TOE) functions are being used */
        priv->uses_rxfcb = 0;
@@ -387,7 +386,7 @@ static void gfar_init_mac(struct net_device *ndev)
                priv->uses_rxfcb = 1;
        }
 
-       if (ndev->features & NETIF_F_HW_VLAN_RX) {
+       if (ndev->features & NETIF_F_HW_VLAN_CTAG_RX) {
                rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
                priv->uses_rxfcb = 1;
        }
@@ -691,7 +690,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
        }
 
        for (i = 0; i < priv->num_tx_queues; i++)
-              priv->tx_queue[i] = NULL;
+               priv->tx_queue[i] = NULL;
        for (i = 0; i < priv->num_rx_queues; i++)
                priv->rx_queue[i] = NULL;
 
@@ -1051,8 +1050,9 @@ static int gfar_probe(struct platform_device *ofdev)
        }
 
        if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
-               dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-               dev->features |= NETIF_F_HW_VLAN_RX;
+               dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
+                                   NETIF_F_HW_VLAN_CTAG_RX;
+               dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
        }
 
        if (priv->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
@@ -1817,25 +1817,15 @@ void gfar_start(struct net_device *dev)
        dev->trans_start = jiffies; /* prevent tx timeout */
 }
 
-void gfar_configure_coalescing(struct gfar_private *priv,
+static void gfar_configure_coalescing(struct gfar_private *priv,
                               unsigned long tx_mask, unsigned long rx_mask)
 {
        struct gfar __iomem *regs = priv->gfargrp[0].regs;
        u32 __iomem *baddr;
-       int i = 0;
-
-       /* Backward compatible case ---- even if we enable
-        * multiple queues, there's only single reg to program
-        */
-       gfar_write(&regs->txic, 0);
-       if (likely(priv->tx_queue[0]->txcoalescing))
-               gfar_write(&regs->txic, priv->tx_queue[0]->txic);
-
-       gfar_write(&regs->rxic, 0);
-       if (unlikely(priv->rx_queue[0]->rxcoalescing))
-               gfar_write(&regs->rxic, priv->rx_queue[0]->rxic);
 
        if (priv->mode == MQ_MG_MODE) {
+               int i = 0;
+
                baddr = &regs->txic0;
                for_each_set_bit(i, &tx_mask, priv->num_tx_queues) {
                        gfar_write(baddr + i, 0);
@@ -1849,9 +1839,25 @@ void gfar_configure_coalescing(struct gfar_private *priv,
                        if (likely(priv->rx_queue[i]->rxcoalescing))
                                gfar_write(baddr + i, priv->rx_queue[i]->rxic);
                }
+       } else {
+               /* Backward compatible case -- even if we enable
+                * multiple queues, there's only single reg to program
+                */
+               gfar_write(&regs->txic, 0);
+               if (likely(priv->tx_queue[0]->txcoalescing))
+                       gfar_write(&regs->txic, priv->tx_queue[0]->txic);
+
+               gfar_write(&regs->rxic, 0);
+               if (unlikely(priv->rx_queue[0]->rxcoalescing))
+                       gfar_write(&regs->rxic, priv->rx_queue[0]->rxic);
        }
 }
 
+void gfar_configure_coalescing_all(struct gfar_private *priv)
+{
+       gfar_configure_coalescing(priv, 0xFF, 0xFF);
+}
+
 static int register_grp_irqs(struct gfar_priv_grp *grp)
 {
        struct gfar_private *priv = grp->priv;
@@ -1941,7 +1947,7 @@ int startup_gfar(struct net_device *ndev)
 
        phy_start(priv->phydev);
 
-       gfar_configure_coalescing(priv, 0xFF, 0xFF);
+       gfar_configure_coalescing_all(priv);
 
        return 0;
 
@@ -2343,7 +2349,7 @@ void gfar_vlan_mode(struct net_device *dev, netdev_features_t features)
        local_irq_save(flags);
        lock_rx_qs(priv);
 
-       if (features & NETIF_F_HW_VLAN_TX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_TX) {
                /* Enable VLAN tag insertion */
                tempval = gfar_read(&regs->tctrl);
                tempval |= TCTRL_VLINS;
@@ -2355,7 +2361,7 @@ void gfar_vlan_mode(struct net_device *dev, netdev_features_t features)
                gfar_write(&regs->tctrl, tempval);
        }
 
-       if (features & NETIF_F_HW_VLAN_RX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_RX) {
                /* Enable VLAN tag extraction */
                tempval = gfar_read(&regs->rctrl);
                tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
@@ -2469,12 +2475,11 @@ static void gfar_align_skb(struct sk_buff *skb)
 }
 
 /* Interrupt Handler for Transmit complete */
-static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
+static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
 {
        struct net_device *dev = tx_queue->dev;
        struct netdev_queue *txq;
        struct gfar_private *priv = netdev_priv(dev);
-       struct gfar_priv_rx_q *rx_queue = NULL;
        struct txbd8 *bdp, *next = NULL;
        struct txbd8 *lbdp = NULL;
        struct txbd8 *base = tx_queue->tx_bd_base;
@@ -2489,7 +2494,6 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
        u32 lstatus;
        size_t buflen;
 
-       rx_queue = priv->rx_queue[tqi];
        txq = netdev_get_tx_queue(dev, tqi);
        bdp = tx_queue->dirty_tx;
        skb_dirtytx = tx_queue->skb_dirtytx;
@@ -2571,8 +2575,6 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
        tx_queue->dirty_tx = bdp;
 
        netdev_tx_completed_queue(txq, howmany, bytes_sent);
-
-       return howmany;
 }
 
 static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
@@ -2694,8 +2696,6 @@ static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
        struct gfar_private *priv = netdev_priv(dev);
        struct rxfcb *fcb = NULL;
 
-       gro_result_t ret;
-
        /* fcb is at the beginning if exists */
        fcb = (struct rxfcb *)skb->data;
 
@@ -2725,19 +2725,17 @@ static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
        /* Tell the skb what kind of packet this is */
        skb->protocol = eth_type_trans(skb, dev);
 
-       /* There's need to check for NETIF_F_HW_VLAN_RX here.
+       /* There's need to check for NETIF_F_HW_VLAN_CTAG_RX here.
         * Even if vlan rx accel is disabled, on some chips
         * RXFCB_VLN is pseudo randomly set.
         */
-       if (dev->features & NETIF_F_HW_VLAN_RX &&
+       if (dev->features & NETIF_F_HW_VLAN_CTAG_RX &&
            fcb->flags & RXFCB_VLN)
-               __vlan_hwaccel_put_tag(skb, fcb->vlctl);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), fcb->vlctl);
 
        /* Send the packet up the stack */
-       ret = napi_gro_receive(napi, skb);
+       napi_gro_receive(napi, skb);
 
-       if (unlikely(GRO_DROP == ret))
-               atomic64_inc(&priv->extra_stats.kernel_dropped);
 }
 
 /* gfar_clean_rx_ring() -- Processes each frame in the rx ring
@@ -2835,62 +2833,82 @@ static int gfar_poll(struct napi_struct *napi, int budget)
        struct gfar __iomem *regs = gfargrp->regs;
        struct gfar_priv_tx_q *tx_queue = NULL;
        struct gfar_priv_rx_q *rx_queue = NULL;
-       int rx_cleaned = 0, budget_per_queue = 0, rx_cleaned_per_queue = 0;
-       int tx_cleaned = 0, i, left_over_budget = budget;
-       unsigned long serviced_queues = 0;
-       int num_queues = 0;
-
-       num_queues = gfargrp->num_rx_queues;
-       budget_per_queue = budget/num_queues;
+       int work_done = 0, work_done_per_q = 0;
+       int i, budget_per_q = 0;
+       int has_tx_work;
+       unsigned long rstat_rxf;
+       int num_act_queues;
 
        /* Clear IEVENT, so interrupts aren't called again
         * because of the packets that have already arrived
         */
        gfar_write(&regs->ievent, IEVENT_RTX_MASK);
 
-       while (num_queues && left_over_budget) {
-               budget_per_queue = left_over_budget/num_queues;
-               left_over_budget = 0;
+       rstat_rxf = gfar_read(&regs->rstat) & RSTAT_RXF_MASK;
+
+       num_act_queues = bitmap_weight(&rstat_rxf, MAX_RX_QS);
+       if (num_act_queues)
+               budget_per_q = budget/num_act_queues;
+
+       while (1) {
+               has_tx_work = 0;
+               for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) {
+                       tx_queue = priv->tx_queue[i];
+                       /* run Tx cleanup to completion */
+                       if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) {
+                               gfar_clean_tx_ring(tx_queue);
+                               has_tx_work = 1;
+                       }
+               }
 
                for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
-                       if (test_bit(i, &serviced_queues))
+                       /* skip queue if not active */
+                       if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i)))
                                continue;
+
                        rx_queue = priv->rx_queue[i];
-                       tx_queue = priv->tx_queue[rx_queue->qindex];
-
-                       tx_cleaned += gfar_clean_tx_ring(tx_queue);
-                       rx_cleaned_per_queue =
-                               gfar_clean_rx_ring(rx_queue, budget_per_queue);
-                       rx_cleaned += rx_cleaned_per_queue;
-                       if (rx_cleaned_per_queue < budget_per_queue) {
-                               left_over_budget = left_over_budget +
-                                       (budget_per_queue -
-                                        rx_cleaned_per_queue);
-                               set_bit(i, &serviced_queues);
-                               num_queues--;
+                       work_done_per_q =
+                               gfar_clean_rx_ring(rx_queue, budget_per_q);
+                       work_done += work_done_per_q;
+
+                       /* finished processing this queue */
+                       if (work_done_per_q < budget_per_q) {
+                               /* clear active queue hw indication */
+                               gfar_write(&regs->rstat,
+                                          RSTAT_CLEAR_RXF0 >> i);
+                               rstat_rxf &= ~(RSTAT_CLEAR_RXF0 >> i);
+                               num_act_queues--;
+
+                               if (!num_act_queues)
+                                       break;
+                               /* recompute budget per Rx queue */
+                               budget_per_q =
+                                       (budget - work_done) / num_act_queues;
                        }
                }
-       }
 
-       if (tx_cleaned)
-               return budget;
+               if (work_done >= budget)
+                       break;
 
-       if (rx_cleaned < budget) {
-               napi_complete(napi);
+               if (!num_act_queues && !has_tx_work) {
 
-               /* Clear the halt bit in RSTAT */
-               gfar_write(&regs->rstat, gfargrp->rstat);
+                       napi_complete(napi);
 
-               gfar_write(&regs->imask, IMASK_DEFAULT);
+                       /* Clear the halt bit in RSTAT */
+                       gfar_write(&regs->rstat, gfargrp->rstat);
 
-               /* If we are coalescing interrupts, update the timer
-                * Otherwise, clear it
-                */
-               gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
-                                         gfargrp->tx_bit_map);
+                       gfar_write(&regs->imask, IMASK_DEFAULT);
+
+                       /* If we are coalescing interrupts, update the timer
+                        * Otherwise, clear it
+                        */
+                       gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
+                                                 gfargrp->tx_bit_map);
+                       break;
+               }
        }
 
-       return rx_cleaned;
+       return work_done;
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
index 63a28d294e20693ba2845cd480e947ae991a7a45..04b552cd419d5f2d8b1546c8371b5ebbe13bf8ff 100644 (file)
@@ -291,7 +291,9 @@ extern const char gfar_driver_version[];
 #define RCTRL_PADDING(x)       ((x << 16) & RCTRL_PAL_MASK)
 
 
-#define RSTAT_CLEAR_RHALT       0x00800000
+#define RSTAT_CLEAR_RHALT      0x00800000
+#define RSTAT_CLEAR_RXF0       0x00000080
+#define RSTAT_RXF_MASK         0x000000ff
 
 #define TCTRL_IPCSEN           0x00004000
 #define TCTRL_TUCSEN           0x00002000
@@ -627,7 +629,6 @@ struct rmon_mib
 };
 
 struct gfar_extra_stats {
-       atomic64_t kernel_dropped;
        atomic64_t rx_large;
        atomic64_t rx_short;
        atomic64_t rx_nonoctet;
@@ -1180,8 +1181,7 @@ extern void stop_gfar(struct net_device *dev);
 extern void gfar_halt(struct net_device *dev);
 extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
                int enable, u32 regnum, u32 read);
-extern void gfar_configure_coalescing(struct gfar_private *priv,
-               unsigned long tx_mask, unsigned long rx_mask);
+extern void gfar_configure_coalescing_all(struct gfar_private *priv);
 void gfar_init_sysfs(struct net_device *dev);
 int gfar_set_features(struct net_device *dev, netdev_features_t features);
 extern void gfar_check_rx_parser_mode(struct gfar_private *priv);
index 75e89acf491238f56c65a0f6abf633f53388a887..21cd88124ca96a2ad3342358531868175c958fc8 100644 (file)
@@ -66,7 +66,6 @@ static void gfar_gdrvinfo(struct net_device *dev,
                          struct ethtool_drvinfo *drvinfo);
 
 static const char stat_gstrings[][ETH_GSTRING_LEN] = {
-       "rx-dropped-by-kernel",
        "rx-large-frame-errors",
        "rx-short-frame-errors",
        "rx-non-octet-errors",
@@ -390,14 +389,14 @@ static int gfar_scoalesce(struct net_device *dev,
 
        /* Check the bounds of the values */
        if (cvals->rx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
-               pr_info("Coalescing is limited to %d microseconds\n",
-                       GFAR_MAX_COAL_USECS);
+               netdev_info(dev, "Coalescing is limited to %d microseconds\n",
+                           GFAR_MAX_COAL_USECS);
                return -EINVAL;
        }
 
        if (cvals->rx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
-               pr_info("Coalescing is limited to %d frames\n",
-                       GFAR_MAX_COAL_FRAMES);
+               netdev_info(dev, "Coalescing is limited to %d frames\n",
+                           GFAR_MAX_COAL_FRAMES);
                return -EINVAL;
        }
 
@@ -419,14 +418,14 @@ static int gfar_scoalesce(struct net_device *dev,
 
        /* Check the bounds of the values */
        if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
-               pr_info("Coalescing is limited to %d microseconds\n",
-                       GFAR_MAX_COAL_USECS);
+               netdev_info(dev, "Coalescing is limited to %d microseconds\n",
+                           GFAR_MAX_COAL_USECS);
                return -EINVAL;
        }
 
        if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
-               pr_info("Coalescing is limited to %d frames\n",
-                       GFAR_MAX_COAL_FRAMES);
+               netdev_info(dev, "Coalescing is limited to %d frames\n",
+                           GFAR_MAX_COAL_FRAMES);
                return -EINVAL;
        }
 
@@ -436,7 +435,7 @@ static int gfar_scoalesce(struct net_device *dev,
                        gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
        }
 
-       gfar_configure_coalescing(priv, 0xFF, 0xFF);
+       gfar_configure_coalescing_all(priv);
 
        return 0;
 }
@@ -543,7 +542,7 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features)
        int err = 0, i = 0;
        netdev_features_t changed = dev->features ^ features;
 
-       if (changed & (NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX))
+       if (changed & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX))
                gfar_vlan_mode(dev, features);
 
        if (!(changed & NETIF_F_RXCSUM))
@@ -736,7 +735,8 @@ static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow,
                cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
                break;
        default:
-               pr_err("Right now this class is not supported\n");
+               netdev_err(priv->ndev,
+                          "Right now this class is not supported\n");
                ret = 0;
                goto err;
        }
@@ -752,7 +752,8 @@ static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow,
        }
 
        if (i == MAX_FILER_IDX + 1) {
-               pr_err("No parse rule found, can't create hash rules\n");
+               netdev_err(priv->ndev,
+                          "No parse rule found, can't create hash rules\n");
                ret = 0;
                goto err;
        }
@@ -1569,7 +1570,7 @@ static int gfar_process_filer_changes(struct gfar_private *priv)
        gfar_cluster_filer(tab);
        gfar_optimize_filer_masks(tab);
 
-       pr_debug("\n\tSummary:\n"
+       pr_debug("\tSummary:\n"
                 "\tData on hardware: %d\n"
                 "\tCompression rate: %d%%\n",
                 tab->index, 100 - (100 * tab->index) / i);
index 2e5daee0438a55891072595400d643bd8ad1340b..fe8e9e5cfb2b720728bf40faf39d56978e01c85b 100644 (file)
@@ -17,6 +17,9 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/device.h>
 #include <linux/hrtimer.h>
 #include <linux/init.h>
index cd14a4d449c2b6cfee53e24f63e8499582a70a33..acb55af7e3f36cbb8a4f3ce7f1220efc90f5ac01 100644 (file)
@@ -337,5 +337,5 @@ void gfar_init_sysfs(struct net_device *dev)
        rc |= device_create_file(&dev->dev, &dev_attr_fifo_starve);
        rc |= device_create_file(&dev->dev, &dev_attr_fifo_starve_off);
        if (rc)
-               dev_err(&dev->dev, "Error creating gianfar sysfs files.\n");
+               dev_err(&dev->dev, "Error creating gianfar sysfs files\n");
 }
index 0a70bb55d1b0fb624cf4202c855055f5969f5154..e04c59818f60ff2f9616599d34e301e97a2f65f5 100644 (file)
@@ -12,6 +12,9 @@
  * 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/kernel.h>
 #include <linux/init.h>
 #include <linux/errno.h>
 
 #define ugeth_dbg(format, arg...)            \
         ugeth_printk(KERN_DEBUG , format , ## arg)
-#define ugeth_err(format, arg...)            \
-        ugeth_printk(KERN_ERR , format , ## arg)
-#define ugeth_info(format, arg...)           \
-        ugeth_printk(KERN_INFO , format , ## arg)
-#define ugeth_warn(format, arg...)           \
-        ugeth_printk(KERN_WARNING , format , ## arg)
 
 #ifdef UGETH_VERBOSE_DEBUG
 #define ugeth_vdbg ugeth_dbg
@@ -281,7 +278,7 @@ static int fill_init_enet_entries(struct ucc_geth_private *ugeth,
        for (i = 0; i < num_entries; i++) {
                if ((snum = qe_get_snum()) < 0) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err("fill_init_enet_entries: Can not get SNUM.");
+                               pr_err("Can not get SNUM\n");
                        return snum;
                }
                if ((i == 0) && skip_page_for_first_entry)
@@ -292,7 +289,7 @@ static int fill_init_enet_entries(struct ucc_geth_private *ugeth,
                            qe_muram_alloc(thread_size, thread_alignment);
                        if (IS_ERR_VALUE(init_enet_offset)) {
                                if (netif_msg_ifup(ugeth))
-                                       ugeth_err("fill_init_enet_entries: Can not allocate DPRAM memory.");
+                                       pr_err("Can not allocate DPRAM memory\n");
                                qe_put_snum((u8) snum);
                                return -ENOMEM;
                        }
@@ -365,10 +362,9 @@ static int dump_init_enet_entries(struct ucc_geth_private *ugeth,
                                init_enet_offset =
                                    (in_be32(p_start) &
                                     ENET_INIT_PARAM_PTR_MASK);
-                               ugeth_info("Init enet entry %d:", i);
-                               ugeth_info("Base address: 0x%08x",
-                                          (u32)
-                                          qe_muram_addr(init_enet_offset));
+                               pr_info("Init enet entry %d:\n", i);
+                               pr_info("Base address: 0x%08x\n",
+                                       (u32)qe_muram_addr(init_enet_offset));
                                mem_disp(qe_muram_addr(init_enet_offset),
                                         thread_size);
                        }
@@ -396,8 +392,8 @@ static int hw_clear_addr_in_paddr(struct ucc_geth_private *ugeth, u8 paddr_num)
 {
        struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt;
 
-       if (!(paddr_num < NUM_OF_PADDRS)) {
-               ugeth_warn("%s: Illagel paddr_num.", __func__);
+       if (paddr_num >= NUM_OF_PADDRS) {
+               pr_warn("%s: Invalid paddr_num: %u\n", __func__, paddr_num);
                return -EINVAL;
        }
 
@@ -573,7 +569,7 @@ static void dump_bds(struct ucc_geth_private *ugeth)
                        length =
                            (ugeth->ug_info->bdRingLenTx[i] *
                             sizeof(struct qe_bd));
-                       ugeth_info("TX BDs[%d]", i);
+                       pr_info("TX BDs[%d]\n", i);
                        mem_disp(ugeth->p_tx_bd_ring[i], length);
                }
        }
@@ -582,7 +578,7 @@ static void dump_bds(struct ucc_geth_private *ugeth)
                        length =
                            (ugeth->ug_info->bdRingLenRx[i] *
                             sizeof(struct qe_bd));
-                       ugeth_info("RX BDs[%d]", i);
+                       pr_info("RX BDs[%d]\n", i);
                        mem_disp(ugeth->p_rx_bd_ring[i], length);
                }
        }
@@ -592,93 +588,93 @@ static void dump_regs(struct ucc_geth_private *ugeth)
 {
        int i;
 
-       ugeth_info("UCC%d Geth registers:", ugeth->ug_info->uf_info.ucc_num + 1);
-       ugeth_info("Base address: 0x%08x", (u32) ugeth->ug_regs);
-
-       ugeth_info("maccfg1    : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->maccfg1,
-                  in_be32(&ugeth->ug_regs->maccfg1));
-       ugeth_info("maccfg2    : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->maccfg2,
-                  in_be32(&ugeth->ug_regs->maccfg2));
-       ugeth_info("ipgifg     : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->ipgifg,
-                  in_be32(&ugeth->ug_regs->ipgifg));
-       ugeth_info("hafdup     : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->hafdup,
-                  in_be32(&ugeth->ug_regs->hafdup));
-       ugeth_info("ifctl      : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->ifctl,
-                  in_be32(&ugeth->ug_regs->ifctl));
-       ugeth_info("ifstat     : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->ifstat,
-                  in_be32(&ugeth->ug_regs->ifstat));
-       ugeth_info("macstnaddr1: addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->macstnaddr1,
-                  in_be32(&ugeth->ug_regs->macstnaddr1));
-       ugeth_info("macstnaddr2: addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->macstnaddr2,
-                  in_be32(&ugeth->ug_regs->macstnaddr2));
-       ugeth_info("uempr      : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->uempr,
-                  in_be32(&ugeth->ug_regs->uempr));
-       ugeth_info("utbipar    : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->utbipar,
-                  in_be32(&ugeth->ug_regs->utbipar));
-       ugeth_info("uescr      : addr - 0x%08x, val - 0x%04x",
-                  (u32) & ugeth->ug_regs->uescr,
-                  in_be16(&ugeth->ug_regs->uescr));
-       ugeth_info("tx64       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->tx64,
-                  in_be32(&ugeth->ug_regs->tx64));
-       ugeth_info("tx127      : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->tx127,
-                  in_be32(&ugeth->ug_regs->tx127));
-       ugeth_info("tx255      : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->tx255,
-                  in_be32(&ugeth->ug_regs->tx255));
-       ugeth_info("rx64       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->rx64,
-                  in_be32(&ugeth->ug_regs->rx64));
-       ugeth_info("rx127      : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->rx127,
-                  in_be32(&ugeth->ug_regs->rx127));
-       ugeth_info("rx255      : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->rx255,
-                  in_be32(&ugeth->ug_regs->rx255));
-       ugeth_info("txok       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->txok,
-                  in_be32(&ugeth->ug_regs->txok));
-       ugeth_info("txcf       : addr - 0x%08x, val - 0x%04x",
-                  (u32) & ugeth->ug_regs->txcf,
-                  in_be16(&ugeth->ug_regs->txcf));
-       ugeth_info("tmca       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->tmca,
-                  in_be32(&ugeth->ug_regs->tmca));
-       ugeth_info("tbca       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->tbca,
-                  in_be32(&ugeth->ug_regs->tbca));
-       ugeth_info("rxfok      : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->rxfok,
-                  in_be32(&ugeth->ug_regs->rxfok));
-       ugeth_info("rxbok      : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->rxbok,
-                  in_be32(&ugeth->ug_regs->rxbok));
-       ugeth_info("rbyt       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->rbyt,
-                  in_be32(&ugeth->ug_regs->rbyt));
-       ugeth_info("rmca       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->rmca,
-                  in_be32(&ugeth->ug_regs->rmca));
-       ugeth_info("rbca       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->rbca,
-                  in_be32(&ugeth->ug_regs->rbca));
-       ugeth_info("scar       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->scar,
-                  in_be32(&ugeth->ug_regs->scar));
-       ugeth_info("scam       : addr - 0x%08x, val - 0x%08x",
-                  (u32) & ugeth->ug_regs->scam,
-                  in_be32(&ugeth->ug_regs->scam));
+       pr_info("UCC%d Geth registers:\n", ugeth->ug_info->uf_info.ucc_num + 1);
+       pr_info("Base address: 0x%08x\n", (u32)ugeth->ug_regs);
+
+       pr_info("maccfg1    : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->maccfg1,
+               in_be32(&ugeth->ug_regs->maccfg1));
+       pr_info("maccfg2    : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->maccfg2,
+               in_be32(&ugeth->ug_regs->maccfg2));
+       pr_info("ipgifg     : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->ipgifg,
+               in_be32(&ugeth->ug_regs->ipgifg));
+       pr_info("hafdup     : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->hafdup,
+               in_be32(&ugeth->ug_regs->hafdup));
+       pr_info("ifctl      : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->ifctl,
+               in_be32(&ugeth->ug_regs->ifctl));
+       pr_info("ifstat     : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->ifstat,
+               in_be32(&ugeth->ug_regs->ifstat));
+       pr_info("macstnaddr1: addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->macstnaddr1,
+               in_be32(&ugeth->ug_regs->macstnaddr1));
+       pr_info("macstnaddr2: addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->macstnaddr2,
+               in_be32(&ugeth->ug_regs->macstnaddr2));
+       pr_info("uempr      : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->uempr,
+               in_be32(&ugeth->ug_regs->uempr));
+       pr_info("utbipar    : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->utbipar,
+               in_be32(&ugeth->ug_regs->utbipar));
+       pr_info("uescr      : addr - 0x%08x, val - 0x%04x\n",
+               (u32)&ugeth->ug_regs->uescr,
+               in_be16(&ugeth->ug_regs->uescr));
+       pr_info("tx64       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->tx64,
+               in_be32(&ugeth->ug_regs->tx64));
+       pr_info("tx127      : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->tx127,
+               in_be32(&ugeth->ug_regs->tx127));
+       pr_info("tx255      : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->tx255,
+               in_be32(&ugeth->ug_regs->tx255));
+       pr_info("rx64       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->rx64,
+               in_be32(&ugeth->ug_regs->rx64));
+       pr_info("rx127      : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->rx127,
+               in_be32(&ugeth->ug_regs->rx127));
+       pr_info("rx255      : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->rx255,
+               in_be32(&ugeth->ug_regs->rx255));
+       pr_info("txok       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->txok,
+               in_be32(&ugeth->ug_regs->txok));
+       pr_info("txcf       : addr - 0x%08x, val - 0x%04x\n",
+               (u32)&ugeth->ug_regs->txcf,
+               in_be16(&ugeth->ug_regs->txcf));
+       pr_info("tmca       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->tmca,
+               in_be32(&ugeth->ug_regs->tmca));
+       pr_info("tbca       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->tbca,
+               in_be32(&ugeth->ug_regs->tbca));
+       pr_info("rxfok      : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->rxfok,
+               in_be32(&ugeth->ug_regs->rxfok));
+       pr_info("rxbok      : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->rxbok,
+               in_be32(&ugeth->ug_regs->rxbok));
+       pr_info("rbyt       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->rbyt,
+               in_be32(&ugeth->ug_regs->rbyt));
+       pr_info("rmca       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->rmca,
+               in_be32(&ugeth->ug_regs->rmca));
+       pr_info("rbca       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->rbca,
+               in_be32(&ugeth->ug_regs->rbca));
+       pr_info("scar       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->scar,
+               in_be32(&ugeth->ug_regs->scar));
+       pr_info("scam       : addr - 0x%08x, val - 0x%08x\n",
+               (u32)&ugeth->ug_regs->scam,
+               in_be32(&ugeth->ug_regs->scam));
 
        if (ugeth->p_thread_data_tx) {
                int numThreadsTxNumerical;
@@ -703,13 +699,13 @@ static void dump_regs(struct ucc_geth_private *ugeth)
                        break;
                }
 
-               ugeth_info("Thread data TXs:");
-               ugeth_info("Base address: 0x%08x",
-                          (u32) ugeth->p_thread_data_tx);
+               pr_info("Thread data TXs:\n");
+               pr_info("Base address: 0x%08x\n",
+                       (u32)ugeth->p_thread_data_tx);
                for (i = 0; i < numThreadsTxNumerical; i++) {
-                       ugeth_info("Thread data TX[%d]:", i);
-                       ugeth_info("Base address: 0x%08x",
-                                  (u32) & ugeth->p_thread_data_tx[i]);
+                       pr_info("Thread data TX[%d]:\n", i);
+                       pr_info("Base address: 0x%08x\n",
+                               (u32)&ugeth->p_thread_data_tx[i]);
                        mem_disp((u8 *) & ugeth->p_thread_data_tx[i],
                                 sizeof(struct ucc_geth_thread_data_tx));
                }
@@ -737,270 +733,260 @@ static void dump_regs(struct ucc_geth_private *ugeth)
                        break;
                }
 
-               ugeth_info("Thread data RX:");
-               ugeth_info("Base address: 0x%08x",
-                          (u32) ugeth->p_thread_data_rx);
+               pr_info("Thread data RX:\n");
+               pr_info("Base address: 0x%08x\n",
+                       (u32)ugeth->p_thread_data_rx);
                for (i = 0; i < numThreadsRxNumerical; i++) {
-                       ugeth_info("Thread data RX[%d]:", i);
-                       ugeth_info("Base address: 0x%08x",
-                                  (u32) & ugeth->p_thread_data_rx[i]);
+                       pr_info("Thread data RX[%d]:\n", i);
+                       pr_info("Base address: 0x%08x\n",
+                               (u32)&ugeth->p_thread_data_rx[i]);
                        mem_disp((u8 *) & ugeth->p_thread_data_rx[i],
                                 sizeof(struct ucc_geth_thread_data_rx));
                }
        }
        if (ugeth->p_exf_glbl_param) {
-               ugeth_info("EXF global param:");
-               ugeth_info("Base address: 0x%08x",
-                          (u32) ugeth->p_exf_glbl_param);
+               pr_info("EXF global param:\n");
+               pr_info("Base address: 0x%08x\n",
+                       (u32)ugeth->p_exf_glbl_param);
                mem_disp((u8 *) ugeth->p_exf_glbl_param,
                         sizeof(*ugeth->p_exf_glbl_param));
        }
        if (ugeth->p_tx_glbl_pram) {
-               ugeth_info("TX global param:");
-               ugeth_info("Base address: 0x%08x", (u32) ugeth->p_tx_glbl_pram);
-               ugeth_info("temoder      : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_tx_glbl_pram->temoder,
-                          in_be16(&ugeth->p_tx_glbl_pram->temoder));
-               ugeth_info("sqptr        : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->sqptr,
-                          in_be32(&ugeth->p_tx_glbl_pram->sqptr));
-               ugeth_info("schedulerbasepointer: addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->schedulerbasepointer,
-                          in_be32(&ugeth->p_tx_glbl_pram->
-                                  schedulerbasepointer));
-               ugeth_info("txrmonbaseptr: addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->txrmonbaseptr,
-                          in_be32(&ugeth->p_tx_glbl_pram->txrmonbaseptr));
-               ugeth_info("tstate       : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->tstate,
-                          in_be32(&ugeth->p_tx_glbl_pram->tstate));
-               ugeth_info("iphoffset[0] : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_tx_glbl_pram->iphoffset[0],
-                          ugeth->p_tx_glbl_pram->iphoffset[0]);
-               ugeth_info("iphoffset[1] : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_tx_glbl_pram->iphoffset[1],
-                          ugeth->p_tx_glbl_pram->iphoffset[1]);
-               ugeth_info("iphoffset[2] : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_tx_glbl_pram->iphoffset[2],
-                          ugeth->p_tx_glbl_pram->iphoffset[2]);
-               ugeth_info("iphoffset[3] : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_tx_glbl_pram->iphoffset[3],
-                          ugeth->p_tx_glbl_pram->iphoffset[3]);
-               ugeth_info("iphoffset[4] : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_tx_glbl_pram->iphoffset[4],
-                          ugeth->p_tx_glbl_pram->iphoffset[4]);
-               ugeth_info("iphoffset[5] : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_tx_glbl_pram->iphoffset[5],
-                          ugeth->p_tx_glbl_pram->iphoffset[5]);
-               ugeth_info("iphoffset[6] : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_tx_glbl_pram->iphoffset[6],
-                          ugeth->p_tx_glbl_pram->iphoffset[6]);
-               ugeth_info("iphoffset[7] : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_tx_glbl_pram->iphoffset[7],
-                          ugeth->p_tx_glbl_pram->iphoffset[7]);
-               ugeth_info("vtagtable[0] : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->vtagtable[0],
-                          in_be32(&ugeth->p_tx_glbl_pram->vtagtable[0]));
-               ugeth_info("vtagtable[1] : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->vtagtable[1],
-                          in_be32(&ugeth->p_tx_glbl_pram->vtagtable[1]));
-               ugeth_info("vtagtable[2] : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->vtagtable[2],
-                          in_be32(&ugeth->p_tx_glbl_pram->vtagtable[2]));
-               ugeth_info("vtagtable[3] : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->vtagtable[3],
-                          in_be32(&ugeth->p_tx_glbl_pram->vtagtable[3]));
-               ugeth_info("vtagtable[4] : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->vtagtable[4],
-                          in_be32(&ugeth->p_tx_glbl_pram->vtagtable[4]));
-               ugeth_info("vtagtable[5] : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->vtagtable[5],
-                          in_be32(&ugeth->p_tx_glbl_pram->vtagtable[5]));
-               ugeth_info("vtagtable[6] : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->vtagtable[6],
-                          in_be32(&ugeth->p_tx_glbl_pram->vtagtable[6]));
-               ugeth_info("vtagtable[7] : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->vtagtable[7],
-                          in_be32(&ugeth->p_tx_glbl_pram->vtagtable[7]));
-               ugeth_info("tqptr        : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_tx_glbl_pram->tqptr,
-                          in_be32(&ugeth->p_tx_glbl_pram->tqptr));
+               pr_info("TX global param:\n");
+               pr_info("Base address: 0x%08x\n", (u32)ugeth->p_tx_glbl_pram);
+               pr_info("temoder      : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->temoder,
+                       in_be16(&ugeth->p_tx_glbl_pram->temoder));
+              pr_info("sqptr        : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->sqptr,
+                       in_be32(&ugeth->p_tx_glbl_pram->sqptr));
+               pr_info("schedulerbasepointer: addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->schedulerbasepointer,
+                       in_be32(&ugeth->p_tx_glbl_pram->schedulerbasepointer));
+               pr_info("txrmonbaseptr: addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->txrmonbaseptr,
+                       in_be32(&ugeth->p_tx_glbl_pram->txrmonbaseptr));
+               pr_info("tstate       : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->tstate,
+                       in_be32(&ugeth->p_tx_glbl_pram->tstate));
+               pr_info("iphoffset[0] : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->iphoffset[0],
+                       ugeth->p_tx_glbl_pram->iphoffset[0]);
+               pr_info("iphoffset[1] : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->iphoffset[1],
+                       ugeth->p_tx_glbl_pram->iphoffset[1]);
+               pr_info("iphoffset[2] : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->iphoffset[2],
+                       ugeth->p_tx_glbl_pram->iphoffset[2]);
+               pr_info("iphoffset[3] : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->iphoffset[3],
+                       ugeth->p_tx_glbl_pram->iphoffset[3]);
+               pr_info("iphoffset[4] : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->iphoffset[4],
+                       ugeth->p_tx_glbl_pram->iphoffset[4]);
+               pr_info("iphoffset[5] : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->iphoffset[5],
+                       ugeth->p_tx_glbl_pram->iphoffset[5]);
+               pr_info("iphoffset[6] : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->iphoffset[6],
+                       ugeth->p_tx_glbl_pram->iphoffset[6]);
+               pr_info("iphoffset[7] : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->iphoffset[7],
+                       ugeth->p_tx_glbl_pram->iphoffset[7]);
+               pr_info("vtagtable[0] : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->vtagtable[0],
+                       in_be32(&ugeth->p_tx_glbl_pram->vtagtable[0]));
+               pr_info("vtagtable[1] : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->vtagtable[1],
+                       in_be32(&ugeth->p_tx_glbl_pram->vtagtable[1]));
+               pr_info("vtagtable[2] : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->vtagtable[2],
+                       in_be32(&ugeth->p_tx_glbl_pram->vtagtable[2]));
+               pr_info("vtagtable[3] : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->vtagtable[3],
+                       in_be32(&ugeth->p_tx_glbl_pram->vtagtable[3]));
+               pr_info("vtagtable[4] : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->vtagtable[4],
+                       in_be32(&ugeth->p_tx_glbl_pram->vtagtable[4]));
+               pr_info("vtagtable[5] : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->vtagtable[5],
+                       in_be32(&ugeth->p_tx_glbl_pram->vtagtable[5]));
+               pr_info("vtagtable[6] : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->vtagtable[6],
+                       in_be32(&ugeth->p_tx_glbl_pram->vtagtable[6]));
+               pr_info("vtagtable[7] : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->vtagtable[7],
+                       in_be32(&ugeth->p_tx_glbl_pram->vtagtable[7]));
+               pr_info("tqptr        : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_tx_glbl_pram->tqptr,
+                       in_be32(&ugeth->p_tx_glbl_pram->tqptr));
        }
        if (ugeth->p_rx_glbl_pram) {
-               ugeth_info("RX global param:");
-               ugeth_info("Base address: 0x%08x", (u32) ugeth->p_rx_glbl_pram);
-               ugeth_info("remoder         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->remoder,
-                          in_be32(&ugeth->p_rx_glbl_pram->remoder));
-               ugeth_info("rqptr           : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->rqptr,
-                          in_be32(&ugeth->p_rx_glbl_pram->rqptr));
-               ugeth_info("typeorlen       : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_rx_glbl_pram->typeorlen,
-                          in_be16(&ugeth->p_rx_glbl_pram->typeorlen));
-               ugeth_info("rxgstpack       : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_rx_glbl_pram->rxgstpack,
-                          ugeth->p_rx_glbl_pram->rxgstpack);
-               ugeth_info("rxrmonbaseptr   : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->rxrmonbaseptr,
-                          in_be32(&ugeth->p_rx_glbl_pram->rxrmonbaseptr));
-               ugeth_info("intcoalescingptr: addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->intcoalescingptr,
-                          in_be32(&ugeth->p_rx_glbl_pram->intcoalescingptr));
-               ugeth_info("rstate          : addr - 0x%08x, val - 0x%02x",
-                          (u32) & ugeth->p_rx_glbl_pram->rstate,
-                          ugeth->p_rx_glbl_pram->rstate);
-               ugeth_info("mrblr           : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_rx_glbl_pram->mrblr,
-                          in_be16(&ugeth->p_rx_glbl_pram->mrblr));
-               ugeth_info("rbdqptr         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->rbdqptr,
-                          in_be32(&ugeth->p_rx_glbl_pram->rbdqptr));
-               ugeth_info("mflr            : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_rx_glbl_pram->mflr,
-                          in_be16(&ugeth->p_rx_glbl_pram->mflr));
-               ugeth_info("minflr          : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_rx_glbl_pram->minflr,
-                          in_be16(&ugeth->p_rx_glbl_pram->minflr));
-               ugeth_info("maxd1           : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_rx_glbl_pram->maxd1,
-                          in_be16(&ugeth->p_rx_glbl_pram->maxd1));
-               ugeth_info("maxd2           : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_rx_glbl_pram->maxd2,
-                          in_be16(&ugeth->p_rx_glbl_pram->maxd2));
-               ugeth_info("ecamptr         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->ecamptr,
-                          in_be32(&ugeth->p_rx_glbl_pram->ecamptr));
-               ugeth_info("l2qt            : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l2qt,
-                          in_be32(&ugeth->p_rx_glbl_pram->l2qt));
-               ugeth_info("l3qt[0]         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l3qt[0],
-                          in_be32(&ugeth->p_rx_glbl_pram->l3qt[0]));
-               ugeth_info("l3qt[1]         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l3qt[1],
-                          in_be32(&ugeth->p_rx_glbl_pram->l3qt[1]));
-               ugeth_info("l3qt[2]         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l3qt[2],
-                          in_be32(&ugeth->p_rx_glbl_pram->l3qt[2]));
-               ugeth_info("l3qt[3]         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l3qt[3],
-                          in_be32(&ugeth->p_rx_glbl_pram->l3qt[3]));
-               ugeth_info("l3qt[4]         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l3qt[4],
-                          in_be32(&ugeth->p_rx_glbl_pram->l3qt[4]));
-               ugeth_info("l3qt[5]         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l3qt[5],
-                          in_be32(&ugeth->p_rx_glbl_pram->l3qt[5]));
-               ugeth_info("l3qt[6]         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l3qt[6],
-                          in_be32(&ugeth->p_rx_glbl_pram->l3qt[6]));
-               ugeth_info("l3qt[7]         : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->l3qt[7],
-                          in_be32(&ugeth->p_rx_glbl_pram->l3qt[7]));
-               ugeth_info("vlantype        : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_rx_glbl_pram->vlantype,
-                          in_be16(&ugeth->p_rx_glbl_pram->vlantype));
-               ugeth_info("vlantci         : addr - 0x%08x, val - 0x%04x",
-                          (u32) & ugeth->p_rx_glbl_pram->vlantci,
-                          in_be16(&ugeth->p_rx_glbl_pram->vlantci));
+               pr_info("RX global param:\n");
+               pr_info("Base address: 0x%08x\n", (u32)ugeth->p_rx_glbl_pram);
+               pr_info("remoder         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->remoder,
+                       in_be32(&ugeth->p_rx_glbl_pram->remoder));
+               pr_info("rqptr           : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->rqptr,
+                       in_be32(&ugeth->p_rx_glbl_pram->rqptr));
+               pr_info("typeorlen       : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->typeorlen,
+                       in_be16(&ugeth->p_rx_glbl_pram->typeorlen));
+               pr_info("rxgstpack       : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->rxgstpack,
+                       ugeth->p_rx_glbl_pram->rxgstpack);
+               pr_info("rxrmonbaseptr   : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->rxrmonbaseptr,
+                       in_be32(&ugeth->p_rx_glbl_pram->rxrmonbaseptr));
+               pr_info("intcoalescingptr: addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->intcoalescingptr,
+                       in_be32(&ugeth->p_rx_glbl_pram->intcoalescingptr));
+               pr_info("rstate          : addr - 0x%08x, val - 0x%02x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->rstate,
+                       ugeth->p_rx_glbl_pram->rstate);
+               pr_info("mrblr           : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->mrblr,
+                       in_be16(&ugeth->p_rx_glbl_pram->mrblr));
+               pr_info("rbdqptr         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->rbdqptr,
+                       in_be32(&ugeth->p_rx_glbl_pram->rbdqptr));
+               pr_info("mflr            : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->mflr,
+                       in_be16(&ugeth->p_rx_glbl_pram->mflr));
+               pr_info("minflr          : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->minflr,
+                       in_be16(&ugeth->p_rx_glbl_pram->minflr));
+               pr_info("maxd1           : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->maxd1,
+                       in_be16(&ugeth->p_rx_glbl_pram->maxd1));
+               pr_info("maxd2           : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->maxd2,
+                       in_be16(&ugeth->p_rx_glbl_pram->maxd2));
+               pr_info("ecamptr         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->ecamptr,
+                       in_be32(&ugeth->p_rx_glbl_pram->ecamptr));
+               pr_info("l2qt            : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l2qt,
+                       in_be32(&ugeth->p_rx_glbl_pram->l2qt));
+               pr_info("l3qt[0]         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l3qt[0],
+                       in_be32(&ugeth->p_rx_glbl_pram->l3qt[0]));
+               pr_info("l3qt[1]         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l3qt[1],
+                       in_be32(&ugeth->p_rx_glbl_pram->l3qt[1]));
+               pr_info("l3qt[2]         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l3qt[2],
+                       in_be32(&ugeth->p_rx_glbl_pram->l3qt[2]));
+               pr_info("l3qt[3]         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l3qt[3],
+                       in_be32(&ugeth->p_rx_glbl_pram->l3qt[3]));
+               pr_info("l3qt[4]         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l3qt[4],
+                       in_be32(&ugeth->p_rx_glbl_pram->l3qt[4]));
+               pr_info("l3qt[5]         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l3qt[5],
+                       in_be32(&ugeth->p_rx_glbl_pram->l3qt[5]));
+               pr_info("l3qt[6]         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l3qt[6],
+                       in_be32(&ugeth->p_rx_glbl_pram->l3qt[6]));
+               pr_info("l3qt[7]         : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->l3qt[7],
+                       in_be32(&ugeth->p_rx_glbl_pram->l3qt[7]));
+               pr_info("vlantype        : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->vlantype,
+                       in_be16(&ugeth->p_rx_glbl_pram->vlantype));
+               pr_info("vlantci         : addr - 0x%08x, val - 0x%04x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->vlantci,
+                       in_be16(&ugeth->p_rx_glbl_pram->vlantci));
                for (i = 0; i < 64; i++)
-                       ugeth_info
-                   ("addressfiltering[%d]: addr - 0x%08x, val - 0x%02x",
-                            i,
-                            (u32) & ugeth->p_rx_glbl_pram->addressfiltering[i],
-                            ugeth->p_rx_glbl_pram->addressfiltering[i]);
-               ugeth_info("exfGlobalParam  : addr - 0x%08x, val - 0x%08x",
-                          (u32) & ugeth->p_rx_glbl_pram->exfGlobalParam,
-                          in_be32(&ugeth->p_rx_glbl_pram->exfGlobalParam));
+                       pr_info("addressfiltering[%d]: addr - 0x%08x, val - 0x%02x\n",
+                               i,
+                               (u32)&ugeth->p_rx_glbl_pram->addressfiltering[i],
+                               ugeth->p_rx_glbl_pram->addressfiltering[i]);
+               pr_info("exfGlobalParam  : addr - 0x%08x, val - 0x%08x\n",
+                       (u32)&ugeth->p_rx_glbl_pram->exfGlobalParam,
+                       in_be32(&ugeth->p_rx_glbl_pram->exfGlobalParam));
        }
        if (ugeth->p_send_q_mem_reg) {
-               ugeth_info("Send Q memory registers:");
-               ugeth_info("Base address: 0x%08x",
-                          (u32) ugeth->p_send_q_mem_reg);
+               pr_info("Send Q memory registers:\n");
+               pr_info("Base address: 0x%08x\n", (u32)ugeth->p_send_q_mem_reg);
                for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) {
-                       ugeth_info("SQQD[%d]:", i);
-                       ugeth_info("Base address: 0x%08x",
-                                  (u32) & ugeth->p_send_q_mem_reg->sqqd[i]);
+                       pr_info("SQQD[%d]:\n", i);
+                       pr_info("Base address: 0x%08x\n",
+                               (u32)&ugeth->p_send_q_mem_reg->sqqd[i]);
                        mem_disp((u8 *) & ugeth->p_send_q_mem_reg->sqqd[i],
                                 sizeof(struct ucc_geth_send_queue_qd));
                }
        }
        if (ugeth->p_scheduler) {
-               ugeth_info("Scheduler:");
-               ugeth_info("Base address: 0x%08x", (u32) ugeth->p_scheduler);
+               pr_info("Scheduler:\n");
+               pr_info("Base address: 0x%08x\n", (u32)ugeth->p_scheduler);
                mem_disp((u8 *) ugeth->p_scheduler,
                         sizeof(*ugeth->p_scheduler));
        }
        if (ugeth->p_tx_fw_statistics_pram) {
-               ugeth_info("TX FW statistics pram:");
-               ugeth_info("Base address: 0x%08x",
-                          (u32) ugeth->p_tx_fw_statistics_pram);
+               pr_info("TX FW statistics pram:\n");
+               pr_info("Base address: 0x%08x\n",
+                       (u32)ugeth->p_tx_fw_statistics_pram);
                mem_disp((u8 *) ugeth->p_tx_fw_statistics_pram,
                         sizeof(*ugeth->p_tx_fw_statistics_pram));
        }
        if (ugeth->p_rx_fw_statistics_pram) {
-               ugeth_info("RX FW statistics pram:");
-               ugeth_info("Base address: 0x%08x",
-                          (u32) ugeth->p_rx_fw_statistics_pram);
+               pr_info("RX FW statistics pram:\n");
+               pr_info("Base address: 0x%08x\n",
+                       (u32)ugeth->p_rx_fw_statistics_pram);
                mem_disp((u8 *) ugeth->p_rx_fw_statistics_pram,
                         sizeof(*ugeth->p_rx_fw_statistics_pram));
        }
        if (ugeth->p_rx_irq_coalescing_tbl) {
-               ugeth_info("RX IRQ coalescing tables:");
-               ugeth_info("Base address: 0x%08x",
-                          (u32) ugeth->p_rx_irq_coalescing_tbl);
+               pr_info("RX IRQ coalescing tables:\n");
+               pr_info("Base address: 0x%08x\n",
+                       (u32)ugeth->p_rx_irq_coalescing_tbl);
                for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) {
-                       ugeth_info("RX IRQ coalescing table entry[%d]:", i);
-                       ugeth_info("Base address: 0x%08x",
-                                  (u32) & ugeth->p_rx_irq_coalescing_tbl->
-                                  coalescingentry[i]);
-                       ugeth_info
-               ("interruptcoalescingmaxvalue: addr - 0x%08x, val - 0x%08x",
-                            (u32) & ugeth->p_rx_irq_coalescing_tbl->
-                            coalescingentry[i].interruptcoalescingmaxvalue,
-                            in_be32(&ugeth->p_rx_irq_coalescing_tbl->
-                                    coalescingentry[i].
-                                    interruptcoalescingmaxvalue));
-                       ugeth_info
-               ("interruptcoalescingcounter : addr - 0x%08x, val - 0x%08x",
-                            (u32) & ugeth->p_rx_irq_coalescing_tbl->
-                            coalescingentry[i].interruptcoalescingcounter,
-                            in_be32(&ugeth->p_rx_irq_coalescing_tbl->
-                                    coalescingentry[i].
-                                    interruptcoalescingcounter));
+                       pr_info("RX IRQ coalescing table entry[%d]:\n", i);
+                       pr_info("Base address: 0x%08x\n",
+                               (u32)&ugeth->p_rx_irq_coalescing_tbl->
+                               coalescingentry[i]);
+                       pr_info("interruptcoalescingmaxvalue: addr - 0x%08x, val - 0x%08x\n",
+                               (u32)&ugeth->p_rx_irq_coalescing_tbl->
+                               coalescingentry[i].interruptcoalescingmaxvalue,
+                               in_be32(&ugeth->p_rx_irq_coalescing_tbl->
+                                       coalescingentry[i].
+                                       interruptcoalescingmaxvalue));
+                       pr_info("interruptcoalescingcounter : addr - 0x%08x, val - 0x%08x\n",
+                               (u32)&ugeth->p_rx_irq_coalescing_tbl->
+                               coalescingentry[i].interruptcoalescingcounter,
+                               in_be32(&ugeth->p_rx_irq_coalescing_tbl->
+                                       coalescingentry[i].
+                                       interruptcoalescingcounter));
                }
        }
        if (ugeth->p_rx_bd_qs_tbl) {
-               ugeth_info("RX BD QS tables:");
-               ugeth_info("Base address: 0x%08x", (u32) ugeth->p_rx_bd_qs_tbl);
+               pr_info("RX BD QS tables:\n");
+               pr_info("Base address: 0x%08x\n", (u32)ugeth->p_rx_bd_qs_tbl);
                for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) {
-                       ugeth_info("RX BD QS table[%d]:", i);
-                       ugeth_info("Base address: 0x%08x",
-                                  (u32) & ugeth->p_rx_bd_qs_tbl[i]);
-                       ugeth_info
-                           ("bdbaseptr        : addr - 0x%08x, val - 0x%08x",
-                            (u32) & ugeth->p_rx_bd_qs_tbl[i].bdbaseptr,
-                            in_be32(&ugeth->p_rx_bd_qs_tbl[i].bdbaseptr));
-                       ugeth_info
-                           ("bdptr            : addr - 0x%08x, val - 0x%08x",
-                            (u32) & ugeth->p_rx_bd_qs_tbl[i].bdptr,
-                            in_be32(&ugeth->p_rx_bd_qs_tbl[i].bdptr));
-                       ugeth_info
-                           ("externalbdbaseptr: addr - 0x%08x, val - 0x%08x",
-                            (u32) & ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr,
-                            in_be32(&ugeth->p_rx_bd_qs_tbl[i].
-                                    externalbdbaseptr));
-                       ugeth_info
-                           ("externalbdptr    : addr - 0x%08x, val - 0x%08x",
-                            (u32) & ugeth->p_rx_bd_qs_tbl[i].externalbdptr,
-                            in_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdptr));
-                       ugeth_info("ucode RX Prefetched BDs:");
-                       ugeth_info("Base address: 0x%08x",
-                                  (u32)
-                                  qe_muram_addr(in_be32
-                                                (&ugeth->p_rx_bd_qs_tbl[i].
-                                                 bdbaseptr)));
+                       pr_info("RX BD QS table[%d]:\n", i);
+                       pr_info("Base address: 0x%08x\n",
+                               (u32)&ugeth->p_rx_bd_qs_tbl[i]);
+                       pr_info("bdbaseptr        : addr - 0x%08x, val - 0x%08x\n",
+                               (u32)&ugeth->p_rx_bd_qs_tbl[i].bdbaseptr,
+                               in_be32(&ugeth->p_rx_bd_qs_tbl[i].bdbaseptr));
+                       pr_info("bdptr            : addr - 0x%08x, val - 0x%08x\n",
+                               (u32)&ugeth->p_rx_bd_qs_tbl[i].bdptr,
+                               in_be32(&ugeth->p_rx_bd_qs_tbl[i].bdptr));
+                       pr_info("externalbdbaseptr: addr - 0x%08x, val - 0x%08x\n",
+                               (u32)&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr,
+                               in_be32(&ugeth->p_rx_bd_qs_tbl[i].
+                                       externalbdbaseptr));
+                       pr_info("externalbdptr    : addr - 0x%08x, val - 0x%08x\n",
+                               (u32)&ugeth->p_rx_bd_qs_tbl[i].externalbdptr,
+                               in_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdptr));
+                       pr_info("ucode RX Prefetched BDs:\n");
+                       pr_info("Base address: 0x%08x\n",
+                               (u32)qe_muram_addr(in_be32
+                                                  (&ugeth->p_rx_bd_qs_tbl[i].
+                                                   bdbaseptr)));
                        mem_disp((u8 *)
                                 qe_muram_addr(in_be32
                                               (&ugeth->p_rx_bd_qs_tbl[i].
@@ -1010,9 +996,9 @@ static void dump_regs(struct ucc_geth_private *ugeth)
        }
        if (ugeth->p_init_enet_param_shadow) {
                int size;
-               ugeth_info("Init enet param shadow:");
-               ugeth_info("Base address: 0x%08x",
-                          (u32) ugeth->p_init_enet_param_shadow);
+               pr_info("Init enet param shadow:\n");
+               pr_info("Base address: 0x%08x\n",
+                       (u32) ugeth->p_init_enet_param_shadow);
                mem_disp((u8 *) ugeth->p_init_enet_param_shadow,
                         sizeof(*ugeth->p_init_enet_param_shadow));
 
@@ -1392,12 +1378,11 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth)
                struct phy_device *tbiphy;
 
                if (!ug_info->tbi_node)
-                       ugeth_warn("TBI mode requires that the device "
-                               "tree specify a tbi-handle\n");
+                       pr_warn("TBI mode requires that the device tree specify a tbi-handle\n");
 
                tbiphy = of_phy_find_device(ug_info->tbi_node);
                if (!tbiphy)
-                       ugeth_warn("Could not get TBI device\n");
+                       pr_warn("Could not get TBI device\n");
 
                value = phy_read(tbiphy, ENET_TBI_MII_CR);
                value &= ~0x1000;       /* Turn off autonegotiation */
@@ -1409,8 +1394,7 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth)
        ret_val = init_preamble_length(ug_info->prel, &ug_regs->maccfg2);
        if (ret_val != 0) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: Preamble length must be between 3 and 7 inclusive.",
-                            __func__);
+                       pr_err("Preamble length must be between 3 and 7 inclusive\n");
                return ret_val;
        }
 
@@ -1520,7 +1504,7 @@ static int ugeth_enable(struct ucc_geth_private *ugeth, enum comm_dir mode)
        /* check if the UCC number is in range. */
        if (ugeth->ug_info->uf_info.ucc_num >= UCC_MAX_NUM) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: ucc_num out of range.", __func__);
+                       pr_err("ucc_num out of range\n");
                return -EINVAL;
        }
 
@@ -1549,7 +1533,7 @@ static int ugeth_disable(struct ucc_geth_private *ugeth, enum comm_dir mode)
        /* check if the UCC number is in range. */
        if (ugeth->ug_info->uf_info.ucc_num >= UCC_MAX_NUM) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: ucc_num out of range.", __func__);
+                       pr_err("ucc_num out of range\n");
                return -EINVAL;
        }
 
@@ -1648,7 +1632,7 @@ static void adjust_link(struct net_device *dev)
                                break;
                        default:
                                if (netif_msg_link(ugeth))
-                                       ugeth_warn(
+                                       pr_warn(
                                                "%s: Ack!  Speed (%d) is not 10/100/1000!",
                                                dev->name, phydev->speed);
                                break;
@@ -2103,8 +2087,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        if (!((uf_info->bd_mem_part == MEM_PART_SYSTEM) ||
              (uf_info->bd_mem_part == MEM_PART_MURAM))) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: Bad memory partition value.",
-                                       __func__);
+                       pr_err("Bad memory partition value\n");
                return -EINVAL;
        }
 
@@ -2114,9 +2097,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
                    (ug_info->bdRingLenRx[i] %
                     UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT)) {
                        if (netif_msg_probe(ugeth))
-                               ugeth_err
-                                   ("%s: Rx BD ring length must be multiple of 4, no smaller than 8.",
-                                       __func__);
+                               pr_err("Rx BD ring length must be multiple of 4, no smaller than 8\n");
                        return -EINVAL;
                }
        }
@@ -2125,9 +2106,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        for (i = 0; i < ug_info->numQueuesTx; i++) {
                if (ug_info->bdRingLenTx[i] < UCC_GETH_TX_BD_RING_SIZE_MIN) {
                        if (netif_msg_probe(ugeth))
-                               ugeth_err
-                                   ("%s: Tx BD ring length must be no smaller than 2.",
-                                    __func__);
+                               pr_err("Tx BD ring length must be no smaller than 2\n");
                        return -EINVAL;
                }
        }
@@ -2136,23 +2115,21 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        if ((uf_info->max_rx_buf_length == 0) ||
            (uf_info->max_rx_buf_length % UCC_GETH_MRBLR_ALIGNMENT)) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err
-                           ("%s: max_rx_buf_length must be non-zero multiple of 128.",
-                            __func__);
+                       pr_err("max_rx_buf_length must be non-zero multiple of 128\n");
                return -EINVAL;
        }
 
        /* num Tx queues */
        if (ug_info->numQueuesTx > NUM_TX_QUEUES) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: number of tx queues too large.", __func__);
+                       pr_err("number of tx queues too large\n");
                return -EINVAL;
        }
 
        /* num Rx queues */
        if (ug_info->numQueuesRx > NUM_RX_QUEUES) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: number of rx queues too large.", __func__);
+                       pr_err("number of rx queues too large\n");
                return -EINVAL;
        }
 
@@ -2160,10 +2137,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        for (i = 0; i < UCC_GETH_VLAN_PRIORITY_MAX; i++) {
                if (ug_info->l2qt[i] >= ug_info->numQueuesRx) {
                        if (netif_msg_probe(ugeth))
-                               ugeth_err
-                                   ("%s: VLAN priority table entry must not be"
-                                       " larger than number of Rx queues.",
-                                    __func__);
+                               pr_err("VLAN priority table entry must not be larger than number of Rx queues\n");
                        return -EINVAL;
                }
        }
@@ -2172,18 +2146,14 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        for (i = 0; i < UCC_GETH_IP_PRIORITY_MAX; i++) {
                if (ug_info->l3qt[i] >= ug_info->numQueuesRx) {
                        if (netif_msg_probe(ugeth))
-                               ugeth_err
-                                   ("%s: IP priority table entry must not be"
-                                       " larger than number of Rx queues.",
-                                    __func__);
+                               pr_err("IP priority table entry must not be larger than number of Rx queues\n");
                        return -EINVAL;
                }
        }
 
        if (ug_info->cam && !ug_info->ecamptr) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: If cam mode is chosen, must supply cam ptr.",
-                                 __func__);
+                       pr_err("If cam mode is chosen, must supply cam ptr\n");
                return -EINVAL;
        }
 
@@ -2191,9 +2161,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
             UCC_GETH_NUM_OF_STATION_ADDRESSES_1) &&
            ug_info->rxExtendedFiltering) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: Number of station addresses greater than 1 "
-                                 "not allowed in extended parsing mode.",
-                                 __func__);
+                       pr_err("Number of station addresses greater than 1 not allowed in extended parsing mode\n");
                return -EINVAL;
        }
 
@@ -2207,7 +2175,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        /* Initialize the general fast UCC block. */
        if (ucc_fast_init(uf_info, &ugeth->uccf)) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: Failed to init uccf.", __func__);
+                       pr_err("Failed to init uccf\n");
                return -ENOMEM;
        }
 
@@ -2222,7 +2190,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        ugeth->ug_regs = ioremap(uf_info->regs, sizeof(*ugeth->ug_regs));
        if (!ugeth->ug_regs) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: Failed to ioremap regs.", __func__);
+                       pr_err("Failed to ioremap regs\n");
                return -ENOMEM;
        }
 
@@ -2273,9 +2241,7 @@ static int ucc_geth_alloc_tx(struct ucc_geth_private *ugeth)
                }
                if (!ugeth->p_tx_bd_ring[j]) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err
-                                   ("%s: Can not allocate memory for Tx bd rings.",
-                                    __func__);
+                               pr_err("Can not allocate memory for Tx bd rings\n");
                        return -ENOMEM;
                }
                /* Zero unused end of bd ring, according to spec */
@@ -2293,8 +2259,7 @@ static int ucc_geth_alloc_tx(struct ucc_geth_private *ugeth)
 
                if (ugeth->tx_skbuff[j] == NULL) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err("%s: Could not allocate tx_skbuff",
-                                         __func__);
+                               pr_err("Could not allocate tx_skbuff\n");
                        return -ENOMEM;
                }
 
@@ -2353,9 +2318,7 @@ static int ucc_geth_alloc_rx(struct ucc_geth_private *ugeth)
                }
                if (!ugeth->p_rx_bd_ring[j]) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err
-                                   ("%s: Can not allocate memory for Rx bd rings.",
-                                    __func__);
+                               pr_err("Can not allocate memory for Rx bd rings\n");
                        return -ENOMEM;
                }
        }
@@ -2369,8 +2332,7 @@ static int ucc_geth_alloc_rx(struct ucc_geth_private *ugeth)
 
                if (ugeth->rx_skbuff[j] == NULL) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err("%s: Could not allocate rx_skbuff",
-                                         __func__);
+                               pr_err("Could not allocate rx_skbuff\n");
                        return -ENOMEM;
                }
 
@@ -2438,8 +2400,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                break;
        default:
                if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Bad number of Rx threads value.",
-                                       __func__);
+                       pr_err("Bad number of Rx threads value\n");
                return -EINVAL;
                break;
        }
@@ -2462,8 +2423,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                break;
        default:
                if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Bad number of Tx threads value.",
-                                       __func__);
+                       pr_err("Bad number of Tx threads value\n");
                return -EINVAL;
                break;
        }
@@ -2512,8 +2472,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                                              &ug_regs->ipgifg);
        if (ret_val != 0) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: IPGIFG initialization parameter too large.",
-                                 __func__);
+                       pr_err("IPGIFG initialization parameter too large\n");
                return ret_val;
        }
 
@@ -2529,8 +2488,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                                          &ug_regs->hafdup);
        if (ret_val != 0) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Half Duplex initialization parameter too large.",
-                         __func__);
+                       pr_err("Half Duplex initialization parameter too large\n");
                return ret_val;
        }
 
@@ -2567,9 +2525,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                           UCC_GETH_TX_GLOBAL_PRAM_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->tx_glbl_pram_offset)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate DPRAM memory for p_tx_glbl_pram.",
-                            __func__);
+                       pr_err("Can not allocate DPRAM memory for p_tx_glbl_pram\n");
                return -ENOMEM;
        }
        ugeth->p_tx_glbl_pram =
@@ -2589,9 +2545,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                           UCC_GETH_THREAD_DATA_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->thread_dat_tx_offset)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate DPRAM memory for p_thread_data_tx.",
-                            __func__);
+                       pr_err("Can not allocate DPRAM memory for p_thread_data_tx\n");
                return -ENOMEM;
        }
 
@@ -2618,9 +2572,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                           UCC_GETH_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->send_q_mem_reg_offset)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate DPRAM memory for p_send_q_mem_reg.",
-                            __func__);
+                       pr_err("Can not allocate DPRAM memory for p_send_q_mem_reg\n");
                return -ENOMEM;
        }
 
@@ -2661,9 +2613,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                                   UCC_GETH_SCHEDULER_ALIGNMENT);
                if (IS_ERR_VALUE(ugeth->scheduler_offset)) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err
-                                ("%s: Can not allocate DPRAM memory for p_scheduler.",
-                                    __func__);
+                               pr_err("Can not allocate DPRAM memory for p_scheduler\n");
                        return -ENOMEM;
                }
 
@@ -2710,10 +2660,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                                   UCC_GETH_TX_STATISTICS_ALIGNMENT);
                if (IS_ERR_VALUE(ugeth->tx_fw_statistics_pram_offset)) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err
-                                   ("%s: Can not allocate DPRAM memory for"
-                                       " p_tx_fw_statistics_pram.",
-                                       __func__);
+                               pr_err("Can not allocate DPRAM memory for p_tx_fw_statistics_pram\n");
                        return -ENOMEM;
                }
                ugeth->p_tx_fw_statistics_pram =
@@ -2750,9 +2697,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                           UCC_GETH_RX_GLOBAL_PRAM_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->rx_glbl_pram_offset)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate DPRAM memory for p_rx_glbl_pram.",
-                            __func__);
+                       pr_err("Can not allocate DPRAM memory for p_rx_glbl_pram\n");
                return -ENOMEM;
        }
        ugeth->p_rx_glbl_pram =
@@ -2771,9 +2716,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                           UCC_GETH_THREAD_DATA_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->thread_dat_rx_offset)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate DPRAM memory for p_thread_data_rx.",
-                            __func__);
+                       pr_err("Can not allocate DPRAM memory for p_thread_data_rx\n");
                return -ENOMEM;
        }
 
@@ -2794,9 +2737,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                                   UCC_GETH_RX_STATISTICS_ALIGNMENT);
                if (IS_ERR_VALUE(ugeth->rx_fw_statistics_pram_offset)) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err
-                                       ("%s: Can not allocate DPRAM memory for"
-                                       " p_rx_fw_statistics_pram.", __func__);
+                               pr_err("Can not allocate DPRAM memory for p_rx_fw_statistics_pram\n");
                        return -ENOMEM;
                }
                ugeth->p_rx_fw_statistics_pram =
@@ -2816,9 +2757,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                           + 4, UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->rx_irq_coalescing_tbl_offset)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate DPRAM memory for"
-                               " p_rx_irq_coalescing_tbl.", __func__);
+                       pr_err("Can not allocate DPRAM memory for p_rx_irq_coalescing_tbl\n");
                return -ENOMEM;
        }
 
@@ -2884,9 +2823,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                           UCC_GETH_RX_BD_QUEUES_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->rx_bd_qs_tbl_offset)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate DPRAM memory for p_rx_bd_qs_tbl.",
-                            __func__);
+                       pr_err("Can not allocate DPRAM memory for p_rx_bd_qs_tbl\n");
                return -ENOMEM;
        }
 
@@ -2961,8 +2898,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        if (ug_info->rxExtendedFiltering) {
                if (!ug_info->extendedFilteringChainPointer) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err("%s: Null Extended Filtering Chain Pointer.",
-                                         __func__);
+                               pr_err("Null Extended Filtering Chain Pointer\n");
                        return -EINVAL;
                }
 
@@ -2973,9 +2909,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                UCC_GETH_RX_EXTENDED_FILTERING_GLOBAL_PARAMETERS_ALIGNMENT);
                if (IS_ERR_VALUE(ugeth->exf_glbl_param_offset)) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err
-                                       ("%s: Can not allocate DPRAM memory for"
-                                       " p_exf_glbl_param.", __func__);
+                               pr_err("Can not allocate DPRAM memory for p_exf_glbl_param\n");
                        return -ENOMEM;
                }
 
@@ -3020,9 +2954,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        if (!(ugeth->p_init_enet_param_shadow =
              kmalloc(sizeof(struct ucc_geth_init_pram), GFP_KERNEL))) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate memory for"
-                               " p_UccInitEnetParamShadows.", __func__);
+                       pr_err("Can not allocate memory for p_UccInitEnetParamShadows\n");
                return -ENOMEM;
        }
        /* Zero out *p_init_enet_param_shadow */
@@ -3055,8 +2987,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
            (ug_info->largestexternallookupkeysize !=
             QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Invalid largest External Lookup Key Size.",
-                                 __func__);
+                       pr_err("Invalid largest External Lookup Key Size\n");
                return -EINVAL;
        }
        ugeth->p_init_enet_param_shadow->largestexternallookupkeysize =
@@ -3081,8 +3012,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                , size, UCC_GETH_THREAD_RX_PRAM_ALIGNMENT,
                ug_info->riscRx, 1)) != 0) {
                if (netif_msg_ifup(ugeth))
-                               ugeth_err("%s: Can not fill p_init_enet_param_shadow.",
-                                       __func__);
+                       pr_err("Can not fill p_init_enet_param_shadow\n");
                return ret_val;
        }
 
@@ -3096,8 +3026,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                                    UCC_GETH_THREAD_TX_PRAM_ALIGNMENT,
                                    ug_info->riscTx, 0)) != 0) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Can not fill p_init_enet_param_shadow.",
-                                 __func__);
+                       pr_err("Can not fill p_init_enet_param_shadow\n");
                return ret_val;
        }
 
@@ -3105,8 +3034,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        for (i = 0; i < ug_info->numQueuesRx; i++) {
                if ((ret_val = rx_bd_buffer_set(ugeth, (u8) i)) != 0) {
                        if (netif_msg_ifup(ugeth))
-                               ugeth_err("%s: Can not fill Rx bds with buffers.",
-                                         __func__);
+                               pr_err("Can not fill Rx bds with buffers\n");
                        return ret_val;
                }
        }
@@ -3115,9 +3043,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        init_enet_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_init_pram), 4);
        if (IS_ERR_VALUE(init_enet_pram_offset)) {
                if (netif_msg_ifup(ugeth))
-                       ugeth_err
-                           ("%s: Can not allocate DPRAM memory for p_init_enet_pram.",
-                            __func__);
+                       pr_err("Can not allocate DPRAM memory for p_init_enet_pram\n");
                return -ENOMEM;
        }
        p_init_enet_pram =
@@ -3266,8 +3192,8 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit
                    (!(bd_status & (R_F | R_L))) ||
                    (bd_status & R_ERRORS_FATAL)) {
                        if (netif_msg_rx_err(ugeth))
-                               ugeth_err("%s, %d: ERROR!!! skb - 0x%08x",
-                                          __func__, __LINE__, (u32) skb);
+                               pr_err("%d: ERROR!!! skb - 0x%08x\n",
+                                      __LINE__, (u32)skb);
                        dev_kfree_skb(skb);
 
                        ugeth->rx_skbuff[rxQ][ugeth->skb_currx[rxQ]] = NULL;
@@ -3290,7 +3216,7 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit
                skb = get_new_skb(ugeth, bd);
                if (!skb) {
                        if (netif_msg_rx_err(ugeth))
-                               ugeth_warn("%s: No Rx Data Buffer", __func__);
+                               pr_warn("No Rx Data Buffer\n");
                        dev->stats.rx_dropped++;
                        break;
                }
@@ -3481,25 +3407,19 @@ static int ucc_geth_init_mac(struct ucc_geth_private *ugeth)
 
        err = ucc_struct_init(ugeth);
        if (err) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Cannot configure internal struct, "
-                                 "aborting.", dev->name);
+               netif_err(ugeth, ifup, dev, "Cannot configure internal struct, aborting\n");
                goto err;
        }
 
        err = ucc_geth_startup(ugeth);
        if (err) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Cannot configure net device, aborting.",
-                                 dev->name);
+               netif_err(ugeth, ifup, dev, "Cannot configure net device, aborting\n");
                goto err;
        }
 
        err = adjust_enet_interface(ugeth);
        if (err) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Cannot configure net device, aborting.",
-                                 dev->name);
+               netif_err(ugeth, ifup, dev, "Cannot configure net device, aborting\n");
                goto err;
        }
 
@@ -3516,8 +3436,7 @@ static int ucc_geth_init_mac(struct ucc_geth_private *ugeth)
 
        err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
        if (err) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Cannot enable net device, aborting.", dev->name);
+               netif_err(ugeth, ifup, dev, "Cannot enable net device, aborting\n");
                goto err;
        }
 
@@ -3538,35 +3457,27 @@ static int ucc_geth_open(struct net_device *dev)
 
        /* Test station address */
        if (dev->dev_addr[0] & ENET_GROUP_ADDR) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Multicast address used for station "
-                                 "address - is this what you wanted?",
-                                 __func__);
+               netif_err(ugeth, ifup, dev,
+                         "Multicast address used for station address - is this what you wanted?\n");
                return -EINVAL;
        }
 
        err = init_phy(dev);
        if (err) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Cannot initialize PHY, aborting.",
-                                 dev->name);
+               netif_err(ugeth, ifup, dev, "Cannot initialize PHY, aborting\n");
                return err;
        }
 
        err = ucc_geth_init_mac(ugeth);
        if (err) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Cannot initialize MAC, aborting.",
-                                 dev->name);
+               netif_err(ugeth, ifup, dev, "Cannot initialize MAC, aborting\n");
                goto err;
        }
 
        err = request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler,
                          0, "UCC Geth", dev);
        if (err) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Cannot get IRQ for net device, aborting.",
-                                 dev->name);
+               netif_err(ugeth, ifup, dev, "Cannot get IRQ for net device, aborting\n");
                goto err;
        }
 
@@ -3704,8 +3615,7 @@ static int ucc_geth_resume(struct platform_device *ofdev)
 
                err = ucc_geth_init_mac(ugeth);
                if (err) {
-                       ugeth_err("%s: Cannot initialize MAC, aborting.",
-                                 ndev->name);
+                       netdev_err(ndev, "Cannot initialize MAC, aborting\n");
                        return err;
                }
        }
@@ -3825,8 +3735,7 @@ static int ucc_geth_probe(struct platform_device* ofdev)
        ug_info = &ugeth_info[ucc_num];
        if (ug_info == NULL) {
                if (netif_msg_probe(&debug))
-                       ugeth_err("%s: [%d] Missing additional data!",
-                                       __func__, ucc_num);
+                       pr_err("[%d] Missing additional data!\n", ucc_num);
                return -ENODEV;
        }
 
@@ -3837,8 +3746,7 @@ static int ucc_geth_probe(struct platform_device* ofdev)
                ug_info->uf_info.rx_clock = qe_clock_source(sprop);
                if ((ug_info->uf_info.rx_clock < QE_CLK_NONE) ||
                    (ug_info->uf_info.rx_clock > QE_CLK24)) {
-                       printk(KERN_ERR
-                               "ucc_geth: invalid rx-clock-name property\n");
+                       pr_err("invalid rx-clock-name property\n");
                        return -EINVAL;
                }
        } else {
@@ -3846,13 +3754,11 @@ static int ucc_geth_probe(struct platform_device* ofdev)
                if (!prop) {
                        /* If both rx-clock-name and rx-clock are missing,
                           we want to tell people to use rx-clock-name. */
-                       printk(KERN_ERR
-                               "ucc_geth: missing rx-clock-name property\n");
+                       pr_err("missing rx-clock-name property\n");
                        return -EINVAL;
                }
                if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) {
-                       printk(KERN_ERR
-                               "ucc_geth: invalid rx-clock propperty\n");
+                       pr_err("invalid rx-clock propperty\n");
                        return -EINVAL;
                }
                ug_info->uf_info.rx_clock = *prop;
@@ -3863,20 +3769,17 @@ static int ucc_geth_probe(struct platform_device* ofdev)
                ug_info->uf_info.tx_clock = qe_clock_source(sprop);
                if ((ug_info->uf_info.tx_clock < QE_CLK_NONE) ||
                    (ug_info->uf_info.tx_clock > QE_CLK24)) {
-                       printk(KERN_ERR
-                               "ucc_geth: invalid tx-clock-name property\n");
+                       pr_err("invalid tx-clock-name property\n");
                        return -EINVAL;
                }
        } else {
                prop = of_get_property(np, "tx-clock", NULL);
                if (!prop) {
-                       printk(KERN_ERR
-                               "ucc_geth: missing tx-clock-name property\n");
+                       pr_err("missing tx-clock-name property\n");
                        return -EINVAL;
                }
                if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) {
-                       printk(KERN_ERR
-                               "ucc_geth: invalid tx-clock property\n");
+                       pr_err("invalid tx-clock property\n");
                        return -EINVAL;
                }
                ug_info->uf_info.tx_clock = *prop;
@@ -3949,7 +3852,7 @@ static int ucc_geth_probe(struct platform_device* ofdev)
        }
 
        if (netif_msg_probe(&debug))
-               printk(KERN_INFO "ucc_geth: UCC%1d at 0x%8x (irq = %d)\n",
+               pr_info("UCC%1d at 0x%8x (irq = %d)\n",
                        ug_info->uf_info.ucc_num + 1, ug_info->uf_info.regs,
                        ug_info->uf_info.irq);
 
@@ -3988,8 +3891,8 @@ static int ucc_geth_probe(struct platform_device* ofdev)
        err = register_netdev(dev);
        if (err) {
                if (netif_msg_probe(ugeth))
-                       ugeth_err("%s: Cannot register net device, aborting.",
-                                 dev->name);
+                       pr_err("%s: Cannot register net device, aborting\n",
+                              dev->name);
                free_netdev(dev);
                return err;
        }
@@ -4047,7 +3950,7 @@ static int __init ucc_geth_init(void)
        int i, ret;
 
        if (netif_msg_drv(&debug))
-               printk(KERN_INFO "ucc_geth: " DRV_DESC "\n");
+               pr_info(DRV_DESC "\n");
        for (i = 0; i < 8; i++)
                memcpy(&(ugeth_info[i]), &ugeth_primary_info,
                       sizeof(ugeth_primary_info));
index 1ebf7128ec04da37a2236f41848bdad8b10162de..e79aaf9ae52a034207e04baf72be162bb553b0ff 100644 (file)
@@ -38,7 +38,7 @@
 
 #include "ucc_geth.h"
 
-static char hw_stat_gstrings[][ETH_GSTRING_LEN] = {
+static const char hw_stat_gstrings[][ETH_GSTRING_LEN] = {
        "tx-64-frames",
        "tx-65-127-frames",
        "tx-128-255-frames",
@@ -59,7 +59,7 @@ static char hw_stat_gstrings[][ETH_GSTRING_LEN] = {
        "rx-dropped-frames",
 };
 
-static char tx_fw_stat_gstrings[][ETH_GSTRING_LEN] = {
+static const char tx_fw_stat_gstrings[][ETH_GSTRING_LEN] = {
        "tx-single-collision",
        "tx-multiple-collision",
        "tx-late-collsion",
@@ -74,7 +74,7 @@ static char tx_fw_stat_gstrings[][ETH_GSTRING_LEN] = {
        "tx-jumbo-frames",
 };
 
-static char rx_fw_stat_gstrings[][ETH_GSTRING_LEN] = {
+static const char rx_fw_stat_gstrings[][ETH_GSTRING_LEN] = {
        "rx-crc-errors",
        "rx-alignment-errors",
        "rx-in-range-length-errors",
@@ -160,8 +160,7 @@ uec_set_pauseparam(struct net_device *netdev,
        if (ugeth->phydev->autoneg) {
                if (netif_running(netdev)) {
                        /* FIXME: automatically restart */
-                       printk(KERN_INFO
-                               "Please re-open the interface.\n");
+                       netdev_info(netdev, "Please re-open the interface\n");
                }
        } else {
                struct ucc_geth_info *ug_info = ugeth->ug_info;
@@ -240,18 +239,18 @@ uec_set_ringparam(struct net_device *netdev,
        int queue = 0, ret = 0;
 
        if (ring->rx_pending < UCC_GETH_RX_BD_RING_SIZE_MIN) {
-               printk("%s: RxBD ring size must be no smaller than %d.\n",
-                               netdev->name, UCC_GETH_RX_BD_RING_SIZE_MIN);
+               netdev_info(netdev, "RxBD ring size must be no smaller than %d\n",
+                           UCC_GETH_RX_BD_RING_SIZE_MIN);
                return -EINVAL;
        }
        if (ring->rx_pending % UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT) {
-               printk("%s: RxBD ring size must be multiple of %d.\n",
-                       netdev->name, UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT);
+               netdev_info(netdev, "RxBD ring size must be multiple of %d\n",
+                           UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT);
                return -EINVAL;
        }
        if (ring->tx_pending < UCC_GETH_TX_BD_RING_SIZE_MIN) {
-               printk("%s: TxBD ring size must be no smaller than %d.\n",
-                               netdev->name, UCC_GETH_TX_BD_RING_SIZE_MIN);
+               netdev_info(netdev, "TxBD ring size must be no smaller than %d\n",
+                           UCC_GETH_TX_BD_RING_SIZE_MIN);
                return -EINVAL;
        }
 
@@ -260,8 +259,7 @@ uec_set_ringparam(struct net_device *netdev,
 
        if (netif_running(netdev)) {
                /* FIXME: restart automatically */
-               printk(KERN_INFO
-                       "Please re-open the interface.\n");
+               netdev_info(netdev, "Please re-open the interface\n");
        }
 
        return ret;
index 2418faf2251aacc2e91877859ee185c95c251516..84125707f321335f3c3038988a41e37b10c01b0b 100644 (file)
@@ -1003,8 +1003,6 @@ static void fjn_rx(struct net_device *dev)
            }
            skb = netdev_alloc_skb(dev, pkt_len + 2);
            if (skb == NULL) {
-               netdev_notice(dev, "Memory squeeze, dropping packet (len %d)\n",
-                             pkt_len);
                outb(F_SKP_PKT, ioaddr + RX_SKIP);
                dev->stats.rx_dropped++;
                break;
index 1c54e229e3cc94ed8ff0eb630bd9acc70cb20a8d..e38816145395c9e2b08416934e0b10b6352b4507 100644 (file)
@@ -798,16 +798,14 @@ static inline int i596_rx(struct net_device *dev)
 #ifdef __mc68000__
                                cache_clear(virt_to_phys(newskb->data), PKT_BUF_SZ);
 #endif
-                       }
-                       else
+                       } else {
                                skb = netdev_alloc_skb(dev, pkt_len + 2);
+                       }
 memory_squeeze:
                        if (skb == NULL) {
                                /* XXX tulip.c can defer packets here!! */
-                               printk(KERN_WARNING "%s: i596_rx Memory squeeze, dropping packet.\n", dev->name);
                                dev->stats.rx_dropped++;
-                       }
-                       else {
+                       } else {
                                if (!rx_in_place) {
                                        /* 16 byte align the data fields */
                                        skb_reserve(skb, 2);
index f045ea4dc5144e60be6fd25b730e8fabc65f2871..d653bac4cfc4e2be49d1ab1ff9a1247eae5460aa 100644 (file)
@@ -715,14 +715,12 @@ static inline int i596_rx(struct net_device *dev)
                                rbd->v_data = newskb->data;
                                rbd->b_data = SWAP32(dma_addr);
                                DMA_WBACK_INV(dev, rbd, sizeof(struct i596_rbd));
-                       } else
+                       } else {
                                skb = netdev_alloc_skb_ip_align(dev, pkt_len);
+                       }
 memory_squeeze:
                        if (skb == NULL) {
                                /* XXX tulip.c can defer packets here!! */
-                               printk(KERN_ERR
-                                      "%s: i596_rx Memory squeeze, dropping packet.\n",
-                                      dev->name);
                                dev->stats.rx_dropped++;
                        } else {
                                if (!rx_in_place) {
index 328f47c92e26e2df74e9008f73127b5cd0cbc337..90ea0b1673ca45300f902f0f038f0a4a7061b745 100644 (file)
@@ -402,7 +402,6 @@ static void ehea_refill_rq1(struct ehea_port_res *pr, int index, int nr_of_wqes)
                        skb_arr_rq1[index] = netdev_alloc_skb(dev,
                                                              EHEA_L_PKT_SIZE);
                        if (!skb_arr_rq1[index]) {
-                               netdev_info(dev, "Unable to allocate enough skb in the array\n");
                                pr->rq1_skba.os_skbs = fill_wqes - i;
                                break;
                        }
@@ -432,10 +431,8 @@ static void ehea_init_fill_rq1(struct ehea_port_res *pr, int nr_rq1a)
 
        for (i = 0; i < nr_rq1a; i++) {
                skb_arr_rq1[i] = netdev_alloc_skb(dev, EHEA_L_PKT_SIZE);
-               if (!skb_arr_rq1[i]) {
-                       netdev_info(dev, "Not enough memory to allocate skb array\n");
+               if (!skb_arr_rq1[i])
                        break;
-               }
        }
        /* Ring doorbell */
        ehea_update_rq1a(pr->qp, i - 1);
@@ -695,10 +692,8 @@ static int ehea_proc_rwqes(struct net_device *dev,
 
                                        skb = netdev_alloc_skb(dev,
                                                               EHEA_L_PKT_SIZE);
-                                       if (!skb) {
-                                               netdev_err(dev, "Not enough memory to allocate skb\n");
+                                       if (!skb)
                                                break;
-                                       }
                                }
                                skb_copy_to_linear_data(skb, ((char *)cqe) + 64,
                                                 cqe->num_bytes_transfered - 4);
@@ -730,7 +725,8 @@ static int ehea_proc_rwqes(struct net_device *dev,
                        processed_bytes += skb->len;
 
                        if (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
-                               __vlan_hwaccel_put_tag(skb, cqe->vlan_tag);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+                                                      cqe->vlan_tag);
 
                        napi_gro_receive(&pr->napi, skb);
                } else {
@@ -2115,7 +2111,7 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
        return NETDEV_TX_OK;
 }
 
-static int ehea_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+static int ehea_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct ehea_port *port = netdev_priv(dev);
        struct ehea_adapter *adapter = port->adapter;
@@ -2153,7 +2149,7 @@ out:
        return err;
 }
 
-static int ehea_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+static int ehea_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct ehea_port *port = netdev_priv(dev);
        struct ehea_adapter *adapter = port->adapter;
@@ -3025,12 +3021,12 @@ static struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
        dev->netdev_ops = &ehea_netdev_ops;
        ehea_set_ethtool_ops(dev);
 
-       dev->hw_features = NETIF_F_SG | NETIF_F_TSO
-                     | NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_TX;
-       dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO
-                     | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_TX
-                     | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER
-                     | NETIF_F_RXCSUM;
+       dev->hw_features = NETIF_F_SG | NETIF_F_TSO |
+                     NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_CTAG_TX;
+       dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO |
+                     NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
+                     NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+                     NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXCSUM;
        dev->vlan_features = NETIF_F_SG | NETIF_F_TSO | NETIF_F_HIGHDMA |
                        NETIF_F_IP_CSUM;
        dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT;
index 1f7ecf57181e291d62f4567581169a5b52244ddd..610ed223d1dbd0e8c8136cdb5bb7bd46a433c8c2 100644 (file)
@@ -637,17 +637,12 @@ static int mal_probe(struct platform_device *ofdev)
        bd_size = sizeof(struct mal_descriptor) *
                (NUM_TX_BUFF * mal->num_tx_chans +
                 NUM_RX_BUFF * mal->num_rx_chans);
-       mal->bd_virt =
-               dma_alloc_coherent(&ofdev->dev, bd_size, &mal->bd_dma,
-                                  GFP_KERNEL);
+       mal->bd_virt = dma_alloc_coherent(&ofdev->dev, bd_size, &mal->bd_dma,
+                                         GFP_KERNEL | __GFP_ZERO);
        if (mal->bd_virt == NULL) {
-               printk(KERN_ERR
-                      "mal%d: out of memory allocating RX/TX descriptors!\n",
-                      index);
                err = -ENOMEM;
                goto fail_unmap;
        }
-       memset(mal->bd_virt, 0, bd_size);
 
        for (i = 0; i < mal->num_tx_chans; ++i)
                set_mal_dcrn(mal, MAL_TXCTPR(i), mal->bd_dma +
index c859771a9902e21e2f5e03a7c0901c06788e6172..302d594010652d829f5cc73d0b7b0df7ebaebf80 100644 (file)
@@ -556,11 +556,9 @@ static int ibmveth_open(struct net_device *netdev)
        adapter->rx_queue.queue_len = sizeof(struct ibmveth_rx_q_entry) *
                                                rxq_entries;
        adapter->rx_queue.queue_addr =
-           dma_alloc_coherent(dev, adapter->rx_queue.queue_len,
-                              &adapter->rx_queue.queue_dma, GFP_KERNEL);
-
+               dma_alloc_coherent(dev, adapter->rx_queue.queue_len,
+                                  &adapter->rx_queue.queue_dma, GFP_KERNEL);
        if (!adapter->rx_queue.queue_addr) {
-               netdev_err(netdev, "unable to allocate rx queue pages\n");
                rc = -ENOMEM;
                goto err_out;
        }
index ffd287196bf875560e40e5330c68e0a84b50e930..82a967c955982c62f2f8d6faba024ce59d9d882a 100644 (file)
@@ -1020,12 +1020,11 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
        txdr->size = txdr->count * sizeof(struct e1000_tx_desc);
        txdr->size = ALIGN(txdr->size, 4096);
        txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size, &txdr->dma,
-                                       GFP_KERNEL);
+                                       GFP_KERNEL | __GFP_ZERO);
        if (!txdr->desc) {
                ret_val = 2;
                goto err_nomem;
        }
-       memset(txdr->desc, 0, txdr->size);
        txdr->next_to_use = txdr->next_to_clean = 0;
 
        ew32(TDBAL, ((u64)txdr->dma & 0x00000000FFFFFFFF));
@@ -1079,12 +1078,11 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
 
        rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc);
        rxdr->desc = dma_alloc_coherent(&pdev->dev, rxdr->size, &rxdr->dma,
-                                       GFP_KERNEL);
+                                       GFP_KERNEL | __GFP_ZERO);
        if (!rxdr->desc) {
                ret_val = 6;
                goto err_nomem;
        }
-       memset(rxdr->desc, 0, rxdr->size);
        rxdr->next_to_use = rxdr->next_to_clean = 0;
 
        rctl = er32(RCTL);
index 8502c625dbef3d10dd28752929bd126f438fa89d..59ad007dd5aa09a6123cbd25db7c7b8f4e06f1b7 100644 (file)
@@ -166,8 +166,10 @@ static void e1000_vlan_mode(struct net_device *netdev,
                            netdev_features_t features);
 static void e1000_vlan_filter_on_off(struct e1000_adapter *adapter,
                                     bool filter_on);
-static int e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid);
-static int e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid);
+static int e1000_vlan_rx_add_vid(struct net_device *netdev,
+                                __be16 proto, u16 vid);
+static int e1000_vlan_rx_kill_vid(struct net_device *netdev,
+                                 __be16 proto, u16 vid);
 static void e1000_restore_vlan(struct e1000_adapter *adapter);
 
 #ifdef CONFIG_PM
@@ -333,7 +335,7 @@ static void e1000_update_mng_vlan(struct e1000_adapter *adapter)
        if (!test_bit(vid, adapter->active_vlans)) {
                if (hw->mng_cookie.status &
                    E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) {
-                       e1000_vlan_rx_add_vid(netdev, vid);
+                       e1000_vlan_rx_add_vid(netdev, htons(ETH_P_8021Q), vid);
                        adapter->mng_vlan_id = vid;
                } else {
                        adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
@@ -341,7 +343,8 @@ static void e1000_update_mng_vlan(struct e1000_adapter *adapter)
                if ((old_vid != (u16)E1000_MNG_VLAN_NONE) &&
                    (vid != old_vid) &&
                    !test_bit(old_vid, adapter->active_vlans))
-                       e1000_vlan_rx_kill_vid(netdev, old_vid);
+                       e1000_vlan_rx_kill_vid(netdev, htons(ETH_P_8021Q),
+                                              old_vid);
        } else {
                adapter->mng_vlan_id = vid;
        }
@@ -809,10 +812,10 @@ static netdev_features_t e1000_fix_features(struct net_device *netdev,
        /* Since there is no support for separate Rx/Tx vlan accel
         * enable/disable make sure Tx flag is always in same state as Rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -823,7 +826,7 @@ static int e1000_set_features(struct net_device *netdev,
        struct e1000_adapter *adapter = netdev_priv(netdev);
        netdev_features_t changed = features ^ netdev->features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                e1000_vlan_mode(netdev, features);
 
        if (!(changed & (NETIF_F_RXCSUM | NETIF_F_RXALL)))
@@ -1058,9 +1061,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (hw->mac_type >= e1000_82543) {
                netdev->hw_features = NETIF_F_SG |
                                   NETIF_F_HW_CSUM |
-                                  NETIF_F_HW_VLAN_RX;
-               netdev->features = NETIF_F_HW_VLAN_TX |
-                                  NETIF_F_HW_VLAN_FILTER;
+                                  NETIF_F_HW_VLAN_CTAG_RX;
+               netdev->features = NETIF_F_HW_VLAN_CTAG_TX |
+                                  NETIF_F_HW_VLAN_CTAG_FILTER;
        }
 
        if ((hw->mac_type >= e1000_82544) &&
@@ -1457,7 +1460,8 @@ static int e1000_close(struct net_device *netdev)
        if ((hw->mng_cookie.status &
             E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) &&
            !test_bit(adapter->mng_vlan_id, adapter->active_vlans)) {
-               e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
+               e1000_vlan_rx_kill_vid(netdev, htons(ETH_P_8021Q),
+                                      adapter->mng_vlan_id);
        }
 
        return 0;
@@ -1516,8 +1520,6 @@ static int e1000_setup_tx_resources(struct e1000_adapter *adapter,
        if (!txdr->desc) {
 setup_tx_desc_die:
                vfree(txdr->buffer_info);
-               e_err(probe, "Unable to allocate memory for the Tx descriptor "
-                     "ring\n");
                return -ENOMEM;
        }
 
@@ -1707,10 +1709,7 @@ static int e1000_setup_rx_resources(struct e1000_adapter *adapter,
 
        rxdr->desc = dma_alloc_coherent(&pdev->dev, rxdr->size, &rxdr->dma,
                                        GFP_KERNEL);
-
        if (!rxdr->desc) {
-               e_err(probe, "Unable to allocate memory for the Rx descriptor "
-                     "ring\n");
 setup_rx_desc_die:
                vfree(rxdr->buffer_info);
                return -ENOMEM;
@@ -1729,8 +1728,6 @@ setup_rx_desc_die:
                if (!rxdr->desc) {
                        dma_free_coherent(&pdev->dev, rxdr->size, olddesc,
                                          olddma);
-                       e_err(probe, "Unable to allocate memory for the Rx "
-                             "descriptor ring\n");
                        goto setup_rx_desc_die;
                }
 
@@ -4006,7 +4003,7 @@ static void e1000_receive_skb(struct e1000_adapter *adapter, u8 status,
        if (status & E1000_RXD_STAT_VP) {
                u16 vid = le16_to_cpu(vlan) & E1000_RXD_SPC_VLAN_MASK;
 
-               __vlan_hwaccel_put_tag(skb, vid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
        }
        napi_gro_receive(&adapter->napi, skb);
 }
@@ -4792,7 +4789,7 @@ static void __e1000_vlan_mode(struct e1000_adapter *adapter,
        u32 ctrl;
 
        ctrl = er32(CTRL);
-       if (features & NETIF_F_HW_VLAN_RX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_RX) {
                /* enable VLAN tag insert/strip */
                ctrl |= E1000_CTRL_VME;
        } else {
@@ -4844,7 +4841,8 @@ static void e1000_vlan_mode(struct net_device *netdev,
                e1000_irq_enable(adapter);
 }
 
-static int e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+static int e1000_vlan_rx_add_vid(struct net_device *netdev,
+                                __be16 proto, u16 vid)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -4869,7 +4867,8 @@ static int e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
        return 0;
 }
 
-static int e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+static int e1000_vlan_rx_kill_vid(struct net_device *netdev,
+                                 __be16 proto, u16 vid)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -4903,7 +4902,7 @@ static void e1000_restore_vlan(struct e1000_adapter *adapter)
 
        e1000_vlan_filter_on_off(adapter, true);
        for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
-               e1000_vlan_rx_add_vid(adapter->netdev, vid);
+               e1000_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid);
 }
 
 int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx)
index e0991388664c9322a11449b994d62d4bc888ef63..b71c8502a2b347d8c770ac723d659a6115127e35 100644 (file)
@@ -37,7 +37,9 @@
  * "index + 5".
  */
 static const u16 e1000_gg82563_cable_length_table[] = {
-        0, 60, 115, 150, 150, 60, 115, 150, 180, 180, 0xFF };
+       0, 60, 115, 150, 150, 60, 115, 150, 180, 180, 0xFF
+};
+
 #define GG82563_CABLE_LENGTH_TABLE_SIZE \
                ARRAY_SIZE(e1000_gg82563_cable_length_table)
 
@@ -116,7 +118,7 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw)
        nvm->type = e1000_nvm_eeprom_spi;
 
        size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >>
-                         E1000_EECD_SIZE_EX_SHIFT);
+                    E1000_EECD_SIZE_EX_SHIFT);
 
        /* Added to a constant, "size" becomes the left-shift value
         * for setting word_size.
@@ -393,7 +395,7 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
                 * before the device has completed the "Page Select" MDI
                 * transaction.  So we wait 200us after each MDI command...
                 */
-               udelay(200);
+               usleep_range(200, 400);
 
                /* ...and verify the command was successful. */
                ret_val = e1000e_read_phy_reg_mdic(hw, page_select, &temp);
@@ -403,17 +405,17 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
                        return -E1000_ERR_PHY;
                }
 
-               udelay(200);
+               usleep_range(200, 400);
 
                ret_val = e1000e_read_phy_reg_mdic(hw,
-                                                 MAX_PHY_REG_ADDRESS & offset,
-                                                 data);
+                                                  MAX_PHY_REG_ADDRESS & offset,
+                                                  data);
 
-               udelay(200);
+               usleep_range(200, 400);
        } else {
                ret_val = e1000e_read_phy_reg_mdic(hw,
-                                                 MAX_PHY_REG_ADDRESS & offset,
-                                                 data);
+                                                  MAX_PHY_REG_ADDRESS & offset,
+                                                  data);
        }
 
        e1000_release_phy_80003es2lan(hw);
@@ -462,7 +464,7 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
                 * before the device has completed the "Page Select" MDI
                 * transaction.  So we wait 200us after each MDI command...
                 */
-               udelay(200);
+               usleep_range(200, 400);
 
                /* ...and verify the command was successful. */
                ret_val = e1000e_read_phy_reg_mdic(hw, page_select, &temp);
@@ -472,17 +474,17 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
                        return -E1000_ERR_PHY;
                }
 
-               udelay(200);
+               usleep_range(200, 400);
 
                ret_val = e1000e_write_phy_reg_mdic(hw,
-                                                 MAX_PHY_REG_ADDRESS & offset,
-                                                 data);
+                                                   MAX_PHY_REG_ADDRESS &
+                                                   offset, data);
 
-               udelay(200);
+               usleep_range(200, 400);
        } else {
                ret_val = e1000e_write_phy_reg_mdic(hw,
-                                                 MAX_PHY_REG_ADDRESS & offset,
-                                                 data);
+                                                   MAX_PHY_REG_ADDRESS &
+                                                   offset, data);
        }
 
        e1000_release_phy_80003es2lan(hw);
@@ -580,7 +582,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw)
                e_dbg("Waiting for forced speed/duplex link on GG82563 phy.\n");
 
                ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
-                                                    100000, &link);
+                                                     100000, &link);
                if (ret_val)
                        return ret_val;
 
@@ -595,7 +597,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw)
 
                /* Try once more */
                ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
-                                                    100000, &link);
+                                                     100000, &link);
                if (ret_val)
                        return ret_val;
        }
@@ -666,14 +668,12 @@ static s32 e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed,
        s32 ret_val;
 
        if (hw->phy.media_type == e1000_media_type_copper) {
-               ret_val = e1000e_get_speed_and_duplex_copper(hw,
-                                                                   speed,
-                                                                   duplex);
+               ret_val = e1000e_get_speed_and_duplex_copper(hw, speed, duplex);
                hw->phy.ops.cfg_on_link_up(hw);
        } else {
                ret_val = e1000e_get_speed_and_duplex_fiber_serdes(hw,
-                                                                 speed,
-                                                                 duplex);
+                                                                  speed,
+                                                                  duplex);
        }
 
        return ret_val;
@@ -754,9 +754,9 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
 
        /* Initialize identification LED */
        ret_val = mac->ops.id_led_init(hw);
+       /* An error is not fatal and we should not stop init due to this */
        if (ret_val)
                e_dbg("Error initializing identification LED\n");
-               /* This is not fatal and we should not stop init due to this */
 
        /* Disabling VLAN filtering */
        e_dbg("Initializing the IEEE VLAN\n");
@@ -784,14 +784,14 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
 
        /* Set the transmit descriptor write-back policy */
        reg_data = er32(TXDCTL(0));
-       reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
-                  E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC;
+       reg_data = ((reg_data & ~E1000_TXDCTL_WTHRESH) |
+                   E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC);
        ew32(TXDCTL(0), reg_data);
 
        /* ...for both queues. */
        reg_data = er32(TXDCTL(1));
-       reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
-                  E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC;
+       reg_data = ((reg_data & ~E1000_TXDCTL_WTHRESH) |
+                   E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC);
        ew32(TXDCTL(1), reg_data);
 
        /* Enable retransmit on late collisions */
@@ -818,13 +818,12 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
        /* default to true to enable the MDIC W/A */
        hw->dev_spec.e80003es2lan.mdic_wa_enable = true;
 
-       ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
-                                     E1000_KMRNCTRLSTA_OFFSET >>
-                                     E1000_KMRNCTRLSTA_OFFSET_SHIFT,
-                                     &i);
+       ret_val =
+           e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_OFFSET >>
+                                           E1000_KMRNCTRLSTA_OFFSET_SHIFT, &i);
        if (!ret_val) {
                if ((i & E1000_KMRNCTRLSTA_OPMODE_MASK) ==
-                    E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO)
+                   E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO)
                        hw->dev_spec.e80003es2lan.mdic_wa_enable = false;
        }
 
@@ -891,7 +890,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
 {
        struct e1000_phy_info *phy = &hw->phy;
        s32 ret_val;
-       u32 ctrl_ext;
+       u32 reg;
        u16 data;
 
        ret_val = e1e_rphy(hw, GG82563_PHY_MAC_SPEC_CTRL, &data);
@@ -954,22 +953,19 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
        }
 
        /* Bypass Rx and Tx FIFO's */
-       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
-                                       E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL,
-                                       E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS |
-                                       E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS);
+       reg = E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL;
+       data = (E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS |
+               E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS);
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw, reg, data);
        if (ret_val)
                return ret_val;
 
-       ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
-                                      E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE,
-                                      &data);
+       reg = E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE;
+       ret_val = e1000_read_kmrn_reg_80003es2lan(hw, reg, &data);
        if (ret_val)
                return ret_val;
        data |= E1000_KMRNCTRLSTA_OPMODE_E_IDLE;
-       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
-                                       E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE,
-                                       data);
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw, reg, data);
        if (ret_val)
                return ret_val;
 
@@ -982,9 +978,9 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
        if (ret_val)
                return ret_val;
 
-       ctrl_ext = er32(CTRL_EXT);
-       ctrl_ext &= ~(E1000_CTRL_EXT_LINK_MODE_MASK);
-       ew32(CTRL_EXT, ctrl_ext);
+       reg = er32(CTRL_EXT);
+       reg &= ~E1000_CTRL_EXT_LINK_MODE_MASK;
+       ew32(CTRL_EXT, reg);
 
        ret_val = e1e_rphy(hw, GG82563_PHY_PWR_MGMT_CTRL, &data);
        if (ret_val)
@@ -1049,27 +1045,29 @@ static s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw)
         * polling the phy; this fixes erroneous timeouts at 10Mbps.
         */
        ret_val = e1000_write_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 4),
-                                                  0xFFFF);
+                                                  0xFFFF);
        if (ret_val)
                return ret_val;
        ret_val = e1000_read_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 9),
-                                                 &reg_data);
+                                                 &reg_data);
        if (ret_val)
                return ret_val;
        reg_data |= 0x3F;
        ret_val = e1000_write_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 9),
-                                                  reg_data);
+                                                  reg_data);
        if (ret_val)
                return ret_val;
-       ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
-                                     E1000_KMRNCTRLSTA_OFFSET_INB_CTRL,
-                                     &reg_data);
+       ret_val =
+           e1000_read_kmrn_reg_80003es2lan(hw,
+                                           E1000_KMRNCTRLSTA_OFFSET_INB_CTRL,
+                                           &reg_data);
        if (ret_val)
                return ret_val;
        reg_data |= E1000_KMRNCTRLSTA_INB_CTRL_DIS_PADDING;
-       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
-                                       E1000_KMRNCTRLSTA_OFFSET_INB_CTRL,
-                                       reg_data);
+       ret_val =
+           e1000_write_kmrn_reg_80003es2lan(hw,
+                                            E1000_KMRNCTRLSTA_OFFSET_INB_CTRL,
+                                            reg_data);
        if (ret_val)
                return ret_val;
 
@@ -1096,7 +1094,7 @@ static s32 e1000_cfg_on_link_up_80003es2lan(struct e1000_hw *hw)
 
        if (hw->phy.media_type == e1000_media_type_copper) {
                ret_val = e1000e_get_speed_and_duplex_copper(hw, &speed,
-                                                            &duplex);
+                                                            &duplex);
                if (ret_val)
                        return ret_val;
 
@@ -1125,9 +1123,10 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex)
        u16 reg_data, reg_data2;
 
        reg_data = E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT;
-       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
-                                      E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
-                                      reg_data);
+       ret_val =
+           e1000_write_kmrn_reg_80003es2lan(hw,
+                                            E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
+                                            reg_data);
        if (ret_val)
                return ret_val;
 
@@ -1171,9 +1170,10 @@ static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw)
        u32 i = 0;
 
        reg_data = E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT;
-       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
-                                      E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
-                                      reg_data);
+       ret_val =
+           e1000_write_kmrn_reg_80003es2lan(hw,
+                                            E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
+                                            reg_data);
        if (ret_val)
                return ret_val;
 
@@ -1220,7 +1220,7 @@ static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
                return ret_val;
 
        kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
-                      E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
+                      E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
        ew32(KMRNCTRLSTA, kmrnctrlsta);
        e1e_flush();
 
@@ -1255,7 +1255,7 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
                return ret_val;
 
        kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
-                      E1000_KMRNCTRLSTA_OFFSET) | data;
+                      E1000_KMRNCTRLSTA_OFFSET) | data;
        ew32(KMRNCTRLSTA, kmrnctrlsta);
        e1e_flush();
 
@@ -1419,4 +1419,3 @@ const struct e1000_info e1000_es2_info = {
        .phy_ops                = &es2_phy_ops,
        .nvm_ops                = &es2_nvm_ops,
 };
-
index 2faffbde179ef95bf46beb514cf483eb996a204a..7380442a38299b5480c7858ee344a48bc87905b5 100644 (file)
@@ -184,7 +184,7 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw)
        default:
                nvm->type = e1000_nvm_eeprom_spi;
                size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >>
-                                 E1000_EECD_SIZE_EX_SHIFT);
+                            E1000_EECD_SIZE_EX_SHIFT);
                /* Added to a constant, "size" becomes the left-shift value
                 * for setting word_size.
                 */
@@ -437,7 +437,7 @@ static s32 e1000_get_phy_id_82571(struct e1000_hw *hw)
                        return ret_val;
 
                phy->id = (u32)(phy_id << 16);
-               udelay(20);
+               usleep_range(20, 40);
                ret_val = e1e_rphy(hw, MII_PHYSID2, &phy_id);
                if (ret_val)
                        return ret_val;
@@ -482,7 +482,7 @@ static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
                if (!(swsm & E1000_SWSM_SMBI))
                        break;
 
-               udelay(50);
+               usleep_range(50, 100);
                i++;
        }
 
@@ -499,7 +499,7 @@ static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
                if (er32(SWSM) & E1000_SWSM_SWESMBI)
                        break;
 
-               udelay(50);
+               usleep_range(50, 100);
        }
 
        if (i == fw_timeout) {
@@ -526,6 +526,7 @@ static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw)
        swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI);
        ew32(SWSM, swsm);
 }
+
 /**
  *  e1000_get_hw_semaphore_82573 - Acquire hardware semaphore
  *  @hw: pointer to the HW structure
@@ -846,9 +847,9 @@ static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset,
        }
 
        for (i = 0; i < words; i++) {
-               eewr = (data[i] << E1000_NVM_RW_REG_DATA) |
-                      ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) |
-                      E1000_NVM_RW_REG_START;
+               eewr = ((data[i] << E1000_NVM_RW_REG_DATA) |
+                       ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) |
+                       E1000_NVM_RW_REG_START);
 
                ret_val = e1000e_poll_eerd_eewr_done(hw, E1000_NVM_POLL_WRITE);
                if (ret_val)
@@ -875,8 +876,7 @@ static s32 e1000_get_cfg_done_82571(struct e1000_hw *hw)
        s32 timeout = PHY_CFG_TIMEOUT;
 
        while (timeout) {
-               if (er32(EEMNGCTL) &
-                   E1000_NVM_CFG_DONE_PORT_0)
+               if (er32(EEMNGCTL) & E1000_NVM_CFG_DONE_PORT_0)
                        break;
                usleep_range(1000, 2000);
                timeout--;
@@ -1022,7 +1022,7 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
        }
 
        if (hw->nvm.type == e1000_nvm_flash_hw) {
-               udelay(10);
+               usleep_range(10, 20);
                ctrl_ext = er32(CTRL_EXT);
                ctrl_ext |= E1000_CTRL_EXT_EE_RST;
                ew32(CTRL_EXT, ctrl_ext);
@@ -1095,9 +1095,9 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
 
        /* Initialize identification LED */
        ret_val = mac->ops.id_led_init(hw);
+       /* An error is not fatal and we should not stop init due to this */
        if (ret_val)
                e_dbg("Error initializing identification LED\n");
-               /* This is not fatal and we should not stop init due to this */
 
        /* Disabling VLAN filtering */
        e_dbg("Initializing the IEEE VLAN\n");
@@ -1122,9 +1122,8 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
 
        /* Set the transmit descriptor write-back policy */
        reg_data = er32(TXDCTL(0));
-       reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
-                  E1000_TXDCTL_FULL_TX_DESC_WB |
-                  E1000_TXDCTL_COUNT_DESC;
+       reg_data = ((reg_data & ~E1000_TXDCTL_WTHRESH) |
+                   E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC);
        ew32(TXDCTL(0), reg_data);
 
        /* ...for both queues. */
@@ -1140,9 +1139,9 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
                break;
        default:
                reg_data = er32(TXDCTL(1));
-               reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
-                          E1000_TXDCTL_FULL_TX_DESC_WB |
-                          E1000_TXDCTL_COUNT_DESC;
+               reg_data = ((reg_data & ~E1000_TXDCTL_WTHRESH) |
+                           E1000_TXDCTL_FULL_TX_DESC_WB |
+                           E1000_TXDCTL_COUNT_DESC);
                ew32(TXDCTL(1), reg_data);
                break;
        }
@@ -1530,7 +1529,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
        status = er32(STATUS);
        er32(RXCW);
        /* SYNCH bit and IV bit are sticky */
-       udelay(10);
+       usleep_range(10, 20);
        rxcw = er32(RXCW);
 
        if ((rxcw & E1000_RXCW_SYNCH) && !(rxcw & E1000_RXCW_IV)) {
@@ -1633,7 +1632,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
                         * the IV bit and restart Autoneg
                         */
                        for (i = 0; i < AN_RETRY_COUNT; i++) {
-                               udelay(10);
+                               usleep_range(10, 20);
                                rxcw = er32(RXCW);
                                if ((rxcw & E1000_RXCW_SYNCH) &&
                                    (rxcw & E1000_RXCW_C))
@@ -2066,4 +2065,3 @@ const struct e1000_info e1000_82583_info = {
        .phy_ops                = &e82_phy_ops_bm,
        .nvm_ops                = &e82571_nvm_ops,
 };
-
index 85cb1a3b7cd47e15affd772a10552df2e15083d0..08e24dc3dc0e9d49c7742fed7b829826e2cea095 100644 (file)
@@ -44,6 +44,8 @@
 #define E1000_EIAC_82574       0x000DC /* Ext. Interrupt Auto Clear - RW */
 #define E1000_EIAC_MASK_82574  0x01F00000
 
+#define E1000_IVAR_INT_ALLOC_VALID     0x8
+
 /* Manageability Operation Mode mask */
 #define E1000_NVM_INIT_CTRL2_MNGM      0x6000
 
index fc3a4fe1ac71b56526e7caa9d5747cd232fd29dd..351c94a0cf74944b95c278c69709257216af64ef 100644 (file)
@@ -66,7 +66,7 @@
 #define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES  0x00C00000
 #define E1000_CTRL_EXT_EIAME          0x01000000
 #define E1000_CTRL_EXT_DRV_LOAD       0x10000000 /* Driver loaded bit for FW */
-#define E1000_CTRL_EXT_IAME           0x08000000 /* Interrupt acknowledge Auto-mask */
+#define E1000_CTRL_EXT_IAME            0x08000000 /* Int ACK Auto-mask */
 #define E1000_CTRL_EXT_PBA_CLR        0x80000000 /* PBA Clear */
 #define E1000_CTRL_EXT_LSECCK         0x00001000
 #define E1000_CTRL_EXT_PHYPDEN        0x00100000
 #define E1000_CTRL_MEHE     0x00080000  /* Memory Error Handling Enable */
 #define E1000_CTRL_SWDPIN0  0x00040000  /* SWDPIN 0 value */
 #define E1000_CTRL_SWDPIN1  0x00080000  /* SWDPIN 1 value */
+#define E1000_CTRL_ADVD3WUC 0x00100000  /* D3 WUC */
+#define E1000_CTRL_EN_PHY_PWR_MGMT 0x00200000 /* PHY PM enable */
 #define E1000_CTRL_SWDPIO0  0x00400000  /* SWDPIN 0 Input or output */
 #define E1000_CTRL_RST      0x04000000  /* Global reset */
 #define E1000_CTRL_RFCE     0x08000000  /* Receive Flow Control enable */
 #define E1000_STATUS_FUNC_SHIFT 2
 #define E1000_STATUS_FUNC_1     0x00000004      /* Function 1 */
 #define E1000_STATUS_TXOFF      0x00000010      /* transmission paused */
+#define E1000_STATUS_SPEED_MASK 0x000000C0
 #define E1000_STATUS_SPEED_10   0x00000000      /* Speed 10Mb/s */
 #define E1000_STATUS_SPEED_100  0x00000040      /* Speed 100Mb/s */
 #define E1000_STATUS_SPEED_1000 0x00000080      /* Speed 1000Mb/s */
 #define E1000_STATUS_LAN_INIT_DONE 0x00000200   /* Lan Init Completion by NVM */
 #define E1000_STATUS_PHYRA      0x00000400      /* PHY Reset Asserted */
-#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */
+#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000      /* Master Req status */
 
 #define HALF_DUPLEX 1
 #define FULL_DUPLEX 2
 
-
 #define ADVERTISE_10_HALF                 0x0001
 #define ADVERTISE_10_FULL                 0x0002
 #define ADVERTISE_100_HALF                0x0004
 
 /* SerDes Control */
 #define E1000_SCTL_DISABLE_SERDES_LOOPBACK 0x0400
+#define E1000_SCTL_ENABLE_SERDES_LOOPBACK      0x0410
 
 /* Receive Checksum Control */
 #define E1000_RXCSUM_TUOFL     0x00000200   /* TCP / UDP checksum offload */
 #define E1000_ICR_RXDMT0        0x00000010 /* Rx desc min. threshold (0) */
 #define E1000_ICR_RXT0          0x00000080 /* Rx timer intr (ring 0) */
 #define E1000_ICR_ECCER         0x00400000 /* Uncorrectable ECC Error */
-#define E1000_ICR_INT_ASSERTED  0x80000000 /* If this bit asserted, the driver should claim the interrupt */
+/* If this bit asserted, the driver should claim the interrupt */
+#define E1000_ICR_INT_ASSERTED 0x80000000
 #define E1000_ICR_RXQ0          0x00100000 /* Rx Queue 0 Interrupt */
 #define E1000_ICR_RXQ1          0x00200000 /* Rx Queue 1 Interrupt */
 #define E1000_ICR_TXQ0          0x00400000 /* Tx Queue 0 Interrupt */
 #define E1000_EECD_SEC1VAL   0x00400000 /* Sector One Valid */
 #define E1000_EECD_SEC1VAL_VALID_MASK (E1000_EECD_AUTO_RD | E1000_EECD_PRES)
 
-#define E1000_NVM_RW_REG_DATA   16   /* Offset to data in NVM read/write registers */
-#define E1000_NVM_RW_REG_DONE   2    /* Offset to READ/WRITE done bit */
-#define E1000_NVM_RW_REG_START  1    /* Start operation */
-#define E1000_NVM_RW_ADDR_SHIFT 2    /* Shift to the address bits */
-#define E1000_NVM_POLL_WRITE    1    /* Flag for polling for write complete */
-#define E1000_NVM_POLL_READ     0    /* Flag for polling for read complete */
-#define E1000_FLASH_UPDATES  2000
+#define E1000_NVM_RW_REG_DATA  16      /* Offset to data in NVM r/w regs */
+#define E1000_NVM_RW_REG_DONE  2       /* Offset to READ/WRITE done bit */
+#define E1000_NVM_RW_REG_START 1       /* Start operation */
+#define E1000_NVM_RW_ADDR_SHIFT        2       /* Shift to the address bits */
+#define E1000_NVM_POLL_WRITE   1       /* Flag for polling write complete */
+#define E1000_NVM_POLL_READ    0       /* Flag for polling read complete */
+#define E1000_FLASH_UPDATES    2000
 
 /* NVM Word Offsets */
 #define NVM_COMPAT                 0x0003
        GG82563_REG(194, 18) /* Inband Control */
 
 /* MDI Control */
+#define E1000_MDIC_REG_MASK    0x001F0000
 #define E1000_MDIC_REG_SHIFT 16
 #define E1000_MDIC_PHY_SHIFT 21
 #define E1000_MDIC_OP_WRITE  0x04000000
index fcc758138b8a0bd6ad40edd145a0f6d8f2c0c232..82f1c84282dbe5278f75f14e5c8913431ed902c7 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/ptp_clock_kernel.h>
 #include <linux/ptp_classify.h>
 #include <linux/mii.h>
+#include <linux/mdio.h>
 #include "hw.h"
 
 struct e1000_info;
@@ -61,7 +62,6 @@ struct e1000_info;
 #define e_notice(format, arg...) \
        netdev_notice(adapter->netdev, format, ## arg)
 
-
 /* Interrupt modes, as used by the IntMode parameter */
 #define E1000E_INT_MODE_LEGACY         0
 #define E1000E_INT_MODE_MSI            1
@@ -239,9 +239,8 @@ struct e1000_adapter {
        u16 tx_itr;
        u16 rx_itr;
 
-       /* Tx */
-       struct e1000_ring *tx_ring /* One per active queue */
-                                               ____cacheline_aligned_in_smp;
+       /* Tx - one ring per active queue */
+       struct e1000_ring *tx_ring ____cacheline_aligned_in_smp;
        u32 tx_fifo_limit;
 
        struct napi_struct napi;
@@ -352,6 +351,8 @@ struct e1000_adapter {
        struct timecounter tc;
        struct ptp_clock *ptp_clock;
        struct ptp_clock_info ptp_clock_info;
+
+       u16 eee_advert;
 };
 
 struct e1000_info {
@@ -487,8 +488,8 @@ extern int e1000e_setup_tx_resources(struct e1000_ring *ring);
 extern void e1000e_free_rx_resources(struct e1000_ring *ring);
 extern void e1000e_free_tx_resources(struct e1000_ring *ring);
 extern struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev,
-                                                    struct rtnl_link_stats64
-                                                    *stats);
+                                                   struct rtnl_link_stats64
+                                                   *stats);
 extern void e1000e_set_interrupt_capability(struct e1000_adapter *adapter);
 extern void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter);
 extern void e1000e_get_hw_control(struct e1000_adapter *adapter);
@@ -558,12 +559,14 @@ static inline s32 e1000e_update_nvm_checksum(struct e1000_hw *hw)
        return hw->nvm.ops.update(hw);
 }
 
-static inline s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
+static inline s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words,
+                                u16 *data)
 {
        return hw->nvm.ops.read(hw, offset, words, data);
 }
 
-static inline s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
+static inline s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words,
+                                 u16 *data)
 {
        return hw->nvm.ops.write(hw, offset, words, data);
 }
@@ -597,7 +600,7 @@ static inline s32 __ew32_prepare(struct e1000_hw *hw)
        s32 i = E1000_ICH_FWSM_PCIM2PCI_COUNT;
 
        while ((er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI) && --i)
-               udelay(50);
+               usleep_range(50, 100);
 
        return i;
 }
index f91a8f3f9d48beff868f0e9e567ec1779273c877..7c8ca658d553d489f37234fe177d25ed870975d0 100644 (file)
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
-#include <linux/mdio.h>
 #include <linux/pm_runtime.h>
 
 #include "e1000.h"
 
-enum {NETDEV_STATS, E1000_STATS};
+enum { NETDEV_STATS, E1000_STATS };
 
 struct e1000_stats {
        char stat_string[ETH_GSTRING_LEN];
@@ -121,6 +120,7 @@ static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = {
        "Interrupt test (offline)", "Loopback test  (offline)",
        "Link test   (on/offline)"
 };
+
 #define E1000_TEST_LEN ARRAY_SIZE(e1000_gstrings_test)
 
 static int e1000_get_settings(struct net_device *netdev,
@@ -197,8 +197,7 @@ static int e1000_get_settings(struct net_device *netdev,
        /* MDI-X => 2; MDI =>1; Invalid =>0 */
        if ((hw->phy.media_type == e1000_media_type_copper) &&
            netif_carrier_ok(netdev))
-               ecmd->eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X :
-                                                     ETH_TP_MDI;
+               ecmd->eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X : ETH_TP_MDI;
        else
                ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
 
@@ -224,8 +223,7 @@ static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx)
 
        /* Fiber NICs only allow 1000 gbps Full duplex */
        if ((adapter->hw.phy.media_type == e1000_media_type_fiber) &&
-           spd != SPEED_1000 &&
-           dplx != DUPLEX_FULL) {
+           (spd != SPEED_1000) && (dplx != DUPLEX_FULL)) {
                goto err_inval;
        }
 
@@ -298,12 +296,10 @@ static int e1000_set_settings(struct net_device *netdev,
                hw->mac.autoneg = 1;
                if (hw->phy.media_type == e1000_media_type_fiber)
                        hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full |
-                                                    ADVERTISED_FIBRE |
-                                                    ADVERTISED_Autoneg;
+                           ADVERTISED_FIBRE | ADVERTISED_Autoneg;
                else
                        hw->phy.autoneg_advertised = ecmd->advertising |
-                                                    ADVERTISED_TP |
-                                                    ADVERTISED_Autoneg;
+                           ADVERTISED_TP | ADVERTISED_Autoneg;
                ecmd->advertising = hw->phy.autoneg_advertised;
                if (adapter->fc_autoneg)
                        hw->fc.requested_mode = e1000_fc_default;
@@ -346,7 +342,7 @@ static void e1000_get_pauseparam(struct net_device *netdev,
        struct e1000_hw *hw = &adapter->hw;
 
        pause->autoneg =
-               (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
+           (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
 
        if (hw->fc.current_mode == e1000_fc_rx_pause) {
                pause->rx_pause = 1;
@@ -435,7 +431,7 @@ static void e1000_get_regs(struct net_device *netdev,
        memset(p, 0, E1000_REGS_LEN * sizeof(u32));
 
        regs->version = (1 << 24) | (adapter->pdev->revision << 16) |
-                       adapter->pdev->device;
+           adapter->pdev->device;
 
        regs_buff[0]  = er32(CTRL);
        regs_buff[1]  = er32(STATUS);
@@ -503,8 +499,8 @@ static int e1000_get_eeprom(struct net_device *netdev,
        first_word = eeprom->offset >> 1;
        last_word = (eeprom->offset + eeprom->len - 1) >> 1;
 
-       eeprom_buff = kmalloc(sizeof(u16) *
-                       (last_word - first_word + 1), GFP_KERNEL);
+       eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+                             GFP_KERNEL);
        if (!eeprom_buff)
                return -ENOMEM;
 
@@ -515,7 +511,7 @@ static int e1000_get_eeprom(struct net_device *netdev,
        } else {
                for (i = 0; i < last_word - first_word + 1; i++) {
                        ret_val = e1000_read_nvm(hw, first_word + i, 1,
-                                                     &eeprom_buff[i]);
+                                                &eeprom_buff[i]);
                        if (ret_val)
                                break;
                }
@@ -553,7 +549,8 @@ static int e1000_set_eeprom(struct net_device *netdev,
        if (eeprom->len == 0)
                return -EOPNOTSUPP;
 
-       if (eeprom->magic != (adapter->pdev->vendor | (adapter->pdev->device << 16)))
+       if (eeprom->magic !=
+           (adapter->pdev->vendor | (adapter->pdev->device << 16)))
                return -EFAULT;
 
        if (adapter->flags & FLAG_READ_ONLY_NVM)
@@ -579,7 +576,7 @@ static int e1000_set_eeprom(struct net_device *netdev,
                /* need read/modify/write of last changed EEPROM word */
                /* only the first byte of the word is being modified */
                ret_val = e1000_read_nvm(hw, last_word, 1,
-                                 &eeprom_buff[last_word - first_word]);
+                                        &eeprom_buff[last_word - first_word]);
 
        if (ret_val)
                goto out;
@@ -618,8 +615,7 @@ static void e1000_get_drvinfo(struct net_device *netdev,
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
 
-       strlcpy(drvinfo->driver,  e1000e_driver_name,
-               sizeof(drvinfo->driver));
+       strlcpy(drvinfo->driver, e1000e_driver_name, sizeof(drvinfo->driver));
        strlcpy(drvinfo->version, e1000e_driver_version,
                sizeof(drvinfo->version));
 
@@ -627,10 +623,10 @@ static void e1000_get_drvinfo(struct net_device *netdev,
         * PCI-E controllers
         */
        snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
-               "%d.%d-%d",
-               (adapter->eeprom_vers & 0xF000) >> 12,
-               (adapter->eeprom_vers & 0x0FF0) >> 4,
-               (adapter->eeprom_vers & 0x000F));
+                "%d.%d-%d",
+                (adapter->eeprom_vers & 0xF000) >> 12,
+                (adapter->eeprom_vers & 0x0FF0) >> 4,
+                (adapter->eeprom_vers & 0x000F));
 
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
@@ -756,7 +752,8 @@ static bool reg_pattern_test(struct e1000_adapter *adapter, u64 *data,
 {
        u32 pat, val;
        static const u32 test[] = {
-               0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};
+               0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF
+       };
        for (pat = 0; pat < ARRAY_SIZE(test); pat++) {
                E1000_WRITE_REG_ARRAY(&adapter->hw, reg, offset,
                                      (test[pat] & write));
@@ -786,6 +783,7 @@ static bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data,
        }
        return 0;
 }
+
 #define REG_PATTERN_TEST_ARRAY(reg, offset, mask, write)                       \
        do {                                                                   \
                if (reg_pattern_test(adapter, data, reg, offset, mask, write)) \
@@ -813,16 +811,16 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data)
        u32 wlock_mac = 0;
 
        /* The status register is Read Only, so a write should fail.
-        * Some bits that get toggled are ignored.
+        * Some bits that get toggled are ignored.  There are several bits
+        * on newer hardware that are r/w.
         */
        switch (mac->type) {
-       /* there are several bits on newer hardware that are r/w */
        case e1000_82571:
        case e1000_82572:
        case e1000_80003es2lan:
                toggle = 0x7FFFF3FF;
                break;
-        default:
+       default:
                toggle = 0x7FFFF033;
                break;
        }
@@ -928,7 +926,7 @@ static int e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data)
        }
 
        /* If Checksum is not Correct return error else test passed */
-       if ((checksum != (u16) NVM_SUM) && !(*data))
+       if ((checksum != (u16)NVM_SUM) && !(*data))
                *data = 2;
 
        return *data;
@@ -936,7 +934,7 @@ static int e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data)
 
 static irqreturn_t e1000_test_intr(int __always_unused irq, void *data)
 {
-       struct net_device *netdev = (struct net_device *) data;
+       struct net_device *netdev = (struct net_device *)data;
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
 
@@ -969,8 +967,8 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
        if (!request_irq(irq, e1000_test_intr, IRQF_PROBE_SHARED, netdev->name,
                         netdev)) {
                shared_int = 0;
-       } else if (request_irq(irq, e1000_test_intr, IRQF_SHARED,
-                netdev->name, netdev)) {
+       } else if (request_irq(irq, e1000_test_intr, IRQF_SHARED, netdev->name,
+                              netdev)) {
                *data = 1;
                ret_val = -1;
                goto out;
@@ -1080,28 +1078,33 @@ static void e1000_free_desc_rings(struct e1000_adapter *adapter)
        struct e1000_ring *tx_ring = &adapter->test_tx_ring;
        struct e1000_ring *rx_ring = &adapter->test_rx_ring;
        struct pci_dev *pdev = adapter->pdev;
+       struct e1000_buffer *buffer_info;
        int i;
 
        if (tx_ring->desc && tx_ring->buffer_info) {
                for (i = 0; i < tx_ring->count; i++) {
-                       if (tx_ring->buffer_info[i].dma)
+                       buffer_info = &tx_ring->buffer_info[i];
+
+                       if (buffer_info->dma)
                                dma_unmap_single(&pdev->dev,
-                                       tx_ring->buffer_info[i].dma,
-                                       tx_ring->buffer_info[i].length,
-                                       DMA_TO_DEVICE);
-                       if (tx_ring->buffer_info[i].skb)
-                               dev_kfree_skb(tx_ring->buffer_info[i].skb);
+                                                buffer_info->dma,
+                                                buffer_info->length,
+                                                DMA_TO_DEVICE);
+                       if (buffer_info->skb)
+                               dev_kfree_skb(buffer_info->skb);
                }
        }
 
        if (rx_ring->desc && rx_ring->buffer_info) {
                for (i = 0; i < rx_ring->count; i++) {
-                       if (rx_ring->buffer_info[i].dma)
+                       buffer_info = &rx_ring->buffer_info[i];
+
+                       if (buffer_info->dma)
                                dma_unmap_single(&pdev->dev,
-                                       rx_ring->buffer_info[i].dma,
-                                       2048, DMA_FROM_DEVICE);
-                       if (rx_ring->buffer_info[i].skb)
-                               dev_kfree_skb(rx_ring->buffer_info[i].skb);
+                                                buffer_info->dma,
+                                                2048, DMA_FROM_DEVICE);
+                       if (buffer_info->skb)
+                               dev_kfree_skb(buffer_info->skb);
                }
        }
 
@@ -1138,8 +1141,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
                tx_ring->count = E1000_DEFAULT_TXD;
 
        tx_ring->buffer_info = kcalloc(tx_ring->count,
-                                      sizeof(struct e1000_buffer),
-                                      GFP_KERNEL);
+                                      sizeof(struct e1000_buffer), GFP_KERNEL);
        if (!tx_ring->buffer_info) {
                ret_val = 1;
                goto err_nomem;
@@ -1156,8 +1158,8 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
        tx_ring->next_to_use = 0;
        tx_ring->next_to_clean = 0;
 
-       ew32(TDBAL(0), ((u64) tx_ring->dma & 0x00000000FFFFFFFF));
-       ew32(TDBAH(0), ((u64) tx_ring->dma >> 32));
+       ew32(TDBAL(0), ((u64)tx_ring->dma & 0x00000000FFFFFFFF));
+       ew32(TDBAH(0), ((u64)tx_ring->dma >> 32));
        ew32(TDLEN(0), tx_ring->count * sizeof(struct e1000_tx_desc));
        ew32(TDH(0), 0);
        ew32(TDT(0), 0);
@@ -1179,8 +1181,8 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
                tx_ring->buffer_info[i].skb = skb;
                tx_ring->buffer_info[i].length = skb->len;
                tx_ring->buffer_info[i].dma =
-                       dma_map_single(&pdev->dev, skb->data, skb->len,
-                                      DMA_TO_DEVICE);
+                   dma_map_single(&pdev->dev, skb->data, skb->len,
+                                  DMA_TO_DEVICE);
                if (dma_mapping_error(&pdev->dev,
                                      tx_ring->buffer_info[i].dma)) {
                        ret_val = 4;
@@ -1200,8 +1202,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
                rx_ring->count = E1000_DEFAULT_RXD;
 
        rx_ring->buffer_info = kcalloc(rx_ring->count,
-                                      sizeof(struct e1000_buffer),
-                                      GFP_KERNEL);
+                                      sizeof(struct e1000_buffer), GFP_KERNEL);
        if (!rx_ring->buffer_info) {
                ret_val = 5;
                goto err_nomem;
@@ -1220,16 +1221,16 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
        rctl = er32(RCTL);
        if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX))
                ew32(RCTL, rctl & ~E1000_RCTL_EN);
-       ew32(RDBAL(0), ((u64) rx_ring->dma & 0xFFFFFFFF));
-       ew32(RDBAH(0), ((u64) rx_ring->dma >> 32));
+       ew32(RDBAL(0), ((u64)rx_ring->dma & 0xFFFFFFFF));
+       ew32(RDBAH(0), ((u64)rx_ring->dma >> 32));
        ew32(RDLEN(0), rx_ring->size);
        ew32(RDH(0), 0);
        ew32(RDT(0), 0);
        rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 |
-               E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_LPE |
-               E1000_RCTL_SBP | E1000_RCTL_SECRC |
-               E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
-               (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
+           E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_LPE |
+           E1000_RCTL_SBP | E1000_RCTL_SECRC |
+           E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
+           (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
        ew32(RCTL, rctl);
 
        for (i = 0; i < rx_ring->count; i++) {
@@ -1244,8 +1245,8 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
                skb_reserve(skb, NET_IP_ALIGN);
                rx_ring->buffer_info[i].skb = skb;
                rx_ring->buffer_info[i].dma =
-                       dma_map_single(&pdev->dev, skb->data, 2048,
-                                      DMA_FROM_DEVICE);
+                   dma_map_single(&pdev->dev, skb->data, 2048,
+                                  DMA_FROM_DEVICE);
                if (dma_mapping_error(&pdev->dev,
                                      rx_ring->buffer_info[i].dma)) {
                        ret_val = 8;
@@ -1296,7 +1297,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
 
                ew32(CTRL, ctrl_reg);
                e1e_flush();
-               udelay(500);
+               usleep_range(500, 1000);
 
                return 0;
        }
@@ -1322,7 +1323,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
                e1e_wphy(hw, PHY_REG(2, 21), phy_reg);
                /* Assert SW reset for above settings to take effect */
                hw->phy.ops.commit(hw);
-               mdelay(1);
+               usleep_range(1000, 2000);
                /* Force Full Duplex */
                e1e_rphy(hw, PHY_REG(769, 16), &phy_reg);
                e1e_wphy(hw, PHY_REG(769, 16), phy_reg | 0x000C);
@@ -1363,7 +1364,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
 
        /* force 1000, set loopback */
        e1e_wphy(hw, MII_BMCR, 0x4140);
-       mdelay(250);
+       msleep(250);
 
        /* Now set up the MAC to the same speed/duplex as the PHY. */
        ctrl_reg = er32(CTRL);
@@ -1395,7 +1396,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
        if (hw->phy.type == e1000_phy_m88)
                e1000_phy_disable_receiver(adapter);
 
-       udelay(500);
+       usleep_range(500, 1000);
 
        return 0;
 }
@@ -1431,8 +1432,7 @@ static int e1000_set_82571_fiber_loopback(struct e1000_adapter *adapter)
        /* special write to serdes control register to enable SerDes analog
         * loopback
         */
-#define E1000_SERDES_LB_ON 0x410
-       ew32(SCTL, E1000_SERDES_LB_ON);
+       ew32(SCTL, E1000_SCTL_ENABLE_SERDES_LOOPBACK);
        e1e_flush();
        usleep_range(10000, 20000);
 
@@ -1526,8 +1526,7 @@ static void e1000_loopback_cleanup(struct e1000_adapter *adapter)
        case e1000_82572:
                if (hw->phy.media_type == e1000_media_type_fiber ||
                    hw->phy.media_type == e1000_media_type_internal_serdes) {
-#define E1000_SERDES_LB_OFF 0x400
-                       ew32(SCTL, E1000_SERDES_LB_OFF);
+                       ew32(SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK);
                        e1e_flush();
                        usleep_range(10000, 20000);
                        break;
@@ -1564,7 +1563,7 @@ static int e1000_check_lbtest_frame(struct sk_buff *skb,
        frame_size &= ~1;
        if (*(skb->data + 3) == 0xFF)
                if ((*(skb->data + frame_size / 2 + 10) == 0xBE) &&
-                  (*(skb->data + frame_size / 2 + 12) == 0xAF))
+                   (*(skb->data + frame_size / 2 + 12) == 0xAF))
                        return 0;
        return 13;
 }
@@ -1575,6 +1574,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
        struct e1000_ring *rx_ring = &adapter->test_rx_ring;
        struct pci_dev *pdev = adapter->pdev;
        struct e1000_hw *hw = &adapter->hw;
+       struct e1000_buffer *buffer_info;
        int i, j, k, l;
        int lc;
        int good_cnt;
@@ -1595,14 +1595,17 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
 
        k = 0;
        l = 0;
-       for (j = 0; j <= lc; j++) { /* loop count loop */
-               for (i = 0; i < 64; i++) { /* send the packets */
-                       e1000_create_lbtest_frame(tx_ring->buffer_info[k].skb,
-                                                 1024);
+       /* loop count loop */
+       for (j = 0; j <= lc; j++) {
+               /* send the packets */
+               for (i = 0; i < 64; i++) {
+                       buffer_info = &tx_ring->buffer_info[k];
+
+                       e1000_create_lbtest_frame(buffer_info->skb, 1024);
                        dma_sync_single_for_device(&pdev->dev,
-                                       tx_ring->buffer_info[k].dma,
-                                       tx_ring->buffer_info[k].length,
-                                       DMA_TO_DEVICE);
+                                                  buffer_info->dma,
+                                                  buffer_info->length,
+                                                  DMA_TO_DEVICE);
                        k++;
                        if (k == tx_ring->count)
                                k = 0;
@@ -1612,13 +1615,16 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
                msleep(200);
                time = jiffies; /* set the start time for the receive */
                good_cnt = 0;
-               do { /* receive the sent packets */
+               /* receive the sent packets */
+               do {
+                       buffer_info = &rx_ring->buffer_info[l];
+
                        dma_sync_single_for_cpu(&pdev->dev,
-                                       rx_ring->buffer_info[l].dma, 2048,
-                                       DMA_FROM_DEVICE);
+                                               buffer_info->dma, 2048,
+                                               DMA_FROM_DEVICE);
 
-                       ret_val = e1000_check_lbtest_frame(
-                                       rx_ring->buffer_info[l].skb, 1024);
+                       ret_val = e1000_check_lbtest_frame(buffer_info->skb,
+                                                          1024);
                        if (!ret_val)
                                good_cnt++;
                        l++;
@@ -1637,7 +1643,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
                        ret_val = 14; /* error code for time out error */
                        break;
                }
-       } /* end loop count loop */
+       }
        return ret_val;
 }
 
@@ -1696,7 +1702,7 @@ static int e1000_link_test(struct e1000_adapter *adapter, u64 *data)
                        /* On some Phy/switch combinations, link establishment
                         * can take a few seconds more than expected.
                         */
-                       msleep(5000);
+                       msleep_interruptible(5000);
 
                if (!(er32(STATUS) & E1000_STATUS_LU))
                        *data = 1;
@@ -1980,12 +1986,12 @@ static void e1000_get_ethtool_stats(struct net_device *netdev,
        for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
                switch (e1000_gstrings_stats[i].type) {
                case NETDEV_STATS:
-                       p = (char *) &net_stats +
-                                       e1000_gstrings_stats[i].stat_offset;
+                       p = (char *)&net_stats +
+                           e1000_gstrings_stats[i].stat_offset;
                        break;
                case E1000_STATS:
-                       p = (char *) adapter +
-                                       e1000_gstrings_stats[i].stat_offset;
+                       p = (char *)adapter +
+                           e1000_gstrings_stats[i].stat_offset;
                        break;
                default:
                        data[i] = 0;
@@ -1993,7 +1999,7 @@ static void e1000_get_ethtool_stats(struct net_device *netdev,
                }
 
                data[i] = (e1000_gstrings_stats[i].sizeof_stat ==
-                       sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+                          sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
 }
 
@@ -2069,23 +2075,20 @@ static int e1000e_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       u16 cap_addr, adv_addr, lpa_addr, pcs_stat_addr, phy_data, lpi_ctrl;
-       u32 status, ret_val;
+       u16 cap_addr, lpa_addr, pcs_stat_addr, phy_data;
+       u32 ret_val;
 
-       if (!(adapter->flags & FLAG_IS_ICH) ||
-           !(adapter->flags2 & FLAG2_HAS_EEE))
+       if (!(adapter->flags2 & FLAG2_HAS_EEE))
                return -EOPNOTSUPP;
 
        switch (hw->phy.type) {
        case e1000_phy_82579:
                cap_addr = I82579_EEE_CAPABILITY;
-               adv_addr = I82579_EEE_ADVERTISEMENT;
                lpa_addr = I82579_EEE_LP_ABILITY;
                pcs_stat_addr = I82579_EEE_PCS_STATUS;
                break;
        case e1000_phy_i217:
                cap_addr = I217_EEE_CAPABILITY;
-               adv_addr = I217_EEE_ADVERTISEMENT;
                lpa_addr = I217_EEE_LP_ABILITY;
                pcs_stat_addr = I217_EEE_PCS_STATUS;
                break;
@@ -2104,10 +2107,7 @@ static int e1000e_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
        edata->supported = mmd_eee_cap_to_ethtool_sup_t(phy_data);
 
        /* EEE Advertised */
-       ret_val = e1000_read_emi_reg_locked(hw, adv_addr, &phy_data);
-       if (ret_val)
-               goto release;
-       edata->advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data);
+       edata->advertised = mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert);
 
        /* EEE Link Partner Advertised */
        ret_val = e1000_read_emi_reg_locked(hw, lpa_addr, &phy_data);
@@ -2125,25 +2125,11 @@ release:
        if (ret_val)
                return -ENODATA;
 
-       e1e_rphy(hw, I82579_LPI_CTRL, &lpi_ctrl);
-       status = er32(STATUS);
-
        /* Result of the EEE auto negotiation - there is no register that
         * has the status of the EEE negotiation so do a best-guess based
-        * on whether both Tx and Rx LPI indications have been received or
-        * base it on the link speed, the EEE advertised speeds on both ends
-        * and the speeds on which EEE is enabled locally.
+        * on whether Tx or Rx LPI indications have been received.
         */
-       if (((phy_data & E1000_EEE_TX_LPI_RCVD) &&
-            (phy_data & E1000_EEE_RX_LPI_RCVD)) ||
-           ((status & E1000_STATUS_SPEED_100) &&
-            (edata->advertised & ADVERTISED_100baseT_Full) &&
-            (edata->lp_advertised & ADVERTISED_100baseT_Full) &&
-            (lpi_ctrl & I82579_LPI_CTRL_100_ENABLE)) ||
-           ((status & E1000_STATUS_SPEED_1000) &&
-            (edata->advertised & ADVERTISED_1000baseT_Full) &&
-            (edata->lp_advertised & ADVERTISED_1000baseT_Full) &&
-            (lpi_ctrl & I82579_LPI_CTRL_1000_ENABLE)))
+       if (phy_data & (E1000_EEE_TX_LPI_RCVD | E1000_EEE_RX_LPI_RCVD))
                edata->eee_active = true;
 
        edata->eee_enabled = !hw->dev_spec.ich8lan.eee_disable;
@@ -2160,19 +2146,10 @@ static int e1000e_set_eee(struct net_device *netdev, struct ethtool_eee *edata)
        struct ethtool_eee eee_curr;
        s32 ret_val;
 
-       if (!(adapter->flags & FLAG_IS_ICH) ||
-           !(adapter->flags2 & FLAG2_HAS_EEE))
-               return -EOPNOTSUPP;
-
        ret_val = e1000e_get_eee(netdev, &eee_curr);
        if (ret_val)
                return ret_val;
 
-       if (eee_curr.advertised != edata->advertised) {
-               e_err("Setting EEE advertisement is not supported\n");
-               return -EINVAL;
-       }
-
        if (eee_curr.tx_lpi_enabled != edata->tx_lpi_enabled) {
                e_err("Setting EEE tx-lpi is not supported\n");
                return -EINVAL;
@@ -2183,16 +2160,21 @@ static int e1000e_set_eee(struct net_device *netdev, struct ethtool_eee *edata)
                return -EINVAL;
        }
 
-       if (hw->dev_spec.ich8lan.eee_disable != !edata->eee_enabled) {
-               hw->dev_spec.ich8lan.eee_disable = !edata->eee_enabled;
-
-               /* reset the link */
-               if (netif_running(netdev))
-                       e1000e_reinit_locked(adapter);
-               else
-                       e1000e_reset(adapter);
+       if (edata->advertised & ~(ADVERTISE_100_FULL | ADVERTISE_1000_FULL)) {
+               e_err("EEE advertisement supports only 100TX and/or 1000T full-duplex\n");
+               return -EINVAL;
        }
 
+       adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised);
+
+       hw->dev_spec.ich8lan.eee_disable = !edata->eee_enabled;
+
+       /* reset the link */
+       if (netif_running(netdev))
+               e1000e_reinit_locked(adapter);
+       else
+               e1000e_reset(adapter);
+
        return 0;
 }
 
index 1e6b889aee8778c27b8c9c5e999fd2baeef6f377..84850f7a23e4205650b9cef83f12be762a4feea9 100644 (file)
@@ -167,7 +167,7 @@ enum e1000_1000t_rx_status {
        e1000_1000t_rx_status_undefined = 0xFF
 };
 
-enum e1000_rev_polarity{
+enum e1000_rev_polarity {
        e1000_rev_polarity_normal = 0,
        e1000_rev_polarity_reversed,
        e1000_rev_polarity_undefined = 0xFF
@@ -545,7 +545,7 @@ struct e1000_mac_info {
        u16 mta_reg_count;
 
        /* Maximum size of the MTA register table in all supported adapters */
-       #define MAX_MTA_REG 128
+#define MAX_MTA_REG 128
        u32 mta_shadow[MAX_MTA_REG];
        u16 rar_entry_count;
 
index 121a865c7fbd12855b32b5dff2883332f3b9485b..ad9d8f2dd868778d1b4f8a1c2e3b767add06acd8 100644 (file)
 /* Offset 04h HSFSTS */
 union ich8_hws_flash_status {
        struct ich8_hsfsts {
-               u16 flcdone    :1; /* bit 0 Flash Cycle Done */
-               u16 flcerr     :1; /* bit 1 Flash Cycle Error */
-               u16 dael       :1; /* bit 2 Direct Access error Log */
-               u16 berasesz   :2; /* bit 4:3 Sector Erase Size */
-               u16 flcinprog  :1; /* bit 5 flash cycle in Progress */
-               u16 reserved1  :2; /* bit 13:6 Reserved */
-               u16 reserved2  :6; /* bit 13:6 Reserved */
-               u16 fldesvalid :1; /* bit 14 Flash Descriptor Valid */
-               u16 flockdn    :1; /* bit 15 Flash Config Lock-Down */
+               u16 flcdone:1;  /* bit 0 Flash Cycle Done */
+               u16 flcerr:1;   /* bit 1 Flash Cycle Error */
+               u16 dael:1;     /* bit 2 Direct Access error Log */
+               u16 berasesz:2; /* bit 4:3 Sector Erase Size */
+               u16 flcinprog:1;        /* bit 5 flash cycle in Progress */
+               u16 reserved1:2;        /* bit 13:6 Reserved */
+               u16 reserved2:6;        /* bit 13:6 Reserved */
+               u16 fldesvalid:1;       /* bit 14 Flash Descriptor Valid */
+               u16 flockdn:1;  /* bit 15 Flash Config Lock-Down */
        } hsf_status;
        u16 regval;
 };
@@ -78,11 +78,11 @@ union ich8_hws_flash_status {
 /* Offset 06h FLCTL */
 union ich8_hws_flash_ctrl {
        struct ich8_hsflctl {
-               u16 flcgo      :1;   /* 0 Flash Cycle Go */
-               u16 flcycle    :2;   /* 2:1 Flash Cycle */
-               u16 reserved   :5;   /* 7:3 Reserved  */
-               u16 fldbcount  :2;   /* 9:8 Flash Data Byte Count */
-               u16 flockdn    :6;   /* 15:10 Reserved */
+               u16 flcgo:1;    /* 0 Flash Cycle Go */
+               u16 flcycle:2;  /* 2:1 Flash Cycle */
+               u16 reserved:5; /* 7:3 Reserved  */
+               u16 fldbcount:2;        /* 9:8 Flash Data Byte Count */
+               u16 flockdn:6;  /* 15:10 Reserved */
        } hsf_ctrl;
        u16 regval;
 };
@@ -90,10 +90,10 @@ union ich8_hws_flash_ctrl {
 /* ICH Flash Region Access Permissions */
 union ich8_hws_flash_regacc {
        struct ich8_flracc {
-               u32 grra      :8; /* 0:7 GbE region Read Access */
-               u32 grwa      :8; /* 8:15 GbE region Write Access */
-               u32 gmrag     :8; /* 23:16 GbE Master Read Access Grant */
-               u32 gmwag     :8; /* 31:24 GbE Master Write Access Grant */
+               u32 grra:8;     /* 0:7 GbE region Read Access */
+               u32 grwa:8;     /* 8:15 GbE region Write Access */
+               u32 gmrag:8;    /* 23:16 GbE Master Read Access Grant */
+               u32 gmwag:8;    /* 31:24 GbE Master Write Access Grant */
        } hsf_flregacc;
        u16 regval;
 };
@@ -142,6 +142,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index);
 static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index);
 static s32 e1000_k1_workaround_lv(struct e1000_hw *hw);
 static void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate);
+static s32 e1000_setup_copper_link_pch_lpt(struct e1000_hw *hw);
 
 static inline u16 __er16flash(struct e1000_hw *hw, unsigned long reg)
 {
@@ -312,7 +313,7 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
                mac_reg &= ~E1000_CTRL_LANPHYPC_VALUE;
                ew32(CTRL, mac_reg);
                e1e_flush();
-               udelay(10);
+               usleep_range(10, 20);
                mac_reg &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
                ew32(CTRL, mac_reg);
                e1e_flush();
@@ -548,8 +549,8 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
        /* find total size of the NVM, then cut in half since the total
         * size represents two separate NVM banks.
         */
-       nvm->flash_bank_size = (sector_end_addr - sector_base_addr)
-                               << FLASH_SECTOR_ADDR_SHIFT;
+       nvm->flash_bank_size = ((sector_end_addr - sector_base_addr)
+                               << FLASH_SECTOR_ADDR_SHIFT);
        nvm->flash_bank_size /= 2;
        /* Adjust to word count */
        nvm->flash_bank_size /= sizeof(u16);
@@ -636,6 +637,8 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
        if (mac->type == e1000_pch_lpt) {
                mac->rar_entry_count = E1000_PCH_LPT_RAR_ENTRIES;
                mac->ops.rar_set = e1000_rar_set_pch_lpt;
+               mac->ops.setup_physical_interface =
+                   e1000_setup_copper_link_pch_lpt;
        }
 
        /* Enable PCS Lock-loss workaround for ICH8 */
@@ -692,7 +695,7 @@ s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data)
  *
  *  Assumes the SW/FW/HW Semaphore is already acquired.
  **/
-static s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data)
+s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data)
 {
        return __e1000_access_emi_reg_locked(hw, addr, &data, false);
 }
@@ -709,11 +712,22 @@ static s32 e1000_set_eee_pchlan(struct e1000_hw *hw)
 {
        struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
        s32 ret_val;
-       u16 lpi_ctrl;
+       u16 lpa, pcs_status, adv, adv_addr, lpi_ctrl, data;
 
-       if ((hw->phy.type != e1000_phy_82579) &&
-           (hw->phy.type != e1000_phy_i217))
+       switch (hw->phy.type) {
+       case e1000_phy_82579:
+               lpa = I82579_EEE_LP_ABILITY;
+               pcs_status = I82579_EEE_PCS_STATUS;
+               adv_addr = I82579_EEE_ADVERTISEMENT;
+               break;
+       case e1000_phy_i217:
+               lpa = I217_EEE_LP_ABILITY;
+               pcs_status = I217_EEE_PCS_STATUS;
+               adv_addr = I217_EEE_ADVERTISEMENT;
+               break;
+       default:
                return 0;
+       }
 
        ret_val = hw->phy.ops.acquire(hw);
        if (ret_val)
@@ -728,34 +742,24 @@ static s32 e1000_set_eee_pchlan(struct e1000_hw *hw)
 
        /* Enable EEE if not disabled by user */
        if (!dev_spec->eee_disable) {
-               u16 lpa, pcs_status, data;
-
                /* Save off link partner's EEE ability */
-               switch (hw->phy.type) {
-               case e1000_phy_82579:
-                       lpa = I82579_EEE_LP_ABILITY;
-                       pcs_status = I82579_EEE_PCS_STATUS;
-                       break;
-               case e1000_phy_i217:
-                       lpa = I217_EEE_LP_ABILITY;
-                       pcs_status = I217_EEE_PCS_STATUS;
-                       break;
-               default:
-                       ret_val = -E1000_ERR_PHY;
-                       goto release;
-               }
                ret_val = e1000_read_emi_reg_locked(hw, lpa,
                                                    &dev_spec->eee_lp_ability);
                if (ret_val)
                        goto release;
 
+               /* Read EEE advertisement */
+               ret_val = e1000_read_emi_reg_locked(hw, adv_addr, &adv);
+               if (ret_val)
+                       goto release;
+
                /* Enable EEE only for speeds in which the link partner is
-                * EEE capable.
+                * EEE capable and for which we advertise EEE.
                 */
-               if (dev_spec->eee_lp_ability & I82579_EEE_1000_SUPPORTED)
+               if (adv & dev_spec->eee_lp_ability & I82579_EEE_1000_SUPPORTED)
                        lpi_ctrl |= I82579_LPI_CTRL_1000_ENABLE;
 
-               if (dev_spec->eee_lp_ability & I82579_EEE_100_SUPPORTED) {
+               if (adv & dev_spec->eee_lp_ability & I82579_EEE_100_SUPPORTED) {
                        e1e_rphy_locked(hw, MII_LPA, &data);
                        if (data & LPA_100FULL)
                                lpi_ctrl |= I82579_LPI_CTRL_100_ENABLE;
@@ -767,13 +771,13 @@ static s32 e1000_set_eee_pchlan(struct e1000_hw *hw)
                                dev_spec->eee_lp_ability &=
                                    ~I82579_EEE_100_SUPPORTED;
                }
-
-               /* R/Clr IEEE MMD 3.1 bits 11:10 - Tx/Rx LPI Received */
-               ret_val = e1000_read_emi_reg_locked(hw, pcs_status, &data);
-               if (ret_val)
-                       goto release;
        }
 
+       /* R/Clr IEEE MMD 3.1 bits 11:10 - Tx/Rx LPI Received */
+       ret_val = e1000_read_emi_reg_locked(hw, pcs_status, &data);
+       if (ret_val)
+               goto release;
+
        ret_val = e1e_wphy_locked(hw, I82579_LPI_CTRL, lpi_ctrl);
 release:
        hw->phy.ops.release(hw);
@@ -834,6 +838,94 @@ release:
        return ret_val;
 }
 
+/**
+ *  e1000_platform_pm_pch_lpt - Set platform power management values
+ *  @hw: pointer to the HW structure
+ *  @link: bool indicating link status
+ *
+ *  Set the Latency Tolerance Reporting (LTR) values for the "PCIe-like"
+ *  GbE MAC in the Lynx Point PCH based on Rx buffer size and link speed
+ *  when link is up (which must not exceed the maximum latency supported
+ *  by the platform), otherwise specify there is no LTR requirement.
+ *  Unlike true-PCIe devices which set the LTR maximum snoop/no-snoop
+ *  latencies in the LTR Extended Capability Structure in the PCIe Extended
+ *  Capability register set, on this device LTR is set by writing the
+ *  equivalent snoop/no-snoop latencies in the LTRV register in the MAC and
+ *  set the SEND bit to send an Intel On-chip System Fabric sideband (IOSF-SB)
+ *  message to the PMC.
+ **/
+static s32 e1000_platform_pm_pch_lpt(struct e1000_hw *hw, bool link)
+{
+       u32 reg = link << (E1000_LTRV_REQ_SHIFT + E1000_LTRV_NOSNOOP_SHIFT) |
+           link << E1000_LTRV_REQ_SHIFT | E1000_LTRV_SEND;
+       u16 lat_enc = 0;        /* latency encoded */
+
+       if (link) {
+               u16 speed, duplex, scale = 0;
+               u16 max_snoop, max_nosnoop;
+               u16 max_ltr_enc;        /* max LTR latency encoded */
+               s64 lat_ns;     /* latency (ns) */
+               s64 value;
+               u32 rxa;
+
+               if (!hw->adapter->max_frame_size) {
+                       e_dbg("max_frame_size not set.\n");
+                       return -E1000_ERR_CONFIG;
+               }
+
+               hw->mac.ops.get_link_up_info(hw, &speed, &duplex);
+               if (!speed) {
+                       e_dbg("Speed not set.\n");
+                       return -E1000_ERR_CONFIG;
+               }
+
+               /* Rx Packet Buffer Allocation size (KB) */
+               rxa = er32(PBA) & E1000_PBA_RXA_MASK;
+
+               /* Determine the maximum latency tolerated by the device.
+                *
+                * Per the PCIe spec, the tolerated latencies are encoded as
+                * a 3-bit encoded scale (only 0-5 are valid) multiplied by
+                * a 10-bit value (0-1023) to provide a range from 1 ns to
+                * 2^25*(2^10-1) ns.  The scale is encoded as 0=2^0ns,
+                * 1=2^5ns, 2=2^10ns,...5=2^25ns.
+                */
+               lat_ns = ((s64)rxa * 1024 -
+                         (2 * (s64)hw->adapter->max_frame_size)) * 8 * 1000;
+               if (lat_ns < 0)
+                       lat_ns = 0;
+               else
+                       do_div(lat_ns, speed);
+
+               value = lat_ns;
+               while (value > PCI_LTR_VALUE_MASK) {
+                       scale++;
+                       value = DIV_ROUND_UP(value, (1 << 5));
+               }
+               if (scale > E1000_LTRV_SCALE_MAX) {
+                       e_dbg("Invalid LTR latency scale %d\n", scale);
+                       return -E1000_ERR_CONFIG;
+               }
+               lat_enc = (u16)((scale << PCI_LTR_SCALE_SHIFT) | value);
+
+               /* Determine the maximum latency tolerated by the platform */
+               pci_read_config_word(hw->adapter->pdev, E1000_PCI_LTR_CAP_LPT,
+                                    &max_snoop);
+               pci_read_config_word(hw->adapter->pdev,
+                                    E1000_PCI_LTR_CAP_LPT + 2, &max_nosnoop);
+               max_ltr_enc = max_t(u16, max_snoop, max_nosnoop);
+
+               if (lat_enc > max_ltr_enc)
+                       lat_enc = max_ltr_enc;
+       }
+
+       /* Set Snoop and No-Snoop latencies the same */
+       reg |= lat_enc | (lat_enc << E1000_LTRV_NOSNOOP_SHIFT);
+       ew32(LTRV, reg);
+
+       return 0;
+}
+
 /**
  *  e1000_check_for_copper_link_ich8lan - Check for link (Copper)
  *  @hw: pointer to the HW structure
@@ -871,6 +963,34 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
                        return ret_val;
        }
 
+       /* When connected at 10Mbps half-duplex, 82579 parts are excessively
+        * aggressive resulting in many collisions. To avoid this, increase
+        * the IPG and reduce Rx latency in the PHY.
+        */
+       if ((hw->mac.type == e1000_pch2lan) && link) {
+               u32 reg;
+               reg = er32(STATUS);
+               if (!(reg & (E1000_STATUS_FD | E1000_STATUS_SPEED_MASK))) {
+                       reg = er32(TIPG);
+                       reg &= ~E1000_TIPG_IPGT_MASK;
+                       reg |= 0xFF;
+                       ew32(TIPG, reg);
+
+                       /* Reduce Rx latency in analog PHY */
+                       ret_val = hw->phy.ops.acquire(hw);
+                       if (ret_val)
+                               return ret_val;
+
+                       ret_val =
+                           e1000_write_emi_reg_locked(hw, I82579_RX_CONFIG, 0);
+
+                       hw->phy.ops.release(hw);
+
+                       if (ret_val)
+                               return ret_val;
+               }
+       }
+
        /* Work-around I218 hang issue */
        if ((hw->adapter->pdev->device == E1000_DEV_ID_PCH_LPTLP_I218_LM) ||
            (hw->adapter->pdev->device == E1000_DEV_ID_PCH_LPTLP_I218_V)) {
@@ -879,6 +999,15 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
                        return ret_val;
        }
 
+       if (hw->mac.type == e1000_pch_lpt) {
+               /* Set platform power management values for
+                * Latency Tolerance Reporting (LTR)
+                */
+               ret_val = e1000_platform_pm_pch_lpt(hw, link);
+               if (ret_val)
+                       return ret_val;
+       }
+
        /* Clear link partner's EEE ability */
        hw->dev_spec.ich8lan.eee_lp_ability = 0;
 
@@ -1002,10 +1131,6 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
            (er32(FWSM) & E1000_ICH_FWSM_FW_VALID))
                adapter->flags2 |= FLAG2_PCIM2PCI_ARBITER_WA;
 
-       /* Disable EEE by default until IEEE802.3az spec is finalized */
-       if (adapter->flags2 & FLAG2_HAS_EEE)
-               adapter->hw.dev_spec.ich8lan.eee_disable = true;
-
        return 0;
 }
 
@@ -1134,9 +1259,9 @@ static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw)
        u32 fwsm;
 
        fwsm = er32(FWSM);
-       return (fwsm & E1000_ICH_FWSM_FW_VALID) &&
-              ((fwsm & E1000_FWSM_MODE_MASK) ==
-               (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT));
+       return ((fwsm & E1000_ICH_FWSM_FW_VALID) &&
+               ((fwsm & E1000_FWSM_MODE_MASK) ==
+                (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)));
 }
 
 /**
@@ -1153,7 +1278,7 @@ static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw)
 
        fwsm = er32(FWSM);
        return (fwsm & E1000_ICH_FWSM_FW_VALID) &&
-              (fwsm & (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT));
+           (fwsm & (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT));
 }
 
 /**
@@ -1440,8 +1565,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
        word_addr = (u16)(cnf_base_addr << 1);
 
        for (i = 0; i < cnf_size; i++) {
-               ret_val = e1000_read_nvm(hw, (word_addr + i * 2), 1,
-                                        &reg_data);
+               ret_val = e1000_read_nvm(hw, (word_addr + i * 2), 1, &reg_data);
                if (ret_val)
                        goto release;
 
@@ -1501,13 +1625,13 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
                        if (ret_val)
                                goto release;
 
-                       status_reg &= BM_CS_STATUS_LINK_UP |
-                                     BM_CS_STATUS_RESOLVED |
-                                     BM_CS_STATUS_SPEED_MASK;
+                       status_reg &= (BM_CS_STATUS_LINK_UP |
+                                      BM_CS_STATUS_RESOLVED |
+                                      BM_CS_STATUS_SPEED_MASK);
 
                        if (status_reg == (BM_CS_STATUS_LINK_UP |
-                                          BM_CS_STATUS_RESOLVED |
-                                          BM_CS_STATUS_SPEED_1000))
+                                          BM_CS_STATUS_RESOLVED |
+                                          BM_CS_STATUS_SPEED_1000))
                                k1_enable = false;
                }
 
@@ -1516,13 +1640,13 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
                        if (ret_val)
                                goto release;
 
-                       status_reg &= HV_M_STATUS_LINK_UP |
-                                     HV_M_STATUS_AUTONEG_COMPLETE |
-                                     HV_M_STATUS_SPEED_MASK;
+                       status_reg &= (HV_M_STATUS_LINK_UP |
+                                      HV_M_STATUS_AUTONEG_COMPLETE |
+                                      HV_M_STATUS_SPEED_MASK);
 
                        if (status_reg == (HV_M_STATUS_LINK_UP |
-                                          HV_M_STATUS_AUTONEG_COMPLETE |
-                                          HV_M_STATUS_SPEED_1000))
+                                          HV_M_STATUS_AUTONEG_COMPLETE |
+                                          HV_M_STATUS_SPEED_1000))
                                k1_enable = false;
                }
 
@@ -1579,7 +1703,7 @@ s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
        if (ret_val)
                return ret_val;
 
-       udelay(20);
+       usleep_range(20, 40);
        ctrl_ext = er32(CTRL_EXT);
        ctrl_reg = er32(CTRL);
 
@@ -1589,11 +1713,11 @@ s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
 
        ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS);
        e1e_flush();
-       udelay(20);
+       usleep_range(20, 40);
        ew32(CTRL, ctrl_reg);
        ew32(CTRL_EXT, ctrl_ext);
        e1e_flush();
-       udelay(20);
+       usleep_range(20, 40);
 
        return 0;
 }
@@ -1667,7 +1791,6 @@ release:
        return ret_val;
 }
 
-
 /**
  *  e1000_set_mdio_slow_mode_hv - Set slow MDIO access mode
  *  @hw:   pointer to the HW structure
@@ -1834,7 +1957,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
                 * SHRAL/H) and initial CRC values to the MAC
                 */
                for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) {
-                       u8 mac_addr[ETH_ALEN] = {0};
+                       u8 mac_addr[ETH_ALEN] = { 0 };
                        u32 addr_high, addr_low;
 
                        addr_high = er32(RAH(i));
@@ -1865,8 +1988,8 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
                ew32(RCTL, mac_reg);
 
                ret_val = e1000e_read_kmrn_reg(hw,
-                                               E1000_KMRNCTRLSTA_CTRL_OFFSET,
-                                               &data);
+                                              E1000_KMRNCTRLSTA_CTRL_OFFSET,
+                                              &data);
                if (ret_val)
                        return ret_val;
                ret_val = e1000e_write_kmrn_reg(hw,
@@ -1875,8 +1998,8 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
                if (ret_val)
                        return ret_val;
                ret_val = e1000e_read_kmrn_reg(hw,
-                                               E1000_KMRNCTRLSTA_HD_CTRL,
-                                               &data);
+                                              E1000_KMRNCTRLSTA_HD_CTRL,
+                                              &data);
                if (ret_val)
                        return ret_val;
                data &= ~(0xF << 8);
@@ -1923,8 +2046,8 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
                ew32(RCTL, mac_reg);
 
                ret_val = e1000e_read_kmrn_reg(hw,
-                                               E1000_KMRNCTRLSTA_CTRL_OFFSET,
-                                               &data);
+                                              E1000_KMRNCTRLSTA_CTRL_OFFSET,
+                                              &data);
                if (ret_val)
                        return ret_val;
                ret_val = e1000e_write_kmrn_reg(hw,
@@ -1933,8 +2056,8 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
                if (ret_val)
                        return ret_val;
                ret_val = e1000e_read_kmrn_reg(hw,
-                                               E1000_KMRNCTRLSTA_HD_CTRL,
-                                               &data);
+                                              E1000_KMRNCTRLSTA_HD_CTRL,
+                                              &data);
                if (ret_val)
                        return ret_val;
                data &= ~(0xF << 8);
@@ -2100,7 +2223,7 @@ static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw)
        do {
                data = er32(STATUS);
                data &= E1000_STATUS_LAN_INIT_DONE;
-               udelay(100);
+               usleep_range(100, 200);
        } while ((!data) && --loop);
 
        /* If basic configuration is incomplete before the above loop
@@ -2445,7 +2568,7 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
 
                /* Check bank 0 */
                ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset,
-                                                       &sig_byte);
+                                                       &sig_byte);
                if (ret_val)
                        return ret_val;
                if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
@@ -2456,8 +2579,8 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
 
                /* Check bank 1 */
                ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset +
-                                                       bank1_offset,
-                                                       &sig_byte);
+                                                       bank1_offset,
+                                                       &sig_byte);
                if (ret_val)
                        return ret_val;
                if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
@@ -2510,8 +2633,8 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
 
        ret_val = 0;
        for (i = 0; i < words; i++) {
-               if (dev_spec->shadow_ram[offset+i].modified) {
-                       data[i] = dev_spec->shadow_ram[offset+i].value;
+               if (dev_spec->shadow_ram[offset + i].modified) {
+                       data[i] = dev_spec->shadow_ram[offset + i].value;
                } else {
                        ret_val = e1000_read_flash_word_ich8lan(hw,
                                                                act_offset + i,
@@ -2696,8 +2819,8 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
        if (size < 1  || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK)
                return -E1000_ERR_NVM;
 
-       flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & offset) +
-                           hw->nvm.flash_base_addr;
+       flash_linear_addr = ((ICH_FLASH_LINEAR_ADDR_MASK & offset) +
+                            hw->nvm.flash_base_addr);
 
        do {
                udelay(1);
@@ -2714,8 +2837,9 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
 
                ew32flash(ICH_FLASH_FADDR, flash_linear_addr);
 
-               ret_val = e1000_flash_cycle_ich8lan(hw,
-                                               ICH_FLASH_READ_COMMAND_TIMEOUT);
+               ret_val =
+                   e1000_flash_cycle_ich8lan(hw,
+                                             ICH_FLASH_READ_COMMAND_TIMEOUT);
 
                /* Check if FCERR is set to 1, if set to 1, clear it
                 * and try the whole sequence a few more times, else
@@ -2774,8 +2898,8 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
        nvm->ops.acquire(hw);
 
        for (i = 0; i < words; i++) {
-               dev_spec->shadow_ram[offset+i].modified = true;
-               dev_spec->shadow_ram[offset+i].value = data[i];
+               dev_spec->shadow_ram[offset + i].modified = true;
+               dev_spec->shadow_ram[offset + i].value = data[i];
        }
 
        nvm->ops.release(hw);
@@ -2844,8 +2968,8 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
                        data = dev_spec->shadow_ram[i].value;
                } else {
                        ret_val = e1000_read_flash_word_ich8lan(hw, i +
-                                                               old_bank_offset,
-                                                               &data);
+                                                               old_bank_offset,
+                                                               &data);
                        if (ret_val)
                                break;
                }
@@ -2863,7 +2987,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
                /* Convert offset to bytes. */
                act_offset = (i + new_bank_offset) << 1;
 
-               udelay(100);
+               usleep_range(100, 200);
                /* Write the bytes to the new bank. */
                ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
                                                               act_offset,
@@ -2871,10 +2995,10 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
                if (ret_val)
                        break;
 
-               udelay(100);
+               usleep_range(100, 200);
                ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
-                                                         act_offset + 1,
-                                                         (u8)(data >> 8));
+                                                              act_offset + 1,
+                                                              (u8)(data >> 8));
                if (ret_val)
                        break;
        }
@@ -3050,8 +3174,8 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
            offset > ICH_FLASH_LINEAR_ADDR_MASK)
                return -E1000_ERR_NVM;
 
-       flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & offset) +
-                           hw->nvm.flash_base_addr;
+       flash_linear_addr = ((ICH_FLASH_LINEAR_ADDR_MASK & offset) +
+                            hw->nvm.flash_base_addr);
 
        do {
                udelay(1);
@@ -3062,7 +3186,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
 
                hsflctl.regval = er16flash(ICH_FLASH_HSFCTL);
                /* 0b/1b corresponds to 1 or 2 byte size, respectively. */
-               hsflctl.hsf_ctrl.fldbcount = size -1;
+               hsflctl.hsf_ctrl.fldbcount = size - 1;
                hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_WRITE;
                ew16flash(ICH_FLASH_HSFCTL, hsflctl.regval);
 
@@ -3078,8 +3202,9 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
                /* check if FCERR is set to 1 , if set to 1, clear it
                 * and try the whole sequence a few more times else done
                 */
-               ret_val = e1000_flash_cycle_ich8lan(hw,
-                                              ICH_FLASH_WRITE_COMMAND_TIMEOUT);
+               ret_val =
+                   e1000_flash_cycle_ich8lan(hw,
+                                             ICH_FLASH_WRITE_COMMAND_TIMEOUT);
                if (!ret_val)
                        break;
 
@@ -3138,7 +3263,7 @@ static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw,
 
        for (program_retries = 0; program_retries < 100; program_retries++) {
                e_dbg("Retrying Byte %2.2X at offset %u\n", byte, offset);
-               udelay(100);
+               usleep_range(100, 200);
                ret_val = e1000_write_flash_byte_ich8lan(hw, offset, byte);
                if (!ret_val)
                        break;
@@ -3209,8 +3334,10 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
        flash_linear_addr = hw->nvm.flash_base_addr;
        flash_linear_addr += (bank) ? flash_bank_size : 0;
 
-       for (j = 0; j < iteration ; j++) {
+       for (j = 0; j < iteration; j++) {
                do {
+                       u32 timeout = ICH_FLASH_ERASE_COMMAND_TIMEOUT;
+
                        /* Steps */
                        ret_val = e1000_flash_cycle_init_ich8lan(hw);
                        if (ret_val)
@@ -3230,8 +3357,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
                        flash_linear_addr += (j * sector_size);
                        ew32flash(ICH_FLASH_FADDR, flash_linear_addr);
 
-                       ret_val = e1000_flash_cycle_ich8lan(hw,
-                                              ICH_FLASH_ERASE_COMMAND_TIMEOUT);
+                       ret_val = e1000_flash_cycle_ich8lan(hw, timeout);
                        if (!ret_val)
                                break;
 
@@ -3270,8 +3396,7 @@ static s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw, u16 *data)
                return ret_val;
        }
 
-       if (*data == ID_LED_RESERVED_0000 ||
-           *data == ID_LED_RESERVED_FFFF)
+       if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF)
                *data = ID_LED_DEFAULT_ICH8LAN;
 
        return 0;
@@ -3511,9 +3636,9 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
 
        /* Initialize identification LED */
        ret_val = mac->ops.id_led_init(hw);
+       /* An error is not fatal and we should not stop init due to this */
        if (ret_val)
                e_dbg("Error initializing identification LED\n");
-               /* This is not fatal and we should not stop init due to this */
 
        /* Setup the receive address. */
        e1000e_init_rx_addrs(hw, mac->rar_entry_count);
@@ -3541,16 +3666,16 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
 
        /* Set the transmit descriptor write-back policy for both queues */
        txdctl = er32(TXDCTL(0));
-       txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) |
-                E1000_TXDCTL_FULL_TX_DESC_WB;
-       txdctl = (txdctl & ~E1000_TXDCTL_PTHRESH) |
-                E1000_TXDCTL_MAX_TX_DESC_PREFETCH;
+       txdctl = ((txdctl & ~E1000_TXDCTL_WTHRESH) |
+                 E1000_TXDCTL_FULL_TX_DESC_WB);
+       txdctl = ((txdctl & ~E1000_TXDCTL_PTHRESH) |
+                 E1000_TXDCTL_MAX_TX_DESC_PREFETCH);
        ew32(TXDCTL(0), txdctl);
        txdctl = er32(TXDCTL(1));
-       txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) |
-                E1000_TXDCTL_FULL_TX_DESC_WB;
-       txdctl = (txdctl & ~E1000_TXDCTL_PTHRESH) |
-                E1000_TXDCTL_MAX_TX_DESC_PREFETCH;
+       txdctl = ((txdctl & ~E1000_TXDCTL_WTHRESH) |
+                 E1000_TXDCTL_FULL_TX_DESC_WB);
+       txdctl = ((txdctl & ~E1000_TXDCTL_PTHRESH) |
+                 E1000_TXDCTL_MAX_TX_DESC_PREFETCH);
        ew32(TXDCTL(1), txdctl);
 
        /* ICH8 has opposite polarity of no_snoop bits.
@@ -3559,7 +3684,7 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
        if (mac->type == e1000_ich8lan)
                snoop = PCIE_ICH8_SNOOP_ALL;
        else
-               snoop = (u32) ~(PCIE_NO_SNOOP_ALL);
+               snoop = (u32)~(PCIE_NO_SNOOP_ALL);
        e1000e_set_pcie_no_snoop(hw, snoop);
 
        ctrl_ext = er32(CTRL_EXT);
@@ -3575,6 +3700,7 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
 
        return ret_val;
 }
+
 /**
  *  e1000_initialize_hw_bits_ich8lan - Initialize required hardware bits
  *  @hw: pointer to the HW structure
@@ -3686,8 +3812,7 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw)
         */
        hw->fc.current_mode = hw->fc.requested_mode;
 
-       e_dbg("After fix-ups FlowControl is now = %x\n",
-               hw->fc.current_mode);
+       e_dbg("After fix-ups FlowControl is now = %x\n", hw->fc.current_mode);
 
        /* Continue to configure the copper link. */
        ret_val = hw->mac.ops.setup_physical_interface(hw);
@@ -3737,12 +3862,12 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
        if (ret_val)
                return ret_val;
        ret_val = e1000e_read_kmrn_reg(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
-                                      &reg_data);
+                                      &reg_data);
        if (ret_val)
                return ret_val;
        reg_data |= 0x3F;
        ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
-                                       reg_data);
+                                       reg_data);
        if (ret_val)
                return ret_val;
 
@@ -3760,7 +3885,6 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
                break;
        case e1000_phy_82577:
        case e1000_phy_82579:
-       case e1000_phy_i217:
                ret_val = e1000_copper_link_setup_82577(hw);
                if (ret_val)
                        return ret_val;
@@ -3795,6 +3919,31 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
        return e1000e_setup_copper_link(hw);
 }
 
+/**
+ *  e1000_setup_copper_link_pch_lpt - Configure MAC/PHY interface
+ *  @hw: pointer to the HW structure
+ *
+ *  Calls the PHY specific link setup function and then calls the
+ *  generic setup_copper_link to finish configuring the link for
+ *  Lynxpoint PCH devices
+ **/
+static s32 e1000_setup_copper_link_pch_lpt(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val;
+
+       ctrl = er32(CTRL);
+       ctrl |= E1000_CTRL_SLU;
+       ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+       ew32(CTRL, ctrl);
+
+       ret_val = e1000_copper_link_setup_82577(hw);
+       if (ret_val)
+               return ret_val;
+
+       return e1000e_setup_copper_link(hw);
+}
+
 /**
  *  e1000_get_link_up_info_ich8lan - Get current link speed and duplex
  *  @hw: pointer to the HW structure
@@ -3815,8 +3964,7 @@ static s32 e1000_get_link_up_info_ich8lan(struct e1000_hw *hw, u16 *speed,
                return ret_val;
 
        if ((hw->mac.type == e1000_ich8lan) &&
-           (hw->phy.type == e1000_phy_igp_3) &&
-           (*speed == SPEED_1000)) {
+           (hw->phy.type == e1000_phy_igp_3) && (*speed == SPEED_1000)) {
                ret_val = e1000_kmrn_lock_loss_workaround_ich8lan(hw);
        }
 
@@ -3899,7 +4047,7 @@ static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw)
  *  /disabled - false).
  **/
 void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
-                                                bool state)
+                                                 bool state)
 {
        struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
 
@@ -3981,12 +4129,12 @@ void e1000e_gig_downshift_workaround_ich8lan(struct e1000_hw *hw)
                return;
 
        ret_val = e1000e_read_kmrn_reg(hw, E1000_KMRNCTRLSTA_DIAG_OFFSET,
-                                     &reg_data);
+                                      &reg_data);
        if (ret_val)
                return;
        reg_data |= E1000_KMRNCTRLSTA_DIAG_NELPBK;
        ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_DIAG_OFFSET,
-                                      reg_data);
+                                       reg_data);
        if (ret_val)
                return;
        reg_data &= ~E1000_KMRNCTRLSTA_DIAG_NELPBK;
index 8bf4655c2e17f25a26b70bb6c0ad268034bf8c04..80034a2b297c1c17f39f5f66d6dbacfab512e5eb 100644 (file)
 #define I82579_MSE_THRESHOLD   0x084F  /* 82579 Mean Square Error Threshold */
 #define I82577_MSE_THRESHOLD   0x0887  /* 82577 Mean Square Error Threshold */
 #define I82579_MSE_LINK_DOWN   0x2411  /* MSE count before dropping link */
-#define I82579_EEE_PCS_STATUS          0x182D  /* IEEE MMD Register 3.1 >> 8 */
+#define I82579_RX_CONFIG               0x3412  /* Receive configuration */
+#define I82579_EEE_PCS_STATUS          0x182E  /* IEEE MMD Register 3.1 >> 8 */
 #define I82579_EEE_CAPABILITY          0x0410  /* IEEE MMD Register 3.20 */
 #define I82579_EEE_ADVERTISEMENT       0x040E  /* IEEE MMD Register 7.60 */
 #define I82579_EEE_LP_ABILITY          0x040F  /* IEEE MMD Register 7.61 */
 /* Proprietary Latency Tolerance Reporting PCI Capability */
 #define E1000_PCI_LTR_CAP_LPT          0xA8
 
-/* OBFF Control & Threshold Defines */
-#define E1000_SVCR_OFF_EN              0x00000001
-#define E1000_SVCR_OFF_MASKINT         0x00001000
-#define E1000_SVCR_OFF_TIMER_MASK      0xFFFF0000
-#define E1000_SVCR_OFF_TIMER_SHIFT     16
-#define E1000_SVT_OFF_HWM_MASK         0x0000001F
-
 void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw);
 void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
                                                  bool state);
@@ -267,4 +261,5 @@ s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable);
 void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw);
 s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable);
 s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data);
+s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data);
 #endif /* _E1000E_ICH8LAN_H_ */
index b78e02174601a5f25a444c964eece95cf50fb6e6..2480c1091873864991d9342b6ce15df61b07c000 100644 (file)
@@ -596,7 +596,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw)
                 * serdes media type.
                 */
                /* SYNCH bit and IV bit are sticky. */
-               udelay(10);
+               usleep_range(10, 20);
                rxcw = er32(RXCW);
                if (rxcw & E1000_RXCW_SYNCH) {
                        if (!(rxcw & E1000_RXCW_IV)) {
@@ -613,7 +613,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw)
                status = er32(STATUS);
                if (status & E1000_STATUS_LU) {
                        /* SYNCH bit and IV bit are sticky, so reread rxcw. */
-                       udelay(10);
+                       usleep_range(10, 20);
                        rxcw = er32(RXCW);
                        if (rxcw & E1000_RXCW_SYNCH) {
                                if (!(rxcw & E1000_RXCW_IV)) {
@@ -1382,7 +1382,7 @@ s32 e1000e_get_hw_semaphore(struct e1000_hw *hw)
                if (!(swsm & E1000_SWSM_SMBI))
                        break;
 
-               udelay(50);
+               usleep_range(50, 100);
                i++;
        }
 
@@ -1400,7 +1400,7 @@ s32 e1000e_get_hw_semaphore(struct e1000_hw *hw)
                if (er32(SWSM) & E1000_SWSM_SWESMBI)
                        break;
 
-               udelay(50);
+               usleep_range(50, 100);
        }
 
        if (i == timeout) {
@@ -1600,15 +1600,28 @@ s32 e1000e_blink_led_generic(struct e1000_hw *hw)
                ledctl_blink = E1000_LEDCTL_LED0_BLINK |
                    (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT);
        } else {
-               /* set the blink bit for each LED that's "on" (0x0E)
-                * in ledctl_mode2
+               /* Set the blink bit for each LED that's "on" (0x0E)
+                * (or "off" if inverted) in ledctl_mode2.  The blink
+                * logic in hardware only works when mode is set to "on"
+                * so it must be changed accordingly when the mode is
+                * "off" and inverted.
                 */
                ledctl_blink = hw->mac.ledctl_mode2;
-               for (i = 0; i < 4; i++)
-                       if (((hw->mac.ledctl_mode2 >> (i * 8)) & 0xFF) ==
-                           E1000_LEDCTL_MODE_LED_ON)
-                               ledctl_blink |= (E1000_LEDCTL_LED0_BLINK <<
-                                                (i * 8));
+               for (i = 0; i < 32; i += 8) {
+                       u32 mode = (hw->mac.ledctl_mode2 >> i) &
+                           E1000_LEDCTL_LED0_MODE_MASK;
+                       u32 led_default = hw->mac.ledctl_default >> i;
+
+                       if ((!(led_default & E1000_LEDCTL_LED0_IVRT) &&
+                            (mode == E1000_LEDCTL_MODE_LED_ON)) ||
+                           ((led_default & E1000_LEDCTL_LED0_IVRT) &&
+                            (mode == E1000_LEDCTL_MODE_LED_OFF))) {
+                               ledctl_blink &=
+                                   ~(E1000_LEDCTL_LED0_MODE_MASK << i);
+                               ledctl_blink |= (E1000_LEDCTL_LED0_BLINK |
+                                                E1000_LEDCTL_MODE_LED_ON) << i;
+                       }
+               }
        }
 
        ew32(LEDCTL, ledctl_blink);
@@ -1712,7 +1725,7 @@ s32 e1000e_disable_pcie_master(struct e1000_hw *hw)
        while (timeout) {
                if (!(er32(STATUS) & E1000_STATUS_GIO_MASTER_ENABLE))
                        break;
-               udelay(100);
+               usleep_range(100, 200);
                timeout--;
        }
 
index 7e615e2bf7e6a4e96971945d33e3e332301e88bb..da7f2fad5ba4815672dff09d3d78af6bccd82393 100644 (file)
@@ -55,7 +55,7 @@
 
 #define DRV_EXTRAVERSION "-k"
 
-#define DRV_VERSION "2.2.14" DRV_EXTRAVERSION
+#define DRV_VERSION "2.3.2" DRV_EXTRAVERSION
 char e1000e_driver_name[] = "e1000e";
 const char e1000e_driver_version[] = DRV_VERSION;
 
@@ -219,9 +219,8 @@ static void e1000e_dump(struct e1000_adapter *adapter)
        if (netdev) {
                dev_info(&adapter->pdev->dev, "Net device Info\n");
                pr_info("Device Name     state            trans_start      last_rx\n");
-               pr_info("%-15s %016lX %016lX %016lX\n",
-                       netdev->name, netdev->state, netdev->trans_start,
-                       netdev->last_rx);
+               pr_info("%-15s %016lX %016lX %016lX\n", netdev->name,
+                       netdev->state, netdev->trans_start, netdev->last_rx);
        }
 
        /* Print Registers */
@@ -555,7 +554,7 @@ static void e1000_receive_skb(struct e1000_adapter *adapter,
        skb->protocol = eth_type_trans(skb, netdev);
 
        if (staterr & E1000_RXD_STAT_VP)
-               __vlan_hwaccel_put_tag(skb, tag);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tag);
 
        napi_gro_receive(&adapter->napi, skb);
 }
@@ -755,8 +754,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_ring *rx_ring,
                            cpu_to_le64(ps_page->dma);
                }
 
-               skb = __netdev_alloc_skb_ip_align(netdev,
-                                                 adapter->rx_ps_bsize0,
+               skb = __netdev_alloc_skb_ip_align(netdev, adapter->rx_ps_bsize0,
                                                  gfp);
 
                if (!skb) {
@@ -850,8 +848,8 @@ check_page:
 
                if (!buffer_info->dma) {
                        buffer_info->dma = dma_map_page(&pdev->dev,
-                                                       buffer_info->page, 0,
-                                                       PAGE_SIZE,
+                                                       buffer_info->page, 0,
+                                                       PAGE_SIZE,
                                                        DMA_FROM_DEVICE);
                        if (dma_mapping_error(&pdev->dev, buffer_info->dma)) {
                                adapter->alloc_rx_buff_failed++;
@@ -942,10 +940,8 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done,
 
                cleaned = true;
                cleaned_count++;
-               dma_unmap_single(&pdev->dev,
-                                buffer_info->dma,
-                                adapter->rx_buffer_len,
-                                DMA_FROM_DEVICE);
+               dma_unmap_single(&pdev->dev, buffer_info->dma,
+                                adapter->rx_buffer_len, DMA_FROM_DEVICE);
                buffer_info->dma = 0;
 
                length = le16_to_cpu(rx_desc->wb.upper.length);
@@ -1073,8 +1069,8 @@ static void e1000_put_txbuf(struct e1000_ring *tx_ring,
 static void e1000_print_hw_hang(struct work_struct *work)
 {
        struct e1000_adapter *adapter = container_of(work,
-                                                    struct e1000_adapter,
-                                                    print_hang_task);
+                                                    struct e1000_adapter,
+                                                    print_hang_task);
        struct net_device *netdev = adapter->netdev;
        struct e1000_ring *tx_ring = adapter->tx_ring;
        unsigned int i = tx_ring->next_to_clean;
@@ -1087,8 +1083,7 @@ static void e1000_print_hw_hang(struct work_struct *work)
        if (test_bit(__E1000_DOWN, &adapter->state))
                return;
 
-       if (!adapter->tx_hang_recheck &&
-           (adapter->flags2 & FLAG2_DMA_BURST)) {
+       if (!adapter->tx_hang_recheck && (adapter->flags2 & FLAG2_DMA_BURST)) {
                /* May be block on write-back, flush and detect again
                 * flush pending descriptor writebacks to memory
                 */
@@ -1130,19 +1125,10 @@ static void e1000_print_hw_hang(struct work_struct *work)
              "PHY 1000BASE-T Status  <%x>\n"
              "PHY Extended Status    <%x>\n"
              "PCI Status             <%x>\n",
-             readl(tx_ring->head),
-             readl(tx_ring->tail),
-             tx_ring->next_to_use,
-             tx_ring->next_to_clean,
-             tx_ring->buffer_info[eop].time_stamp,
-             eop,
-             jiffies,
-             eop_desc->upper.fields.status,
-             er32(STATUS),
-             phy_status,
-             phy_1000t_status,
-             phy_ext_status,
-             pci_status);
+             readl(tx_ring->head), readl(tx_ring->tail), tx_ring->next_to_use,
+             tx_ring->next_to_clean, tx_ring->buffer_info[eop].time_stamp,
+             eop, jiffies, eop_desc->upper.fields.status, er32(STATUS),
+             phy_status, phy_1000t_status, phy_ext_status, pci_status);
 
        /* Suggest workaround for known h/w issue */
        if ((hw->mac.type == e1000_pchlan) && (er32(CTRL) & E1000_CTRL_TFCE))
@@ -1435,7 +1421,7 @@ copydone:
                e1000_rx_hash(netdev, rx_desc->wb.lower.hi_dword.rss, skb);
 
                if (rx_desc->wb.upper.header_status &
-                          cpu_to_le16(E1000_RXDPS_HDRSTAT_HDRSP))
+                   cpu_to_le16(E1000_RXDPS_HDRSTAT_HDRSP))
                        adapter->rx_hdr_split++;
 
                e1000_receive_skb(adapter, netdev, skb, staterr,
@@ -1473,7 +1459,7 @@ next_desc:
  * e1000_consume_page - helper function
  **/
 static void e1000_consume_page(struct e1000_buffer *bi, struct sk_buff *skb,
-                               u16 length)
+                              u16 length)
 {
        bi->page = NULL;
        skb->len += length;
@@ -1500,7 +1486,8 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done,
        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;
+       struct skb_shared_info *shinfo;
 
        i = rx_ring->next_to_clean;
        rx_desc = E1000_RX_DESC_EXT(*rx_ring, i);
@@ -1546,7 +1533,6 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done,
                        rx_ring->rx_skb_top = NULL;
                        goto next_desc;
                }
-
 #define rxtop (rx_ring->rx_skb_top)
                if (!(staterr & E1000_RXD_STAT_EOP)) {
                        /* this descriptor is only the beginning (or middle) */
@@ -1554,12 +1540,13 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done,
                                /* this is the beginning of a chain */
                                rxtop = skb;
                                skb_fill_page_desc(rxtop, 0, buffer_info->page,
-                                                  0, length);
+                                                  0, length);
                        } else {
                                /* this is the middle of a chain */
-                               skb_fill_page_desc(rxtop,
-                                   skb_shinfo(rxtop)->nr_frags,
-                                   buffer_info->page, 0, length);
+                               shinfo = skb_shinfo(rxtop);
+                               skb_fill_page_desc(rxtop, shinfo->nr_frags,
+                                                  buffer_info->page, 0,
+                                                  length);
                                /* re-use the skb, only consumed the page */
                                buffer_info->skb = skb;
                        }
@@ -1568,9 +1555,10 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done,
                } else {
                        if (rxtop) {
                                /* end of the chain */
-                               skb_fill_page_desc(rxtop,
-                                   skb_shinfo(rxtop)->nr_frags,
-                                   buffer_info->page, 0, length);
+                               shinfo = skb_shinfo(rxtop);
+                               skb_fill_page_desc(rxtop, shinfo->nr_frags,
+                                                  buffer_info->page, 0,
+                                                  length);
                                /* re-use the current skb, we only consumed the
                                 * page
                                 */
@@ -1595,10 +1583,10 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done,
                                        skb_put(skb, length);
                                } else {
                                        skb_fill_page_desc(skb, 0,
-                                                          buffer_info->page, 0,
-                                                          length);
+                                                          buffer_info->page, 0,
+                                                          length);
                                        e1000_consume_page(buffer_info, skb,
-                                                          length);
+                                                          length);
                                }
                        }
                }
@@ -1671,8 +1659,7 @@ static void e1000_clean_rx_ring(struct e1000_ring *rx_ring)
                                                 DMA_FROM_DEVICE);
                        else if (adapter->clean_rx == e1000_clean_jumbo_rx_irq)
                                dma_unmap_page(&pdev->dev, buffer_info->dma,
-                                              PAGE_SIZE,
-                                              DMA_FROM_DEVICE);
+                                              PAGE_SIZE, DMA_FROM_DEVICE);
                        else if (adapter->clean_rx == e1000_clean_rx_irq_ps)
                                dma_unmap_single(&pdev->dev, buffer_info->dma,
                                                 adapter->rx_ps_bsize0,
@@ -1725,7 +1712,8 @@ static void e1000_clean_rx_ring(struct e1000_ring *rx_ring)
 static void e1000e_downshift_workaround(struct work_struct *work)
 {
        struct e1000_adapter *adapter = container_of(work,
-                                       struct e1000_adapter, downshift_task);
+                                                    struct e1000_adapter,
+                                                    downshift_task);
 
        if (test_bit(__E1000_DOWN, &adapter->state))
                return;
@@ -1918,7 +1906,6 @@ static irqreturn_t e1000_intr_msix_tx(int __always_unused irq, void *data)
        struct e1000_hw *hw = &adapter->hw;
        struct e1000_ring *tx_ring = adapter->tx_ring;
 
-
        adapter->total_tx_bytes = 0;
        adapter->total_tx_packets = 0;
 
@@ -1975,7 +1962,6 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)
                ew32(RFCTL, rfctl);
        }
 
-#define E1000_IVAR_INT_ALLOC_VALID     0x8
        /* Configure Rx vector */
        rx_ring->ims_val = E1000_IMS_RXQ0;
        adapter->eiac_mask |= rx_ring->ims_val;
@@ -2050,8 +2036,9 @@ void e1000e_set_interrupt_capability(struct e1000_adapter *adapter)
                if (adapter->flags & FLAG_HAS_MSIX) {
                        adapter->num_vectors = 3; /* RxQ0, TxQ0 and other */
                        adapter->msix_entries = kcalloc(adapter->num_vectors,
-                                                     sizeof(struct msix_entry),
-                                                     GFP_KERNEL);
+                                                       sizeof(struct
+                                                              msix_entry),
+                                                       GFP_KERNEL);
                        if (adapter->msix_entries) {
                                for (i = 0; i < adapter->num_vectors; i++)
                                        adapter->msix_entries[i].entry = i;
@@ -2495,7 +2482,7 @@ static unsigned int e1000_update_itr(u16 itr_setting, int packets, int bytes)
        switch (itr_setting) {
        case lowest_latency:
                /* handle TSO and jumbo frames */
-               if (bytes/packets > 8000)
+               if (bytes / packets > 8000)
                        retval = bulk_latency;
                else if ((packets < 5) && (bytes > 512))
                        retval = low_latency;
@@ -2503,13 +2490,13 @@ static unsigned int e1000_update_itr(u16 itr_setting, int packets, int bytes)
        case low_latency:  /* 50 usec aka 20000 ints/s */
                if (bytes > 10000) {
                        /* this if handles the TSO accounting */
-                       if (bytes/packets > 8000)
+                       if (bytes / packets > 8000)
                                retval = bulk_latency;
-                       else if ((packets < 10) || ((bytes/packets) > 1200))
+                       else if ((packets < 10) || ((bytes / packets) > 1200))
                                retval = bulk_latency;
                        else if ((packets > 35))
                                retval = lowest_latency;
-               } else if (bytes/packets > 2000) {
+               } else if (bytes / packets > 2000) {
                        retval = bulk_latency;
                } else if (packets <= 2 && bytes < 512) {
                        retval = lowest_latency;
@@ -2561,8 +2548,8 @@ static void e1000_set_itr(struct e1000_adapter *adapter)
 
        current_itr = max(adapter->rx_itr, adapter->tx_itr);
 
-       switch (current_itr) {
        /* counts and packets in update_itr are dependent on these numbers */
+       switch (current_itr) {
        case lowest_latency:
                new_itr = 70000;
                break;
@@ -2583,8 +2570,7 @@ set_itr_now:
                 * increasing
                 */
                new_itr = new_itr > adapter->itr ?
-                            min(adapter->itr + (new_itr >> 2), new_itr) :
-                            new_itr;
+                   min(adapter->itr + (new_itr >> 2), new_itr) : new_itr;
                adapter->itr = new_itr;
                adapter->rx_ring->itr_val = new_itr;
                if (adapter->msix_entries)
@@ -2686,7 +2672,8 @@ static int e1000e_poll(struct napi_struct *napi, int weight)
        return work_done;
 }
 
-static int e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+static int e1000_vlan_rx_add_vid(struct net_device *netdev,
+                                __be16 proto, u16 vid)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -2711,7 +2698,8 @@ static int e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
        return 0;
 }
 
-static int e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+static int e1000_vlan_rx_kill_vid(struct net_device *netdev,
+                                 __be16 proto, u16 vid)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -2755,7 +2743,8 @@ static void e1000e_vlan_filter_disable(struct e1000_adapter *adapter)
                ew32(RCTL, rctl);
 
                if (adapter->mng_vlan_id != (u16)E1000_MNG_VLAN_NONE) {
-                       e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
+                       e1000_vlan_rx_kill_vid(netdev, htons(ETH_P_8021Q),
+                                              adapter->mng_vlan_id);
                        adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
                }
        }
@@ -2815,24 +2804,23 @@ static void e1000_update_mng_vlan(struct e1000_adapter *adapter)
        u16 vid = adapter->hw.mng_cookie.vlan_id;
        u16 old_vid = adapter->mng_vlan_id;
 
-       if (adapter->hw.mng_cookie.status &
-           E1000_MNG_DHCP_COOKIE_STATUS_VLAN) {
-               e1000_vlan_rx_add_vid(netdev, vid);
+       if (adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN) {
+               e1000_vlan_rx_add_vid(netdev, htons(ETH_P_8021Q), vid);
                adapter->mng_vlan_id = vid;
        }
 
        if ((old_vid != (u16)E1000_MNG_VLAN_NONE) && (vid != old_vid))
-               e1000_vlan_rx_kill_vid(netdev, old_vid);
+               e1000_vlan_rx_kill_vid(netdev, htons(ETH_P_8021Q), old_vid);
 }
 
 static void e1000_restore_vlan(struct e1000_adapter *adapter)
 {
        u16 vid;
 
-       e1000_vlan_rx_add_vid(adapter->netdev, 0);
+       e1000_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), 0);
 
        for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
-               e1000_vlan_rx_add_vid(adapter->netdev, vid);
+           e1000_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid);
 }
 
 static void e1000_init_manageability_pt(struct e1000_adapter *adapter)
@@ -3007,8 +2995,8 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
        rctl = er32(RCTL);
        rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
        rctl |= E1000_RCTL_EN | E1000_RCTL_BAM |
-               E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
-               (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
+           E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
+           (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
 
        /* Do not Store bad packets */
        rctl &= ~E1000_RCTL_SBP;
@@ -3094,19 +3082,17 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
                /* Enable Packet split descriptors */
                rctl |= E1000_RCTL_DTYP_PS;
 
-               psrctl |= adapter->rx_ps_bsize0 >>
-                       E1000_PSRCTL_BSIZE0_SHIFT;
+               psrctl |= adapter->rx_ps_bsize0 >> E1000_PSRCTL_BSIZE0_SHIFT;
 
                switch (adapter->rx_ps_pages) {
                case 3:
-                       psrctl |= PAGE_SIZE <<
-                               E1000_PSRCTL_BSIZE3_SHIFT;
+                       psrctl |= PAGE_SIZE << E1000_PSRCTL_BSIZE3_SHIFT;
+                       /* fall-through */
                case 2:
-                       psrctl |= PAGE_SIZE <<
-                               E1000_PSRCTL_BSIZE2_SHIFT;
+                       psrctl |= PAGE_SIZE << E1000_PSRCTL_BSIZE2_SHIFT;
+                       /* fall-through */
                case 1:
-                       psrctl |= PAGE_SIZE >>
-                               E1000_PSRCTL_BSIZE1_SHIFT;
+                       psrctl |= PAGE_SIZE >> E1000_PSRCTL_BSIZE1_SHIFT;
                        break;
                }
 
@@ -3280,7 +3266,7 @@ static int e1000e_write_mc_addr_list(struct net_device *netdev)
        /* update_mc_addr_list expects a packed array of only addresses. */
        i = 0;
        netdev_for_each_mc_addr(ha, netdev)
-               memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN);
+           memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN);
 
        hw->mac.ops.update_mc_addr_list(hw, mta_list, i);
        kfree(mta_list);
@@ -3390,7 +3376,7 @@ static void e1000e_set_rx_mode(struct net_device *netdev)
 
        ew32(RCTL, rctl);
 
-       if (netdev->features & NETIF_F_HW_VLAN_RX)
+       if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
                e1000e_vlan_strip_enable(adapter);
        else
                e1000e_vlan_strip_disable(adapter);
@@ -3757,8 +3743,7 @@ void e1000e_reset(struct e1000_adapter *adapter)
                 * but don't include ethernet FCS because hardware appends it
                 */
                min_tx_space = (adapter->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 */
@@ -3861,13 +3846,13 @@ void e1000e_reset(struct e1000_adapter *adapter)
                if ((adapter->max_frame_size * 2) > (pba << 10)) {
                        if (!(adapter->flags2 & FLAG2_DISABLE_AIM)) {
                                dev_info(&adapter->pdev->dev,
-                                       "Interrupt Throttle Rate turned off\n");
+                                        "Interrupt Throttle Rate off\n");
                                adapter->flags2 |= FLAG2_DISABLE_AIM;
                                e1000e_write_itr(adapter, 0);
                        }
                } else if (adapter->flags2 & FLAG2_DISABLE_AIM) {
                        dev_info(&adapter->pdev->dev,
-                                "Interrupt Throttle Rate turned on\n");
+                                "Interrupt Throttle Rate on\n");
                        adapter->flags2 &= ~FLAG2_DISABLE_AIM;
                        adapter->itr = 20000;
                        e1000e_write_itr(adapter, adapter->itr);
@@ -3898,6 +3883,38 @@ void e1000e_reset(struct e1000_adapter *adapter)
        /* initialize systim and reset the ns time counter */
        e1000e_config_hwtstamp(adapter);
 
+       /* Set EEE advertisement as appropriate */
+       if (adapter->flags2 & FLAG2_HAS_EEE) {
+               s32 ret_val;
+               u16 adv_addr;
+
+               switch (hw->phy.type) {
+               case e1000_phy_82579:
+                       adv_addr = I82579_EEE_ADVERTISEMENT;
+                       break;
+               case e1000_phy_i217:
+                       adv_addr = I217_EEE_ADVERTISEMENT;
+                       break;
+               default:
+                       dev_err(&adapter->pdev->dev,
+                               "Invalid PHY type setting EEE advertisement\n");
+                       return;
+               }
+
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val) {
+                       dev_err(&adapter->pdev->dev,
+                               "EEE advertisement - unable to acquire PHY\n");
+                       return;
+               }
+
+               e1000_write_emi_reg_locked(hw, adv_addr,
+                                          hw->dev_spec.ich8lan.eee_disable ?
+                                          0 : adapter->eee_advert);
+
+               hw->phy.ops.release(hw);
+       }
+
        if (!netif_running(adapter->netdev) &&
            !test_bit(__E1000_TESTING, &adapter->state)) {
                e1000_power_down_phy(adapter);
@@ -4266,8 +4283,7 @@ static int e1000_open(struct net_device *netdev)
        e1000e_power_up_phy(adapter);
 
        adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
-       if ((adapter->hw.mng_cookie.status &
-            E1000_MNG_DHCP_COOKIE_STATUS_VLAN))
+       if ((adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN))
                e1000_update_mng_vlan(adapter);
 
        /* DMA latency requirement to workaround jumbo issue */
@@ -4370,9 +4386,9 @@ static int e1000_close(struct net_device *netdev)
        /* kill manageability vlan ID if supported, but not if a vlan with
         * the same ID is registered on the host OS (let 8021q kill it)
         */
-       if (adapter->hw.mng_cookie.status &
-           E1000_MNG_DHCP_COOKIE_STATUS_VLAN)
-               e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
+       if (adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN)
+               e1000_vlan_rx_kill_vid(netdev, htons(ETH_P_8021Q),
+                                      adapter->mng_vlan_id);
 
        /* If AMT is enabled, let the firmware know that the network
         * interface is now closed
@@ -4387,6 +4403,7 @@ static int e1000_close(struct net_device *netdev)
 
        return 0;
 }
+
 /**
  * e1000_set_mac - Change the Ethernet Address of the NIC
  * @netdev: network interface device structure
@@ -4437,7 +4454,8 @@ static int e1000_set_mac(struct net_device *netdev, void *p)
 static void e1000e_update_phy_task(struct work_struct *work)
 {
        struct e1000_adapter *adapter = container_of(work,
-                                       struct e1000_adapter, update_phy_task);
+                                                    struct e1000_adapter,
+                                                    update_phy_task);
 
        if (test_bit(__E1000_DOWN, &adapter->state))
                return;
@@ -4454,7 +4472,7 @@ static void e1000e_update_phy_task(struct work_struct *work)
  **/
 static void e1000_update_phy_info(unsigned long data)
 {
-       struct e1000_adapter *adapter = (struct e1000_adapter *) data;
+       struct e1000_adapter *adapter = (struct e1000_adapter *)data;
 
        if (test_bit(__E1000_DOWN, &adapter->state))
                return;
@@ -4621,18 +4639,16 @@ static void e1000e_update_stats(struct e1000_adapter *adapter)
         * our own version based on RUC and ROC
         */
        netdev->stats.rx_errors = adapter->stats.rxerrc +
-               adapter->stats.crcerrs + adapter->stats.algnerrc +
-               adapter->stats.ruc + adapter->stats.roc +
-               adapter->stats.cexterr;
+           adapter->stats.crcerrs + adapter->stats.algnerrc +
+           adapter->stats.ruc + adapter->stats.roc + adapter->stats.cexterr;
        netdev->stats.rx_length_errors = adapter->stats.ruc +
-                                             adapter->stats.roc;
+           adapter->stats.roc;
        netdev->stats.rx_crc_errors = adapter->stats.crcerrs;
        netdev->stats.rx_frame_errors = adapter->stats.algnerrc;
        netdev->stats.rx_missed_errors = adapter->stats.mpc;
 
        /* Tx Errors */
-       netdev->stats.tx_errors = adapter->stats.ecol +
-                                      adapter->stats.latecol;
+       netdev->stats.tx_errors = adapter->stats.ecol + adapter->stats.latecol;
        netdev->stats.tx_aborted_errors = adapter->stats.ecol;
        netdev->stats.tx_window_errors = adapter->stats.latecol;
        netdev->stats.tx_carrier_errors = adapter->stats.tncrs;
@@ -4790,7 +4806,7 @@ static void e1000e_check_82574_phy_workaround(struct e1000_adapter *adapter)
  **/
 static void e1000_watchdog(unsigned long data)
 {
-       struct e1000_adapter *adapter = (struct e1000_adapter *) data;
+       struct e1000_adapter *adapter = (struct e1000_adapter *)data;
 
        /* Do the rest outside of interrupt context */
        schedule_work(&adapter->watchdog_task);
@@ -4801,7 +4817,8 @@ static void e1000_watchdog(unsigned long data)
 static void e1000_watchdog_task(struct work_struct *work)
 {
        struct e1000_adapter *adapter = container_of(work,
-                                       struct e1000_adapter, watchdog_task);
+                                                    struct e1000_adapter,
+                                                    watchdog_task);
        struct net_device *netdev = adapter->netdev;
        struct e1000_mac_info *mac = &adapter->hw.mac;
        struct e1000_phy_info *phy = &adapter->hw.phy;
@@ -4835,8 +4852,8 @@ static void e1000_watchdog_task(struct work_struct *work)
                        /* update snapshot of PHY registers on LSC */
                        e1000_phy_read_status(adapter);
                        mac->ops.get_link_up_info(&adapter->hw,
-                                                  &adapter->link_speed,
-                                                  &adapter->link_duplex);
+                                                 &adapter->link_speed,
+                                                 &adapter->link_duplex);
                        e1000_print_link_info(adapter);
 
                        /* check if SmartSpeed worked */
@@ -4949,7 +4966,7 @@ static void e1000_watchdog_task(struct work_struct *work)
                                adapter->flags |= FLAG_RESTART_NOW;
                        else
                                pm_schedule_suspend(netdev->dev.parent,
-                                                       LINK_TIMEOUT);
+                                                   LINK_TIMEOUT);
                }
        }
 
@@ -4984,8 +5001,8 @@ link_up:
                 */
                u32 goc = (adapter->gotc + adapter->gorc) / 10000;
                u32 dif = (adapter->gotc > adapter->gorc ?
-                           adapter->gotc - adapter->gorc :
-                           adapter->gorc - adapter->gotc) / 10000;
+                          adapter->gotc - adapter->gorc :
+                          adapter->gorc - adapter->gotc) / 10000;
                u32 itr = goc > 0 ? (dif * 6000 / goc + 2000) : 8000;
 
                e1000e_write_itr(adapter, itr);
@@ -5064,14 +5081,14 @@ static int e1000_tso(struct e1000_ring *tx_ring, struct sk_buff *skb)
                iph->tot_len = 0;
                iph->check = 0;
                tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                                        0, IPPROTO_TCP, 0);
+                                                        0, IPPROTO_TCP, 0);
                cmd_length = E1000_TXD_CMD_IP;
                ipcse = skb_transport_offset(skb) - 1;
        } else if (skb_is_gso_v6(skb)) {
                ipv6_hdr(skb)->payload_len = 0;
                tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
-                                                      &ipv6_hdr(skb)->daddr,
-                                                      0, IPPROTO_TCP, 0);
+                                                      &ipv6_hdr(skb)->daddr,
+                                                      0, IPPROTO_TCP, 0);
                ipcse = 0;
        }
        ipcss = skb_network_offset(skb);
@@ -5080,7 +5097,7 @@ static int e1000_tso(struct e1000_ring *tx_ring, struct sk_buff *skb)
        tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data;
 
        cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE |
-                      E1000_TXD_CMD_TCP | (skb->len - (hdr_len)));
+                      E1000_TXD_CMD_TCP | (skb->len - (hdr_len)));
 
        i = tx_ring->next_to_use;
        context_desc = E1000_CONTEXT_DESC(*tx_ring, i);
@@ -5150,8 +5167,7 @@ static bool e1000_tx_csum(struct e1000_ring *tx_ring, struct sk_buff *skb)
 
        context_desc->lower_setup.ip_config = 0;
        context_desc->upper_setup.tcp_fields.tucss = css;
-       context_desc->upper_setup.tcp_fields.tucso =
-                               css + skb->csum_offset;
+       context_desc->upper_setup.tcp_fields.tucso = css + skb->csum_offset;
        context_desc->upper_setup.tcp_fields.tucse = 0;
        context_desc->tcp_seg_setup.data = 0;
        context_desc->cmd_and_length = cpu_to_le32(cmd_len);
@@ -5224,7 +5240,8 @@ static int e1000_tx_map(struct e1000_ring *tx_ring, struct sk_buff *skb,
                        buffer_info->time_stamp = jiffies;
                        buffer_info->next_to_watch = i;
                        buffer_info->dma = skb_frag_dma_map(&pdev->dev, frag,
-                                               offset, size, DMA_TO_DEVICE);
+                                                           offset, size,
+                                                           DMA_TO_DEVICE);
                        buffer_info->mapped_as_page = true;
                        if (dma_mapping_error(&pdev->dev, buffer_info->dma))
                                goto dma_error;
@@ -5273,7 +5290,7 @@ static void e1000_tx_queue(struct e1000_ring *tx_ring, int tx_flags, int count)
 
        if (tx_flags & E1000_TX_FLAGS_TSO) {
                txd_lower |= E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D |
-                            E1000_TXD_CMD_TSE;
+                   E1000_TXD_CMD_TSE;
                txd_upper |= E1000_TXD_POPTS_TXSM << 8;
 
                if (tx_flags & E1000_TX_FLAGS_IPV4)
@@ -5304,8 +5321,8 @@ static void e1000_tx_queue(struct e1000_ring *tx_ring, int tx_flags, int count)
                buffer_info = &tx_ring->buffer_info[i];
                tx_desc = E1000_TX_DESC(*tx_ring, i);
                tx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
-               tx_desc->lower.data =
-                       cpu_to_le32(txd_lower | buffer_info->length);
+               tx_desc->lower.data = cpu_to_le32(txd_lower |
+                                                 buffer_info->length);
                tx_desc->upper.data = cpu_to_le32(txd_upper);
 
                i++;
@@ -5355,11 +5372,11 @@ static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter,
        if (skb->len <= MINIMUM_DHCP_PACKET_SIZE)
                return 0;
 
-       if (((struct ethhdr *) skb->data)->h_proto != htons(ETH_P_IP))
+       if (((struct ethhdr *)skb->data)->h_proto != htons(ETH_P_IP))
                return 0;
 
        {
-               const struct iphdr *ip = (struct iphdr *)((u8 *)skb->data+14);
+               const struct iphdr *ip = (struct iphdr *)((u8 *)skb->data + 14);
                struct udphdr *udp;
 
                if (ip->protocol != IPPROTO_UDP)
@@ -5584,7 +5601,7 @@ static void e1000_reset_task(struct work_struct *work)
  * Returns the address of the device statistics structure.
  **/
 struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev,
-                                             struct rtnl_link_stats64 *stats)
+                                            struct rtnl_link_stats64 *stats)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
 
@@ -5605,18 +5622,15 @@ struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev,
         * our own version based on RUC and ROC
         */
        stats->rx_errors = adapter->stats.rxerrc +
-               adapter->stats.crcerrs + adapter->stats.algnerrc +
-               adapter->stats.ruc + adapter->stats.roc +
-               adapter->stats.cexterr;
-       stats->rx_length_errors = adapter->stats.ruc +
-                                             adapter->stats.roc;
+           adapter->stats.crcerrs + adapter->stats.algnerrc +
+           adapter->stats.ruc + adapter->stats.roc + adapter->stats.cexterr;
+       stats->rx_length_errors = adapter->stats.ruc + adapter->stats.roc;
        stats->rx_crc_errors = adapter->stats.crcerrs;
        stats->rx_frame_errors = adapter->stats.algnerrc;
        stats->rx_missed_errors = adapter->stats.mpc;
 
        /* Tx Errors */
-       stats->tx_errors = adapter->stats.ecol +
-                                      adapter->stats.latecol;
+       stats->tx_errors = adapter->stats.ecol + adapter->stats.latecol;
        stats->tx_aborted_errors = adapter->stats.ecol;
        stats->tx_window_errors = adapter->stats.latecol;
        stats->tx_carrier_errors = adapter->stats.tncrs;
@@ -5685,9 +5699,9 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
 
        /* adjust allocation if LPE protects us, and we aren't using SBP */
        if ((max_frame == ETH_FRAME_LEN + ETH_FCS_LEN) ||
-            (max_frame == ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN))
+           (max_frame == ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN))
                adapter->rx_buffer_len = ETH_FRAME_LEN + VLAN_HLEN
-                                        + ETH_FCS_LEN;
+                   + ETH_FCS_LEN;
 
        if (netif_running(netdev))
                e1000e_up(adapter);
@@ -5866,7 +5880,7 @@ static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc)
        phy_reg &= ~(BM_RCTL_MO_MASK);
        if (mac_reg & E1000_RCTL_MO_3)
                phy_reg |= (((mac_reg & E1000_RCTL_MO_3) >> E1000_RCTL_MO_SHIFT)
-                               << BM_RCTL_MO_SHIFT);
+                           << BM_RCTL_MO_SHIFT);
        if (mac_reg & E1000_RCTL_BAM)
                phy_reg |= BM_RCTL_BAM;
        if (mac_reg & E1000_RCTL_PMCF)
@@ -5935,10 +5949,6 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
                }
 
                ctrl = er32(CTRL);
-               /* advertise wake from D3Cold */
-               #define E1000_CTRL_ADVD3WUC 0x00100000
-               /* phy power management enable */
-               #define E1000_CTRL_EN_PHY_PWR_MGMT 0x00200000
                ctrl |= E1000_CTRL_ADVD3WUC;
                if (!(adapter->flags2 & FLAG2_HAS_PHY_WAKEUP))
                        ctrl |= E1000_CTRL_EN_PHY_PWR_MGMT;
@@ -5982,8 +5992,6 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
         */
        e1000e_release_hw_control(adapter);
 
-       pci_clear_master(pdev);
-
        /* The pci-e switch on some quad port adapters will report a
         * correctable error when the MAC transitions from D0 to D3.  To
         * prevent this we need to mask off the correctable errors on the
@@ -6082,24 +6090,24 @@ static int __e1000_resume(struct pci_dev *pdev)
                e1e_rphy(&adapter->hw, BM_WUS, &phy_data);
                if (phy_data) {
                        e_info("PHY Wakeup cause - %s\n",
-                               phy_data & E1000_WUS_EX ? "Unicast Packet" :
-                               phy_data & E1000_WUS_MC ? "Multicast Packet" :
-                               phy_data & E1000_WUS_BC ? "Broadcast Packet" :
-                               phy_data & E1000_WUS_MAG ? "Magic Packet" :
-                               phy_data & E1000_WUS_LNKC ?
-                               "Link Status Change" : "other");
+                              phy_data & E1000_WUS_EX ? "Unicast Packet" :
+                              phy_data & E1000_WUS_MC ? "Multicast Packet" :
+                              phy_data & E1000_WUS_BC ? "Broadcast Packet" :
+                              phy_data & E1000_WUS_MAG ? "Magic Packet" :
+                              phy_data & E1000_WUS_LNKC ?
+                              "Link Status Change" : "other");
                }
                e1e_wphy(&adapter->hw, BM_WUS, ~0);
        } else {
                u32 wus = er32(WUS);
                if (wus) {
                        e_info("MAC Wakeup cause - %s\n",
-                               wus & E1000_WUS_EX ? "Unicast Packet" :
-                               wus & E1000_WUS_MC ? "Multicast Packet" :
-                               wus & E1000_WUS_BC ? "Broadcast Packet" :
-                               wus & E1000_WUS_MAG ? "Magic Packet" :
-                               wus & E1000_WUS_LNKC ? "Link Status Change" :
-                               "other");
+                              wus & E1000_WUS_EX ? "Unicast Packet" :
+                              wus & E1000_WUS_MC ? "Multicast Packet" :
+                              wus & E1000_WUS_BC ? "Broadcast Packet" :
+                              wus & E1000_WUS_MAG ? "Magic Packet" :
+                              wus & E1000_WUS_LNKC ? "Link Status Change" :
+                              "other");
                }
                ew32(WUS, ~0);
        }
@@ -6374,7 +6382,7 @@ static void e1000_print_device_info(struct e1000_adapter *adapter)
        e_info("(PCI Express:2.5GT/s:%s) %pM\n",
               /* bus width */
               ((hw->bus.width == e1000_bus_width_pcie_x4) ? "Width x4" :
-               "Width x1"),
+               "Width x1"),
               /* MAC address */
               netdev->dev_addr);
        e_info("Intel(R) PRO/%s Network Connection\n",
@@ -6414,7 +6422,7 @@ static int e1000_set_features(struct net_device *netdev,
        if (changed & (NETIF_F_TSO | NETIF_F_TSO6))
                adapter->flags |= FLAG_TSO_FORCE;
 
-       if (!(changed & (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX |
+       if (!(changed & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX |
                         NETIF_F_RXCSUM | NETIF_F_RXHASH | NETIF_F_RXFCS |
                         NETIF_F_RXALL)))
                return 0;
@@ -6484,7 +6492,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        resource_size_t flash_start, flash_len;
        static int cards_found;
        u16 aspm_disable_flag = 0;
-       int i, err, pci_using_dac;
+       int bars, i, err, pci_using_dac;
        u16 eeprom_data = 0;
        u16 eeprom_apme_mask = E1000_EEPROM_APME;
 
@@ -6511,15 +6519,16 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                        err = dma_set_coherent_mask(&pdev->dev,
                                                    DMA_BIT_MASK(32));
                        if (err) {
-                               dev_err(&pdev->dev, "No usable DMA configuration, aborting\n");
+                               dev_err(&pdev->dev,
+                                       "No usable DMA configuration, aborting\n");
                                goto err_dma;
                        }
                }
        }
 
-       err = pci_request_selected_regions_exclusive(pdev,
-                                         pci_select_bars(pdev, IORESOURCE_MEM),
-                                         e1000e_driver_name);
+       bars = pci_select_bars(pdev, IORESOURCE_MEM);
+       err = pci_request_selected_regions_exclusive(pdev, bars,
+                                                    e1000e_driver_name);
        if (err)
                goto err_pci_reg;
 
@@ -6572,6 +6581,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                        goto err_flashmap;
        }
 
+       /* Set default EEE advertisement */
+       if (adapter->flags2 & FLAG2_HAS_EEE)
+               adapter->eee_advert = MDIO_EEE_100TX | MDIO_EEE_1000T;
+
        /* construct the net_device struct */
        netdev->netdev_ops              = &e1000e_netdev_ops;
        e1000e_set_ethtool_ops(netdev);
@@ -6620,8 +6633,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        /* Set initial default active device features */
        netdev->features = (NETIF_F_SG |
-                           NETIF_F_HW_VLAN_RX |
-                           NETIF_F_HW_VLAN_TX |
+                           NETIF_F_HW_VLAN_CTAG_RX |
+                           NETIF_F_HW_VLAN_CTAG_TX |
                            NETIF_F_TSO |
                            NETIF_F_TSO6 |
                            NETIF_F_RXHASH |
@@ -6635,7 +6648,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        netdev->hw_features |= NETIF_F_RXALL;
 
        if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER)
-               netdev->features |= NETIF_F_HW_VLAN_FILTER;
+               netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        netdev->vlan_features |= (NETIF_F_SG |
                                  NETIF_F_TSO |
@@ -6688,11 +6701,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        init_timer(&adapter->watchdog_timer);
        adapter->watchdog_timer.function = e1000_watchdog;
-       adapter->watchdog_timer.data = (unsigned long) adapter;
+       adapter->watchdog_timer.data = (unsigned long)adapter;
 
        init_timer(&adapter->phy_info_timer);
        adapter->phy_info_timer.function = e1000_update_phy_info;
-       adapter->phy_info_timer.data = (unsigned long) adapter;
+       adapter->phy_info_timer.data = (unsigned long)adapter;
 
        INIT_WORK(&adapter->reset_task, e1000_reset_task);
        INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task);
@@ -6800,7 +6813,7 @@ err_ioremap:
        free_netdev(netdev);
 err_alloc_etherdev:
        pci_release_selected_regions(pdev,
-                                    pci_select_bars(pdev, IORESOURCE_MEM));
+                                    pci_select_bars(pdev, IORESOURCE_MEM));
 err_pci_reg:
 err_dma:
        pci_disable_device(pdev);
@@ -6870,7 +6883,7 @@ static void e1000_remove(struct pci_dev *pdev)
        if (adapter->hw.flash_address)
                iounmap(adapter->hw.flash_address);
        pci_release_selected_regions(pdev,
-                                    pci_select_bars(pdev, IORESOURCE_MEM));
+                                    pci_select_bars(pdev, IORESOURCE_MEM));
 
        free_netdev(netdev);
 
@@ -6891,7 +6904,8 @@ static DEFINE_PCI_DEVICE_TABLE(e1000_pci_tbl) = {
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571 },
-       { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP), board_82571 },
+       { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP),
+         board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_FIBER), board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES), board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES_DUAL), board_82571 },
@@ -6967,8 +6981,8 @@ MODULE_DEVICE_TABLE(pci, e1000_pci_tbl);
 #ifdef CONFIG_PM
 static const struct dev_pm_ops e1000_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(e1000_suspend, e1000_resume)
-       SET_RUNTIME_PM_OPS(e1000_runtime_suspend,
-                               e1000_runtime_resume, e1000_idle)
+       SET_RUNTIME_PM_OPS(e1000_runtime_suspend, e1000_runtime_resume,
+                          e1000_idle)
 };
 #endif
 
index 84fecc268162417b676f65066abc2fb2e4bd20bf..44ddc0a0ee0e4287ee17b6a389904aaedb9b226a 100644 (file)
@@ -630,7 +630,7 @@ void e1000e_reload_nvm_generic(struct e1000_hw *hw)
 {
        u32 ctrl_ext;
 
-       udelay(10);
+       usleep_range(10, 20);
        ctrl_ext = er32(CTRL_EXT);
        ctrl_ext |= E1000_CTRL_EXT_EE_RST;
        ew32(CTRL_EXT, ctrl_ext);
index 98da75dff936d73329d272adcf54f779796c5745..c16bd75b6caa3f85f4f510da7fb56bafcd0f2b40 100644 (file)
@@ -45,7 +45,7 @@
 unsigned int copybreak = COPYBREAK_DEFAULT;
 module_param(copybreak, uint, 0644);
 MODULE_PARM_DESC(copybreak,
-       "Maximum size of packet that is copied to a new buffer on receive");
+                "Maximum size of packet that is copied to a new buffer on receive");
 
 /* All parameters are treated the same, as an integer array of values.
  * This macro just reduces the need to repeat the same declaration code
@@ -143,7 +143,8 @@ E1000_PARAM(KumeranLockLoss, "Enable Kumeran lock loss workaround");
  *
  * Default Value: 1 (enabled)
  */
-E1000_PARAM(WriteProtectNVM, "Write-protect NVM [WARNING: disabling this can lead to corrupted NVM]");
+E1000_PARAM(WriteProtectNVM,
+           "Write-protect NVM [WARNING: disabling this can lead to corrupted NVM]");
 
 /* Enable CRC Stripping
  *
@@ -160,13 +161,18 @@ struct e1000_option {
        const char *err;
        int def;
        union {
-               struct { /* range_option info */
+               /* range_option info */
+               struct {
                        int min;
                        int max;
                } r;
-               struct { /* list_option info */
+               /* list_option info */
+               struct {
                        int nr;
-                       struct e1000_opt_list { int i; char *str; } *p;
+                       struct e1000_opt_list {
+                               int i;
+                               char *str;
+                       } *p;
                } l;
        } arg;
 };
@@ -246,7 +252,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                           "Using defaults for all values\n");
        }
 
-       { /* Transmit Interrupt Delay */
+       /* Transmit Interrupt Delay */
+       {
                static const struct e1000_option opt = {
                        .type = range_option,
                        .name = "Transmit Interrupt Delay",
@@ -265,7 +272,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                        adapter->tx_int_delay = opt.def;
                }
        }
-       { /* Transmit Absolute Interrupt Delay */
+       /* Transmit Absolute Interrupt Delay */
+       {
                static const struct e1000_option opt = {
                        .type = range_option,
                        .name = "Transmit Absolute Interrupt Delay",
@@ -284,7 +292,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                        adapter->tx_abs_int_delay = opt.def;
                }
        }
-       { /* Receive Interrupt Delay */
+       /* Receive Interrupt Delay */
+       {
                static struct e1000_option opt = {
                        .type = range_option,
                        .name = "Receive Interrupt Delay",
@@ -303,7 +312,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                        adapter->rx_int_delay = opt.def;
                }
        }
-       { /* Receive Absolute Interrupt Delay */
+       /* Receive Absolute Interrupt Delay */
+       {
                static const struct e1000_option opt = {
                        .type = range_option,
                        .name = "Receive Absolute Interrupt Delay",
@@ -322,7 +332,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                        adapter->rx_abs_int_delay = opt.def;
                }
        }
-       { /* Interrupt Throttling Rate */
+       /* Interrupt Throttling Rate */
+       {
                static const struct e1000_option opt = {
                        .type = range_option,
                        .name = "Interrupt Throttling Rate (ints/sec)",
@@ -392,7 +403,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                        break;
                }
        }
-       { /* Interrupt Mode */
+       /* Interrupt Mode */
+       {
                static struct e1000_option opt = {
                        .type = range_option,
                        .name = "Interrupt Mode",
@@ -435,7 +447,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                kfree(opt.err);
 #endif
        }
-       { /* Smart Power Down */
+       /* Smart Power Down */
+       {
                static const struct e1000_option opt = {
                        .type = enable_option,
                        .name = "PHY Smart Power Down",
@@ -450,7 +463,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                                adapter->flags |= FLAG_SMART_POWER_DOWN;
                }
        }
-       { /* CRC Stripping */
+       /* CRC Stripping */
+       {
                static const struct e1000_option opt = {
                        .type = enable_option,
                        .name = "CRC Stripping",
@@ -470,27 +484,28 @@ void e1000e_check_options(struct e1000_adapter *adapter)
                        adapter->flags2 |= FLAG2_DFLT_CRC_STRIPPING;
                }
        }
-       { /* Kumeran Lock Loss Workaround */
+       /* Kumeran Lock Loss Workaround */
+       {
                static const struct e1000_option opt = {
                        .type = enable_option,
                        .name = "Kumeran Lock Loss Workaround",
                        .err  = "defaulting to Enabled",
                        .def  = OPTION_ENABLED
                };
+               bool enabled = opt.def;
 
                if (num_KumeranLockLoss > bd) {
                        unsigned int kmrn_lock_loss = KumeranLockLoss[bd];
                        e1000_validate_option(&kmrn_lock_loss, &opt, adapter);
-                       if (hw->mac.type == e1000_ich8lan)
-                               e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw,
-                                                               kmrn_lock_loss);
-               } else {
-                       if (hw->mac.type == e1000_ich8lan)
-                               e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw,
-                                                                      opt.def);
+                       enabled = kmrn_lock_loss;
                }
+
+               if (hw->mac.type == e1000_ich8lan)
+                       e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw,
+                                                                    enabled);
        }
-       { /* Write-protect NVM */
+       /* Write-protect NVM */
+       {
                static const struct e1000_option opt = {
                        .type = enable_option,
                        .name = "Write-protect NVM",
@@ -500,7 +515,8 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (adapter->flags & FLAG_IS_ICH) {
                        if (num_WriteProtectNVM > bd) {
-                               unsigned int write_protect_nvm = WriteProtectNVM[bd];
+                               unsigned int write_protect_nvm =
+                                   WriteProtectNVM[bd];
                                e1000_validate_option(&write_protect_nvm, &opt,
                                                      adapter);
                                if (write_protect_nvm)
index 0930c136aa310644d8117c63cad3f87868ff54f7..59c76a6815a0854edd9b38067b82dfcede1c9d10 100644 (file)
@@ -37,7 +37,9 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
 
 /* Cable length tables */
 static const u16 e1000_m88_cable_length_table[] = {
-       0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED };
+       0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED
+};
+
 #define M88E1000_CABLE_LENGTH_TABLE_SIZE \
                ARRAY_SIZE(e1000_m88_cable_length_table)
 
@@ -49,7 +51,9 @@ static const u16 e1000_igp_2_cable_length_table[] = {
        66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104, 60, 66, 72, 77, 82,
        87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121, 83, 89, 95,
        100, 105, 109, 113, 116, 119, 122, 124, 104, 109, 114, 118, 121,
-       124};
+       124
+};
+
 #define IGP02E1000_CABLE_LENGTH_TABLE_SIZE \
                ARRAY_SIZE(e1000_igp_2_cable_length_table)
 
@@ -67,8 +71,7 @@ s32 e1000e_check_reset_block_generic(struct e1000_hw *hw)
 
        manc = er32(MANC);
 
-       return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ?
-              E1000_BLK_PHY_RESET : 0;
+       return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? E1000_BLK_PHY_RESET : 0;
 }
 
 /**
@@ -94,7 +97,7 @@ s32 e1000e_get_phy_id(struct e1000_hw *hw)
                        return ret_val;
 
                phy->id = (u32)(phy_id << 16);
-               udelay(20);
+               usleep_range(20, 40);
                ret_val = e1e_rphy(hw, MII_PHYSID2, &phy_id);
                if (ret_val)
                        return ret_val;
@@ -175,7 +178,13 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
                e_dbg("MDI Error\n");
                return -E1000_ERR_PHY;
        }
-       *data = (u16) mdic;
+       if (((mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT) != offset) {
+               e_dbg("MDI Read offset error - requested %d, returned %d\n",
+                     offset,
+                     (mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT);
+               return -E1000_ERR_PHY;
+       }
+       *data = (u16)mdic;
 
        /* Allow some time after each MDIC transaction to avoid
         * reading duplicate data in the next MDIC transaction.
@@ -233,6 +242,12 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
                e_dbg("MDI Error\n");
                return -E1000_ERR_PHY;
        }
+       if (((mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT) != offset) {
+               e_dbg("MDI Write offset error - requested %d, returned %d\n",
+                     offset,
+                     (mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT);
+               return -E1000_ERR_PHY;
+       }
 
        /* Allow some time after each MDIC transaction to avoid
         * reading duplicate data in the next MDIC transaction.
@@ -324,7 +339,7 @@ s32 e1000_set_page_igp(struct e1000_hw *hw, u16 page)
  *  semaphores before exiting.
  **/
 static s32 __e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data,
-                                    bool locked)
+                                    bool locked)
 {
        s32 ret_val = 0;
 
@@ -391,7 +406,7 @@ s32 e1000e_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data)
  *  at the offset.  Release any acquired semaphores before exiting.
  **/
 static s32 __e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data,
-                                     bool locked)
+                                     bool locked)
 {
        s32 ret_val = 0;
 
@@ -410,8 +425,7 @@ static s32 __e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data,
                                                    (u16)offset);
        if (!ret_val)
                ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS &
-                                                       offset,
-                                                   data);
+                                                   offset, data);
        if (!locked)
                hw->phy.ops.release(hw);
 
@@ -458,7 +472,7 @@ s32 e1000e_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data)
  *  Release any acquired semaphores before exiting.
  **/
 static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data,
-                                 bool locked)
+                                bool locked)
 {
        u32 kmrnctrlsta;
 
@@ -531,7 +545,7 @@ s32 e1000e_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data)
  *  before exiting.
  **/
 static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data,
-                                  bool locked)
+                                 bool locked)
 {
        u32 kmrnctrlsta;
 
@@ -772,8 +786,7 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw)
 
                phy_data |= M88E1000_EPSCR_TX_CLK_25;
 
-               if ((phy->revision == 2) &&
-                   (phy->id == M88E1111_I_PHY_ID)) {
+               if ((phy->revision == 2) && (phy->id == M88E1111_I_PHY_ID)) {
                        /* 82573L PHY - set the downshift counter to 5x. */
                        phy_data &= ~M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK;
                        phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X;
@@ -1296,7 +1309,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw)
                e_dbg("Waiting for forced speed/duplex link on M88 phy.\n");
 
                ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
-                                                    100000, &link);
+                                                     100000, &link);
                if (ret_val)
                        return ret_val;
 
@@ -1319,7 +1332,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw)
 
                /* Try once more */
                ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
-                                                    100000, &link);
+                                                     100000, &link);
                if (ret_val)
                        return ret_val;
        }
@@ -1609,9 +1622,9 @@ s32 e1000_check_polarity_m88(struct e1000_hw *hw)
        ret_val = e1e_rphy(hw, M88E1000_PHY_SPEC_STATUS, &data);
 
        if (!ret_val)
-               phy->cable_polarity = (data & M88E1000_PSSR_REV_POLARITY)
-                                     ? e1000_rev_polarity_reversed
-                                     : e1000_rev_polarity_normal;
+               phy->cable_polarity = ((data & M88E1000_PSSR_REV_POLARITY)
+                                      ? e1000_rev_polarity_reversed
+                                      : e1000_rev_polarity_normal);
 
        return ret_val;
 }
@@ -1653,9 +1666,9 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw)
        ret_val = e1e_rphy(hw, offset, &data);
 
        if (!ret_val)
-               phy->cable_polarity = (data & mask)
-                                     ? e1000_rev_polarity_reversed
-                                     : e1000_rev_polarity_normal;
+               phy->cable_polarity = ((data & mask)
+                                      ? e1000_rev_polarity_reversed
+                                      : e1000_rev_polarity_normal);
 
        return ret_val;
 }
@@ -1685,9 +1698,9 @@ s32 e1000_check_polarity_ife(struct e1000_hw *hw)
        ret_val = e1e_rphy(hw, offset, &phy_data);
 
        if (!ret_val)
-               phy->cable_polarity = (phy_data & mask)
-                                      ? e1000_rev_polarity_reversed
-                                      : e1000_rev_polarity_normal;
+               phy->cable_polarity = ((phy_data & mask)
+                                      ? e1000_rev_polarity_reversed
+                                      : e1000_rev_polarity_normal);
 
        return ret_val;
 }
@@ -1733,7 +1746,7 @@ static s32 e1000_wait_autoneg(struct e1000_hw *hw)
  *  Polls the PHY status register for link, 'iterations' number of times.
  **/
 s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
-                              u32 usec_interval, bool *success)
+                               u32 usec_interval, bool *success)
 {
        s32 ret_val = 0;
        u16 i, phy_status;
@@ -1756,7 +1769,7 @@ s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
                if (phy_status & BMSR_LSTATUS)
                        break;
                if (usec_interval >= 1000)
-                       mdelay(usec_interval/1000);
+                       mdelay(usec_interval / 1000);
                else
                        udelay(usec_interval);
        }
@@ -1791,8 +1804,8 @@ s32 e1000e_get_cable_length_m88(struct e1000_hw *hw)
        if (ret_val)
                return ret_val;
 
-       index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
-               M88E1000_PSSR_CABLE_LENGTH_SHIFT;
+       index = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
+                M88E1000_PSSR_CABLE_LENGTH_SHIFT);
 
        if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1)
                return -E1000_ERR_PHY;
@@ -1824,10 +1837,10 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw)
        u16 cur_agc_index, max_agc_index = 0;
        u16 min_agc_index = IGP02E1000_CABLE_LENGTH_TABLE_SIZE - 1;
        static const u16 agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] = {
-              IGP02E1000_PHY_AGC_A,
-              IGP02E1000_PHY_AGC_B,
-              IGP02E1000_PHY_AGC_C,
-              IGP02E1000_PHY_AGC_D
+               IGP02E1000_PHY_AGC_A,
+               IGP02E1000_PHY_AGC_B,
+               IGP02E1000_PHY_AGC_C,
+               IGP02E1000_PHY_AGC_D
        };
 
        /* Read the AGC registers for all channels */
@@ -1841,8 +1854,8 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw)
                 * that can be put into the lookup table to obtain the
                 * approximate cable length.
                 */
-               cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) &
-                               IGP02E1000_AGC_LENGTH_MASK;
+               cur_agc_index = ((phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) &
+                                IGP02E1000_AGC_LENGTH_MASK);
 
                /* Array index bound check. */
                if ((cur_agc_index >= IGP02E1000_CABLE_LENGTH_TABLE_SIZE) ||
@@ -1865,8 +1878,8 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw)
        agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2);
 
        /* Calculate cable length with the error range of +/- 10 meters. */
-       phy->min_cable_length = ((agc_value - IGP02E1000_AGC_RANGE) > 0) ?
-                                (agc_value - IGP02E1000_AGC_RANGE) : 0;
+       phy->min_cable_length = (((agc_value - IGP02E1000_AGC_RANGE) > 0) ?
+                                (agc_value - IGP02E1000_AGC_RANGE) : 0);
        phy->max_cable_length = agc_value + IGP02E1000_AGC_RANGE;
 
        phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
@@ -2040,9 +2053,9 @@ s32 e1000_get_phy_info_ife(struct e1000_hw *hw)
                        return ret_val;
        } else {
                /* Polarity is forced */
-               phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY)
-                                     ? e1000_rev_polarity_reversed
-                                     : e1000_rev_polarity_normal;
+               phy->cable_polarity = ((data & IFE_PSC_FORCE_POLARITY)
+                                      ? e1000_rev_polarity_reversed
+                                      : e1000_rev_polarity_normal);
        }
 
        ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data);
@@ -2119,7 +2132,7 @@ s32 e1000e_phy_hw_reset_generic(struct e1000_hw *hw)
        ew32(CTRL, ctrl);
        e1e_flush();
 
-       udelay(150);
+       usleep_range(150, 300);
 
        phy->ops.release(hw);
 
@@ -2375,13 +2388,13 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
 
                /* Page is shifted left, PHY expects (page x 32) */
                ret_val = e1000e_write_phy_reg_mdic(hw, page_select,
-                                                   (page << page_shift));
+                                                   (page << page_shift));
                if (ret_val)
                        goto release;
        }
 
        ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
-                                           data);
+                                           data);
 
 release:
        hw->phy.ops.release(hw);
@@ -2433,13 +2446,13 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
 
                /* Page is shifted left, PHY expects (page x 32) */
                ret_val = e1000e_write_phy_reg_mdic(hw, page_select,
-                                                   (page << page_shift));
+                                                   (page << page_shift));
                if (ret_val)
                        goto release;
        }
 
        ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
-                                          data);
+                                          data);
 release:
        hw->phy.ops.release(hw);
        return ret_val;
@@ -2674,7 +2687,7 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
        if (read) {
                /* Read the Wakeup register page value using opcode 0x12 */
                ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
-                                                  data);
+                                                  data);
        } else {
                /* Write the Wakeup register page value using opcode 0x12 */
                ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
@@ -2763,7 +2776,7 @@ static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
 
        if (page > 0 && page < HV_INTC_FC_PAGE_START) {
                ret_val = e1000_access_phy_debug_regs_hv(hw, offset,
-                                                        data, true);
+                                                        data, true);
                goto out;
        }
 
@@ -2786,8 +2799,7 @@ static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
        e_dbg("reading PHY page %d (or 0x%x shifted) reg 0x%x\n", page,
              page << IGP_PAGE_SHIFT, reg);
 
-       ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
-                                         data);
+       ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg, data);
 out:
        if (!locked)
                hw->phy.ops.release(hw);
@@ -2871,7 +2883,7 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
 
        if (page > 0 && page < HV_INTC_FC_PAGE_START) {
                ret_val = e1000_access_phy_debug_regs_hv(hw, offset,
-                                                        &data, false);
+                                                        &data, false);
                goto out;
        }
 
@@ -2910,7 +2922,7 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
              page << IGP_PAGE_SHIFT, reg);
 
        ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
-                                         data);
+                                           data);
 
 out:
        if (!locked)
@@ -2988,15 +3000,15 @@ static u32 e1000_get_phy_addr_for_hv_page(u32 page)
  *  These accesses done with PHY address 2 and without using pages.
  **/
 static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
-                                          u16 *data, bool read)
+                                         u16 *data, bool read)
 {
        s32 ret_val;
        u32 addr_reg;
        u32 data_reg;
 
        /* This takes care of the difference with desktop vs mobile phy */
-       addr_reg = (hw->phy.type == e1000_phy_82578) ?
-                  I82578_ADDR_REG : I82577_ADDR_REG;
+       addr_reg = ((hw->phy.type == e1000_phy_82578) ?
+                   I82578_ADDR_REG : I82577_ADDR_REG);
        data_reg = addr_reg + 1;
 
        /* All operations in this function are phy address 2 */
@@ -3050,8 +3062,8 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw)
        if (ret_val)
                return ret_val;
 
-       data &= BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED |
-               BM_CS_STATUS_SPEED_MASK;
+       data &= (BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED |
+                BM_CS_STATUS_SPEED_MASK);
 
        if (data != (BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED |
                     BM_CS_STATUS_SPEED_1000))
@@ -3086,9 +3098,9 @@ s32 e1000_check_polarity_82577(struct e1000_hw *hw)
        ret_val = e1e_rphy(hw, I82577_PHY_STATUS_2, &data);
 
        if (!ret_val)
-               phy->cable_polarity = (data & I82577_PHY_STATUS2_REV_POLARITY)
-                                     ? e1000_rev_polarity_reversed
-                                     : e1000_rev_polarity_normal;
+               phy->cable_polarity = ((data & I82577_PHY_STATUS2_REV_POLARITY)
+                                      ? e1000_rev_polarity_reversed
+                                      : e1000_rev_polarity_normal);
 
        return ret_val;
 }
@@ -3215,8 +3227,8 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw)
        if (ret_val)
                return ret_val;
 
-       length = (phy_data & I82577_DSTATUS_CABLE_LENGTH) >>
-                I82577_DSTATUS_CABLE_LENGTH_SHIFT;
+       length = ((phy_data & I82577_DSTATUS_CABLE_LENGTH) >>
+                 I82577_DSTATUS_CABLE_LENGTH_SHIFT);
 
        if (length == E1000_CABLE_LENGTH_UNDEFINED)
                return -E1000_ERR_PHY;
index 12b1d84808084269971e5f83f55bd8b0f3fb8240..c9bba39d50bd130cd581a801f7c2abfb33de73ef 100644 (file)
@@ -100,6 +100,7 @@ static bool igb_sgmii_uses_mdio_82575(struct e1000_hw *hw)
                break;
        case e1000_82580:
        case e1000_i350:
+       case e1000_i354:
        case e1000_i210:
        case e1000_i211:
                reg = rd32(E1000_MDICNFG);
@@ -149,6 +150,7 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
                switch (hw->mac.type) {
                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;
@@ -174,13 +176,14 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
 
        /* Verify phy id and set remaining function pointers */
        switch (phy->id) {
+       case M88E1545_E_PHY_ID:
        case I347AT4_E_PHY_ID:
        case M88E1112_E_PHY_ID:
        case M88E1111_I_PHY_ID:
                phy->type               = e1000_phy_m88;
+               phy->ops.check_polarity = igb_check_polarity_m88;
                phy->ops.get_phy_info   = igb_get_phy_info_m88;
-               if (phy->id == I347AT4_E_PHY_ID ||
-                   phy->id == M88E1112_E_PHY_ID)
+               if (phy->id != M88E1111_I_PHY_ID)
                        phy->ops.get_cable_length =
                                         igb_get_cable_length_m88_gen2;
                else
@@ -227,7 +230,7 @@ out:
  *  igb_init_nvm_params_82575 - Init NVM func ptrs.
  *  @hw: pointer to the HW structure
  **/
-s32 igb_init_nvm_params_82575(struct e1000_hw *hw)
+static s32 igb_init_nvm_params_82575(struct e1000_hw *hw)
 {
        struct e1000_nvm_info *nvm = &hw->nvm;
        u32 eecd = rd32(E1000_EECD);
@@ -287,6 +290,7 @@ s32 igb_init_nvm_params_82575(struct e1000_hw *hw)
                        nvm->ops.read = igb_read_nvm_spi;
                nvm->ops.write = igb_write_nvm_spi;
                break;
+       case e1000_i354:
        case e1000_i350:
                nvm->ops.validate = igb_validate_nvm_checksum_i350;
                nvm->ops.update = igb_update_nvm_checksum_i350;
@@ -352,6 +356,7 @@ static s32 igb_init_mac_params_82575(struct e1000_hw *hw)
                mac->rar_entry_count = E1000_RAR_ENTRIES_82580;
                break;
        case e1000_i350:
+       case e1000_i354:
                mac->rar_entry_count = E1000_RAR_ENTRIES_I350;
                break;
        default:
@@ -445,14 +450,18 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
        case E1000_DEV_ID_I211_COPPER:
                mac->type = e1000_i211;
                break;
+       case E1000_DEV_ID_I354_BACKPLANE_1GBPS:
+       case E1000_DEV_ID_I354_SGMII:
+       case E1000_DEV_ID_I354_BACKPLANE_2_5GBPS:
+               mac->type = e1000_i354;
+               break;
        default:
                return -E1000_ERR_MAC_INIT;
                break;
        }
 
        /* Set media type */
-       /*
-        * The 82575 uses bits 22:23 for link mode. The mode can be changed
+       /* The 82575 uses bits 22:23 for link mode. The mode can be changed
         * based on the EEPROM. We cannot rely upon device ID. There
         * is no distinguishable difference between fiber and internal
         * SerDes mode on the 82575. There can be an external PHY attached
@@ -621,8 +630,7 @@ static s32 igb_get_phy_id_82575(struct e1000_hw *hw)
        u32 ctrl_ext;
        u32 mdic;
 
-       /*
-        * For SGMII PHYs, we try the list of possible addresses until
+       /* For SGMII PHYs, we try the list of possible addresses until
         * we find one that works.  For non-SGMII PHYs
         * (e.g. integrated copper PHYs), an address of 1 should
         * work.  The result of this function should mean phy->phy_addr
@@ -644,6 +652,7 @@ static s32 igb_get_phy_id_82575(struct e1000_hw *hw)
                        break;
                case e1000_82580:
                case e1000_i350:
+               case e1000_i354:
                case e1000_i210:
                case e1000_i211:
                        mdic = rd32(E1000_MDICNFG);
@@ -665,8 +674,7 @@ static s32 igb_get_phy_id_82575(struct e1000_hw *hw)
        wrfl();
        msleep(300);
 
-       /*
-        * The address field in the I2CCMD register is 3 bits and 0 is invalid.
+       /* The address field in the I2CCMD register is 3 bits and 0 is invalid.
         * Therefore, we need to test 1-7
         */
        for (phy->addr = 1; phy->addr < 8; phy->addr++) {
@@ -674,8 +682,7 @@ static s32 igb_get_phy_id_82575(struct e1000_hw *hw)
                if (ret_val == 0) {
                        hw_dbg("Vendor ID 0x%08X read at address %u\n",
                               phy_id, phy->addr);
-                       /*
-                        * At the time of this writing, The M88 part is
+                       /* At the time of this writing, The M88 part is
                         * the only supported SGMII PHY product.
                         */
                        if (phy_id == M88_VENDOR)
@@ -711,15 +718,13 @@ static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *hw)
 {
        s32 ret_val;
 
-       /*
-        * This isn't a true "hard" reset, but is the only reset
+       /* This isn't a true "hard" reset, but is the only reset
         * available to us at this time.
         */
 
        hw_dbg("Soft resetting SGMII attached PHY...\n");
 
-       /*
-        * SFP documentation requires the following to configure the SPF module
+       /* SFP documentation requires the following to configure the SPF module
         * to work on SGMII.  No further documentation is given.
         */
        ret_val = hw->phy.ops.write_reg(hw, 0x1B, 0x8084);
@@ -774,8 +779,7 @@ static s32 igb_set_d0_lplu_state_82575(struct e1000_hw *hw, bool active)
                data &= ~IGP02E1000_PM_D0_LPLU;
                ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT,
                                                 data);
-               /*
-                * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
+               /* LPLU and SmartSpeed are mutually exclusive.  LPLU is used
                 * during Dx states where the power conservation is most
                 * important.  During driver activity we should enable
                 * SmartSpeed, so performance is maintained.
@@ -838,8 +842,7 @@ static s32 igb_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active)
        } else {
                data &= ~E1000_82580_PM_D0_LPLU;
 
-               /*
-                * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
+               /* LPLU and SmartSpeed are mutually exclusive.  LPLU is used
                 * during Dx states where the power conservation is most
                 * important.  During driver activity we should enable
                 * SmartSpeed, so performance is maintained.
@@ -867,7 +870,7 @@ static s32 igb_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active)
  *  During driver activity, SmartSpeed should be enabled so performance is
  *  maintained.
  **/
-s32 igb_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active)
+static s32 igb_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active)
 {
        struct e1000_phy_info *phy = &hw->phy;
        s32 ret_val = 0;
@@ -877,8 +880,7 @@ s32 igb_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active)
 
        if (!active) {
                data &= ~E1000_82580_PM_D3_LPLU;
-               /*
-                * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
+               /* LPLU and SmartSpeed are mutually exclusive.  LPLU is used
                 * during Dx states where the power conservation is most
                 * important.  During driver activity we should enable
                 * SmartSpeed, so performance is maintained.
@@ -964,8 +966,7 @@ static s32 igb_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask)
                if (!(swfw_sync & (fwmask | swmask)))
                        break;
 
-               /*
-                * Firmware currently using resource (fwmask)
+               /* Firmware currently using resource (fwmask)
                 * or other software thread using resource (swmask)
                 */
                igb_put_hw_semaphore(hw);
@@ -1065,8 +1066,7 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
        if (hw->phy.media_type != e1000_media_type_copper) {
                ret_val = igb_get_pcs_speed_and_duplex_82575(hw, &speed,
                                                             &duplex);
-               /*
-                * Use this flag to determine if link needs to be checked or
+               /* Use this flag to determine if link needs to be checked or
                 * not.  If  we have link clear the flag so that we do not
                 * continue to check for link.
                 */
@@ -1135,15 +1135,13 @@ static s32 igb_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, u16 *speed,
        *speed = 0;
        *duplex = 0;
 
-       /*
-        * Read the PCS Status register for link state. For non-copper mode,
+       /* Read the PCS Status register for link state. For non-copper mode,
         * the status register is not accurate. The PCS status register is
         * used instead.
         */
        pcs = rd32(E1000_PCS_LSTAT);
 
-       /*
-        * The link up bit determines when link is up on autoneg. The sync ok
+       /* The link up bit determines when link is up on autoneg. The sync ok
         * gets set once both sides sync up and agree upon link. Stable link
         * can be determined by checking for both link up and link sync ok
         */
@@ -1214,8 +1212,7 @@ static s32 igb_reset_hw_82575(struct e1000_hw *hw)
        u32 ctrl, icr;
        s32 ret_val;
 
-       /*
-        * Prevent the PCI-E bus from sticking if there is no TLP connection
+       /* Prevent the PCI-E bus from sticking if there is no TLP connection
         * on the last TLP read/write transaction when MAC is reset.
         */
        ret_val = igb_disable_pcie_master(hw);
@@ -1244,8 +1241,7 @@ static s32 igb_reset_hw_82575(struct e1000_hw *hw)
 
        ret_val = igb_get_auto_rd_done(hw);
        if (ret_val) {
-               /*
-                * When auto config read does not complete, do not
+               /* When auto config read does not complete, do not
                 * return with an error. This can happen in situations
                 * where there is no eeprom and prevents getting link.
                 */
@@ -1287,7 +1283,7 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
 
        /* Disabling VLAN filtering */
        hw_dbg("Initializing the IEEE VLAN\n");
-       if (hw->mac.type == e1000_i350)
+       if ((hw->mac.type == e1000_i350) || (hw->mac.type == e1000_i354))
                igb_clear_vfta_i350(hw);
        else
                igb_clear_vfta(hw);
@@ -1308,8 +1304,7 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
        /* Setup link and flow control */
        ret_val = igb_setup_link(hw);
 
-       /*
-        * Clear all of the statistics registers (clear on read).  It is
+       /* Clear all of the statistics registers (clear on read).  It is
         * important that we do this after we have tried to establish link
         * because the symbol error count will increment wildly if there
         * is no link.
@@ -1364,6 +1359,7 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
                switch (hw->phy.id) {
                case I347AT4_E_PHY_ID:
                case M88E1112_E_PHY_ID:
+               case M88E1545_E_PHY_ID:
                case I210_I_PHY_ID:
                        ret_val = igb_copper_link_setup_m88_gen2(hw);
                        break;
@@ -1412,17 +1408,17 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
                return ret_val;
 
 
-       /*
-        * On the 82575, SerDes loopback mode persists until it is
+       /* On the 82575, SerDes loopback mode persists until it is
         * explicitly turned off or a power cycle is performed.  A read to
         * the register does not indicate its status.  Therefore, we ensure
         * loopback mode is disabled during initialization.
         */
        wr32(E1000_SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK);
 
-       /* power on the sfp cage if present */
+       /* power on the sfp cage if present and turn on I2C */
        ctrl_ext = rd32(E1000_CTRL_EXT);
        ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA;
+       ctrl_ext |= E1000_CTRL_I2C_ENA;
        wr32(E1000_CTRL_EXT, ctrl_ext);
 
        ctrl_reg = rd32(E1000_CTRL);
@@ -1466,8 +1462,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
                                pcs_autoneg = false;
                }
 
-               /*
-                * non-SGMII modes only supports a speed of 1000/Full for the
+               /* non-SGMII modes only supports a speed of 1000/Full for the
                 * link so it is best to just force the MAC and let the pcs
                 * link either autoneg or be forced to 1000/Full
                 */
@@ -1481,8 +1476,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
 
        wr32(E1000_CTRL, ctrl_reg);
 
-       /*
-        * New SerDes mode allows for forcing speed or autonegotiating speed
+       /* New SerDes mode allows for forcing speed or autonegotiating speed
         * at 1gb. Autoneg should be default set by most drivers. This is the
         * mode that will be compatible with older link partners and switches.
         * However, both are supported by the hardware and some drivers/tools.
@@ -1592,8 +1586,7 @@ static s32 igb_read_mac_addr_82575(struct e1000_hw *hw)
 {
        s32 ret_val = 0;
 
-       /*
-        * If there's an alternate MAC address place it in RAR0
+       /* If there's an alternate MAC address place it in RAR0
         * so that it will override the Si installed default perm
         * address.
         */
@@ -1777,8 +1770,7 @@ static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw)
        if (gcr & E1000_GCR_CMPL_TMOUT_MASK)
                goto out;
 
-       /*
-        * if capababilities version is type 1 we can write the
+       /* if capabilities version is type 1 we can write the
         * timeout of 10ms to 200ms through the GCR register
         */
        if (!(gcr & E1000_GCR_CAP_VER2)) {
@@ -1786,8 +1778,7 @@ static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw)
                goto out;
        }
 
-       /*
-        * for version 2 capabilities we need to write the config space
+       /* for version 2 capabilities we need to write the config space
         * directly in order to set the completion timeout value for
         * 16ms to 55ms
         */
@@ -1825,6 +1816,7 @@ void igb_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf)
                reg_offset = E1000_DTXSWC;
                break;
        case e1000_i350:
+       case e1000_i354:
                reg_offset = E1000_TXSWC;
                break;
        default:
@@ -1866,6 +1858,7 @@ void igb_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable)
                        dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN;
                wr32(E1000_DTXSWC, dtxswc);
                break;
+       case e1000_i354:
        case e1000_i350:
                dtxswc = rd32(E1000_TXSWC);
                if (enable)
@@ -1879,7 +1872,6 @@ void igb_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable)
                break;
        }
 
-
 }
 
 /**
@@ -1914,7 +1906,6 @@ static s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data)
 {
        s32 ret_val;
 
-
        ret_val = hw->phy.ops.acquire(hw);
        if (ret_val)
                goto out;
@@ -2016,8 +2007,7 @@ static s32 igb_reset_hw_82580(struct e1000_hw *hw)
        /* Get current control state. */
        ctrl = rd32(E1000_CTRL);
 
-       /*
-        * Prevent the PCI-E bus from sticking if there is no TLP connection
+       /* Prevent the PCI-E bus from sticking if there is no TLP connection
         * on the last TLP read/write transaction when MAC is reset.
         */
        ret_val = igb_disable_pcie_master(hw);
@@ -2052,8 +2042,7 @@ static s32 igb_reset_hw_82580(struct e1000_hw *hw)
 
        ret_val = igb_get_auto_rd_done(hw);
        if (ret_val) {
-               /*
-                * When auto config read does not complete, do not
+               /* When auto config read does not complete, do not
                 * return with an error. This can happen in situations
                 * where there is no eeprom and prevents getting link.
                 */
@@ -2197,7 +2186,8 @@ static s32 igb_validate_nvm_checksum_82580(struct e1000_hw *hw)
 
        if (nvm_data & NVM_COMPATIBILITY_BIT_MASK) {
                /* if checksums compatibility bit is set validate checksums
-                * for all 4 ports. */
+                * for all 4 ports.
+                */
                eeprom_regions_count = 4;
        }
 
@@ -2308,6 +2298,41 @@ out:
        return ret_val;
 }
 
+/**
+ *  __igb_access_emi_reg - Read/write EMI register
+ *  @hw: pointer to the HW structure
+ *  @addr: EMI address to program
+ *  @data: pointer to value to read/write from/to the EMI address
+ *  @read: boolean flag to indicate read or write
+ **/
+static s32 __igb_access_emi_reg(struct e1000_hw *hw, u16 address,
+                                 u16 *data, bool read)
+{
+       s32 ret_val = E1000_SUCCESS;
+
+       ret_val = hw->phy.ops.write_reg(hw, E1000_EMIADD, address);
+       if (ret_val)
+               return ret_val;
+
+       if (read)
+               ret_val = hw->phy.ops.read_reg(hw, E1000_EMIDATA, data);
+       else
+               ret_val = hw->phy.ops.write_reg(hw, E1000_EMIDATA, *data);
+
+       return ret_val;
+}
+
+/**
+ *  igb_read_emi_reg - Read Extended Management Interface register
+ *  @hw: pointer to the HW structure
+ *  @addr: EMI address to program
+ *  @data: value to be read from the EMI address
+ **/
+s32 igb_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data)
+{
+       return __igb_access_emi_reg(hw, addr, data, true);
+}
+
 /**
  *  igb_set_eee_i350 - Enable/disable EEE support
  *  @hw: pointer to the HW structure
@@ -2338,7 +2363,6 @@ s32 igb_set_eee_i350(struct e1000_hw *hw)
                if (eee_su & E1000_EEE_SU_LPI_CLK_STP)
                        hw_dbg("LPI Clock Stop Bit should not be set!\n");
 
-
        } else {
                ipcnfg &= ~(E1000_IPCNFG_EEE_1G_AN |
                        E1000_IPCNFG_EEE_100M_AN);
@@ -2355,6 +2379,108 @@ out:
        return ret_val;
 }
 
+/**
+ *  igb_set_eee_i354 - Enable/disable EEE support
+ *  @hw: pointer to the HW structure
+ *
+ *  Enable/disable EEE legacy mode based on setting in dev_spec structure.
+ *
+ **/
+s32 igb_set_eee_i354(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = 0;
+       u16 phy_data;
+
+       if ((hw->phy.media_type != e1000_media_type_copper) ||
+           (phy->id != M88E1545_E_PHY_ID))
+               goto out;
+
+       if (!hw->dev_spec._82575.eee_disable) {
+               /* Switch to PHY page 18. */
+               ret_val = phy->ops.write_reg(hw, E1000_M88E1545_PAGE_ADDR, 18);
+               if (ret_val)
+                       goto out;
+
+               ret_val = phy->ops.read_reg(hw, E1000_M88E1545_EEE_CTRL_1,
+                                           &phy_data);
+               if (ret_val)
+                       goto out;
+
+               phy_data |= E1000_M88E1545_EEE_CTRL_1_MS;
+               ret_val = phy->ops.write_reg(hw, E1000_M88E1545_EEE_CTRL_1,
+                                            phy_data);
+               if (ret_val)
+                       goto out;
+
+               /* Return the PHY to page 0. */
+               ret_val = phy->ops.write_reg(hw, E1000_M88E1545_PAGE_ADDR, 0);
+               if (ret_val)
+                       goto out;
+
+               /* Turn on EEE advertisement. */
+               ret_val = igb_read_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354,
+                                            E1000_EEE_ADV_DEV_I354,
+                                            &phy_data);
+               if (ret_val)
+                       goto out;
+
+               phy_data |= E1000_EEE_ADV_100_SUPPORTED |
+                           E1000_EEE_ADV_1000_SUPPORTED;
+               ret_val = igb_write_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354,
+                                               E1000_EEE_ADV_DEV_I354,
+                                               phy_data);
+       } else {
+               /* Turn off EEE advertisement. */
+               ret_val = igb_read_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354,
+                                            E1000_EEE_ADV_DEV_I354,
+                                            &phy_data);
+               if (ret_val)
+                       goto out;
+
+               phy_data &= ~(E1000_EEE_ADV_100_SUPPORTED |
+                             E1000_EEE_ADV_1000_SUPPORTED);
+               ret_val = igb_write_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354,
+                                             E1000_EEE_ADV_DEV_I354,
+                                             phy_data);
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  igb_get_eee_status_i354 - Get EEE status
+ *  @hw: pointer to the HW structure
+ *  @status: EEE status
+ *
+ *  Get EEE status by guessing based on whether Tx or Rx LPI indications have
+ *  been received.
+ **/
+s32 igb_get_eee_status_i354(struct e1000_hw *hw, bool *status)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = 0;
+       u16 phy_data;
+
+       /* Check if EEE is supported on this device. */
+       if ((hw->phy.media_type != e1000_media_type_copper) ||
+           (phy->id != M88E1545_E_PHY_ID))
+               goto out;
+
+       ret_val = igb_read_xmdio_reg(hw, E1000_PCS_STATUS_ADDR_I354,
+                                    E1000_PCS_STATUS_DEV_I354,
+                                    &phy_data);
+       if (ret_val)
+               goto out;
+
+       *status = phy_data & (E1000_PCS_STATUS_TX_LPI_RCVD |
+                             E1000_PCS_STATUS_RX_LPI_RCVD) ? true : false;
+
+out:
+       return ret_val;
+}
+
 static const u8 e1000_emc_temp_data[4] = {
        E1000_EMC_INTERNAL_DATA,
        E1000_EMC_DIODE1_DATA,
@@ -2368,11 +2494,12 @@ static const u8 e1000_emc_therm_limit[4] = {
        E1000_EMC_DIODE3_THERM_LIMIT
 };
 
-/* igb_get_thermal_sensor_data_generic - Gathers thermal sensor data
+/**
+ *  igb_get_thermal_sensor_data_generic - Gathers thermal sensor data
  *  @hw: pointer to hardware structure
  *
  *  Updates the temperatures in mac.thermal_sensor_data
- */
+ **/
 s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
 {
        s32 status = E1000_SUCCESS;
@@ -2420,12 +2547,13 @@ s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
        return status;
 }
 
-/* igb_init_thermal_sensor_thresh_generic - Sets thermal sensor thresholds
+/**
+ *  igb_init_thermal_sensor_thresh_generic - Sets thermal sensor thresholds
  *  @hw: pointer to hardware structure
  *
  *  Sets the thermal sensor thresholds according to the NVM map
  *  and save off the threshold and location values into mac.thermal_sensor_data
- */
+ **/
 s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
 {
        s32 status = E1000_SUCCESS;
index 73ab41f0e032009422cf3488c851ea118cd0109c..74a1506b42359d02305ea9d78cda7e536633559f 100644 (file)
@@ -263,7 +263,9 @@ void igb_vmdq_set_anti_spoofing_pf(struct e1000_hw *, bool, int);
 void igb_vmdq_set_loopback_pf(struct e1000_hw *, bool);
 void igb_vmdq_set_replication_pf(struct e1000_hw *, bool);
 u16 igb_rxpbs_adjust_82580(u32 data);
+s32 igb_read_emi_reg(struct e1000_hw *, u16 addr, u16 *data);
 s32 igb_set_eee_i350(struct e1000_hw *);
+s32 igb_set_eee_i354(struct e1000_hw *);
 s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *);
 s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw);
 
index 7e13337d3b9d519bb33b3df0f362d1d63f77cf69..31a0f82cc650b9ae1eca5940d2f939632c337dcc 100644 (file)
 #define E1000_RCTL_PMCF           0x00800000    /* pass MAC control frames */
 #define E1000_RCTL_SECRC          0x04000000    /* Strip Ethernet CRC */
 
-/*
- * Use byte values for the following shift parameters
+/* Use byte values for the following shift parameters
  * Usage:
  *     psrctl |= (((ROUNDUP(value0, 128) >> E1000_PSRCTL_BSIZE0_SHIFT) &
  *                  E1000_PSRCTL_BSIZE0_MASK) |
 #define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000
 /* BMC external code execution disabled */
 
+#define E1000_STATUS_2P5_SKU           0x00001000 /* Val of 2.5GBE SKU strap */
+#define E1000_STATUS_2P5_SKU_OVER      0x00002000 /* Val of 2.5GBE SKU Over */
 /* Constants used to intrepret the masked PCI-X bus speed. */
 
 #define SPEED_10    10
 #define SPEED_100   100
 #define SPEED_1000  1000
+#define SPEED_2500  2500
 #define HALF_DUPLEX 1
 #define FULL_DUPLEX 2
 
 #define E1000_EICR_OTHER        0x80000000 /* Interrupt Cause Active */
 /* TCP Timer */
 
-/*
- * This defines the bits that are set in the Interrupt Mask
+/* This defines the bits that are set in the Interrupt Mask
  * Set/Read Register.  Each bit is documented below:
  *   o RXT0   = Receiver Timer Interrupt (ring 0)
  *   o TXDW   = Transmit Descriptor Written Back
 #define E1000_VLAN_FILTER_TBL_SIZE 128  /* VLAN Filter Table (4096 bits) */
 
 /* Receive Address */
-/*
- * Number of high/low register pairs in the RAR. The RAR (Receive Address
+/* Number of high/low register pairs in the RAR. The RAR (Receive Address
  * Registers) holds the directed and multicast addresses that we monitor.
  * Technically, we have 16 spots.  However, we reserve one of these spots
  * (RAR[15]) for our directed address used by controllers with
 #define MAX_PHY_MULTI_PAGE_REG 0xF
 
 /* Bit definitions for valid PHY IDs. */
-/*
- * I = Integrated
+/* I = Integrated
  * E = External
  */
 #define M88E1111_I_PHY_ID    0x01410CC0
 #define I350_I_PHY_ID        0x015403B0
 #define M88_VENDOR           0x0141
 #define I210_I_PHY_ID        0x01410C00
+#define M88E1545_E_PHY_ID    0x01410EA0
 
 /* M88E1000 Specific Registers */
 #define M88E1000_PHY_SPEC_CTRL     0x10  /* PHY Specific Control Register */
 #define M88E1000_PSCR_AUTO_X_1000T     0x0040
 /* Auto crossover enabled all speeds */
 #define M88E1000_PSCR_AUTO_X_MODE      0x0060
-/*
- * 1=Enable Extended 10BASE-T distance (Lower 10BASE-T Rx Threshold
+/* 1=Enable Extended 10BASE-T distance (Lower 10BASE-T Rx Threshold
  * 0=Normal 10BASE-T Rx Threshold
  */
 /* 1=5-bit interface in 100BASE-TX, 0=MII interface in 100BASE-TX */
 #define M88E1000_PSSR_REV_POLARITY       0x0002 /* 1=Polarity reversed */
 #define M88E1000_PSSR_DOWNSHIFT          0x0020 /* 1=Downshifted */
 #define M88E1000_PSSR_MDIX               0x0040 /* 1=MDIX; 0=MDI */
-/*
- * 0 = <50M
+/* 0 = <50M
  * 1 = 50-80M
  * 2 = 80-110M
  * 3 = 110-140M
 #define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7
 
 /* M88E1000 Extended PHY Specific Control Register */
-/*
- * 1 = Lost lock detect enabled.
+/* 1 = Lost lock detect enabled.
  * Will assert lost lock and bring
  * link down if idle not seen
  * within 1ms in 1000BASE-T
  */
-/*
- * Number of times we will attempt to autonegotiate before downshifting if we
+/* Number of times we will attempt to autonegotiate before downshifting if we
  * are the master
  */
 #define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00
 #define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X   0x0000
-/*
- * Number of times we will attempt to autonegotiate before downshifting if we
+/* Number of times we will attempt to autonegotiate before downshifting if we
  * are the slave
  */
 #define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK  0x0300
 
 /* i347-AT4 Extended PHY Specific Control Register */
 
-/*
- *  Number of times we will attempt to autonegotiate before downshifting if we
+/*  Number of times we will attempt to autonegotiate before downshifting if we
  *  are the master
  */
 #define I347AT4_PSCR_DOWNSHIFT_ENABLE 0x0800
 #define E1000_EEER_LPI_FC            0x00040000  /* EEE Enable on FC */
 #define E1000_EEE_SU_LPI_CLK_STP     0X00800000  /* EEE LPI Clock Stop */
 #define E1000_EEER_EEE_NEG           0x20000000  /* EEE capability nego */
+#define E1000_EEE_LP_ADV_ADDR_I350   0x040F      /* EEE LP Advertisement */
+#define E1000_EEE_LP_ADV_DEV_I210    7           /* EEE LP Adv Device */
+#define E1000_EEE_LP_ADV_ADDR_I210   61          /* EEE LP Adv Register */
+#define E1000_MMDAC_FUNC_DATA        0x4000      /* Data, no post increment */
+#define E1000_M88E1545_PAGE_ADDR       0x16       /* Page Offset Register */
+#define E1000_M88E1545_EEE_CTRL_1      0x0
+#define E1000_M88E1545_EEE_CTRL_1_MS   0x0001     /* EEE Master/Slave */
+#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 */
+#define E1000_EEE_ADV_1000_SUPPORTED   (1 << 2)   /* 1000BaseT EEE Supported */
+#define E1000_PCS_STATUS_DEV_I354      3
+#define E1000_PCS_STATUS_ADDR_I354     1
+#define E1000_PCS_STATUS_TX_LPI_IND    0x0200     /* Tx in LPI state */
+#define E1000_PCS_STATUS_RX_LPI_RCVD   0x0400
+#define E1000_PCS_STATUS_TX_LPI_RCVD   0x0800
 
 /* SerDes Control */
 #define E1000_GEN_CTL_READY             0x80000000
index 0d5cf9c63d0d1192930fdda4e5f12bf254dc3974..1138ccaf95ff13ad3a78eb74c1a74f6dcccd010d 100644 (file)
 
 struct e1000_hw;
 
-#define E1000_DEV_ID_82576                    0x10C9
-#define E1000_DEV_ID_82576_FIBER              0x10E6
-#define E1000_DEV_ID_82576_SERDES             0x10E7
-#define E1000_DEV_ID_82576_QUAD_COPPER        0x10E8
-#define E1000_DEV_ID_82576_QUAD_COPPER_ET2    0x1526
-#define E1000_DEV_ID_82576_NS                 0x150A
-#define E1000_DEV_ID_82576_NS_SERDES          0x1518
-#define E1000_DEV_ID_82576_SERDES_QUAD        0x150D
-#define E1000_DEV_ID_82575EB_COPPER           0x10A7
-#define E1000_DEV_ID_82575EB_FIBER_SERDES     0x10A9
-#define E1000_DEV_ID_82575GB_QUAD_COPPER      0x10D6
-#define E1000_DEV_ID_82580_COPPER             0x150E
-#define E1000_DEV_ID_82580_FIBER              0x150F
-#define E1000_DEV_ID_82580_SERDES             0x1510
-#define E1000_DEV_ID_82580_SGMII              0x1511
-#define E1000_DEV_ID_82580_COPPER_DUAL        0x1516
-#define E1000_DEV_ID_82580_QUAD_FIBER         0x1527
-#define E1000_DEV_ID_DH89XXCC_SGMII           0x0438
-#define E1000_DEV_ID_DH89XXCC_SERDES          0x043A
-#define E1000_DEV_ID_DH89XXCC_BACKPLANE       0x043C
-#define E1000_DEV_ID_DH89XXCC_SFP             0x0440
-#define E1000_DEV_ID_I350_COPPER              0x1521
-#define E1000_DEV_ID_I350_FIBER               0x1522
-#define E1000_DEV_ID_I350_SERDES              0x1523
-#define E1000_DEV_ID_I350_SGMII               0x1524
+#define E1000_DEV_ID_82576                     0x10C9
+#define E1000_DEV_ID_82576_FIBER               0x10E6
+#define E1000_DEV_ID_82576_SERDES              0x10E7
+#define E1000_DEV_ID_82576_QUAD_COPPER         0x10E8
+#define E1000_DEV_ID_82576_QUAD_COPPER_ET2     0x1526
+#define E1000_DEV_ID_82576_NS                  0x150A
+#define E1000_DEV_ID_82576_NS_SERDES           0x1518
+#define E1000_DEV_ID_82576_SERDES_QUAD         0x150D
+#define E1000_DEV_ID_82575EB_COPPER            0x10A7
+#define E1000_DEV_ID_82575EB_FIBER_SERDES      0x10A9
+#define E1000_DEV_ID_82575GB_QUAD_COPPER       0x10D6
+#define E1000_DEV_ID_82580_COPPER              0x150E
+#define E1000_DEV_ID_82580_FIBER               0x150F
+#define E1000_DEV_ID_82580_SERDES              0x1510
+#define E1000_DEV_ID_82580_SGMII               0x1511
+#define E1000_DEV_ID_82580_COPPER_DUAL         0x1516
+#define E1000_DEV_ID_82580_QUAD_FIBER          0x1527
+#define E1000_DEV_ID_DH89XXCC_SGMII            0x0438
+#define E1000_DEV_ID_DH89XXCC_SERDES           0x043A
+#define E1000_DEV_ID_DH89XXCC_BACKPLANE                0x043C
+#define E1000_DEV_ID_DH89XXCC_SFP              0x0440
+#define E1000_DEV_ID_I350_COPPER               0x1521
+#define E1000_DEV_ID_I350_FIBER                        0x1522
+#define E1000_DEV_ID_I350_SERDES               0x1523
+#define E1000_DEV_ID_I350_SGMII                        0x1524
 #define E1000_DEV_ID_I210_COPPER               0x1533
 #define E1000_DEV_ID_I210_COPPER_OEM1          0x1534
 #define E1000_DEV_ID_I210_COPPER_IT            0x1535
@@ -70,6 +70,9 @@ struct e1000_hw;
 #define E1000_DEV_ID_I210_SERDES               0x1537
 #define E1000_DEV_ID_I210_SGMII                        0x1538
 #define E1000_DEV_ID_I211_COPPER               0x1539
+#define E1000_DEV_ID_I354_BACKPLANE_1GBPS      0x1F40
+#define E1000_DEV_ID_I354_SGMII                        0x1F41
+#define E1000_DEV_ID_I354_BACKPLANE_2_5GBPS    0x1F45
 
 #define E1000_REVISION_2 2
 #define E1000_REVISION_4 4
@@ -90,6 +93,7 @@ enum e1000_mac_type {
        e1000_82576,
        e1000_82580,
        e1000_i350,
+       e1000_i354,
        e1000_i210,
        e1000_i211,
        e1000_num_macs  /* List is 1-based, so subtract 1 for true count. */
@@ -98,7 +102,8 @@ enum e1000_mac_type {
 enum e1000_media_type {
        e1000_media_type_unknown = 0,
        e1000_media_type_copper = 1,
-       e1000_media_type_internal_serdes = 2,
+       e1000_media_type_fiber = 2,
+       e1000_media_type_internal_serdes = 3,
        e1000_num_media_types
 };
 
index 6a42344f24f1805ed6b3e2fe1c4afa98cb1534cf..9764cd3610e5490edd8c24c82c7d19f0b2b612f0 100644 (file)
@@ -103,7 +103,7 @@ void igb_release_nvm_i210(struct e1000_hw *hw)
  *  @hw: pointer to the HW structure
  *
  *  Release hardware semaphore used to access the PHY or NVM
- */
+ **/
 static void igb_put_hw_semaphore_i210(struct e1000_hw *hw)
 {
        u32 swsm;
@@ -141,9 +141,7 @@ s32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
                if (!(swfw_sync & fwmask))
                        break;
 
-               /*
-                * Firmware currently using resource (fwmask)
-                */
+               /* Firmware currently using resource (fwmask) */
                igb_put_hw_semaphore_i210(hw);
                mdelay(5);
                i++;
@@ -203,7 +201,8 @@ s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
 
        /* We cannot hold synchronization semaphores for too long,
         * because of forceful takeover procedure. However it is more efficient
-        * to read in bursts than synchronizing access for each word. */
+        * to read in bursts than synchronizing access for each word.
+        */
        for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
                count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
                        E1000_EERD_EEWR_MAX_COUNT : (words - i);
@@ -242,8 +241,7 @@ static s32 igb_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
        u32 attempts = 100000;
        s32 ret_val = E1000_SUCCESS;
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* A check for invalid values:  offset too large, too many words,
         * too many words for the offset, and not enough words.
         */
        if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
@@ -294,7 +292,7 @@ out:
  *
  *  If error code is returned, data and Shadow RAM may be inconsistent - buffer
  *  partially written.
- */
+ **/
 s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
                              u16 *data)
 {
@@ -326,7 +324,7 @@ s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
 /**
  *  igb_read_nvm_i211 - Read NVM wrapper function for I211
  *  @hw: pointer to the HW structure
- *  @address: the word address (aka eeprom offset) to read
+ *  @words: number of words to read
  *  @data: pointer to the data read
  *
  *  Wrapper function to return data formerly found in the NVM.
@@ -549,8 +547,7 @@ s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
 
        if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
 
-               /*
-                * Replace the read function with semaphore grabbing with
+               /* Replace the read function with semaphore grabbing with
                 * the one that skips this for a while.
                 * We have semaphore taken already here.
                 */
@@ -570,7 +567,6 @@ s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
        return status;
 }
 
-
 /**
  *  igb_update_nvm_checksum_i210 - Update EEPROM checksum
  *  @hw: pointer to the HW structure
@@ -585,8 +581,7 @@ s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
        u16 checksum = 0;
        u16 i, nvm_data;
 
-       /*
-        * Read the first word from the EEPROM. If this times out or fails, do
+       /* Read the first word from the EEPROM. If this times out or fails, do
         * not continue or we could be in for a very long wait while every
         * EEPROM read fails
         */
@@ -597,8 +592,7 @@ s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
        }
 
        if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
-               /*
-                * Do not use hw->nvm.ops.write, hw->nvm.ops.read
+               /* Do not use hw->nvm.ops.write, hw->nvm.ops.read
                 * because we do not want to take the synchronization
                 * semaphores twice here.
                 */
@@ -635,7 +629,7 @@ out:
  *  igb_pool_flash_update_done_i210 - Pool FLUDONE status.
  *  @hw: pointer to the HW structure
  *
- */
+ **/
 static s32 igb_pool_flash_update_done_i210(struct e1000_hw *hw)
 {
        s32 ret_val = -E1000_ERR_NVM;
@@ -714,3 +708,68 @@ s32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data)
 out:
        return ret_val;
 }
+
+/**
+ *  __igb_access_xmdio_reg - Read/write XMDIO register
+ *  @hw: pointer to the HW structure
+ *  @address: XMDIO address to program
+ *  @dev_addr: device address to program
+ *  @data: pointer to value to read/write from/to the XMDIO address
+ *  @read: boolean flag to indicate read or write
+ **/
+static s32 __igb_access_xmdio_reg(struct e1000_hw *hw, u16 address,
+                                 u8 dev_addr, u16 *data, bool read)
+{
+       s32 ret_val = E1000_SUCCESS;
+
+       ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, dev_addr);
+       if (ret_val)
+               return ret_val;
+
+       ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, address);
+       if (ret_val)
+               return ret_val;
+
+       ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, E1000_MMDAC_FUNC_DATA |
+                                                        dev_addr);
+       if (ret_val)
+               return ret_val;
+
+       if (read)
+               ret_val = hw->phy.ops.read_reg(hw, E1000_MMDAAD, data);
+       else
+               ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, *data);
+       if (ret_val)
+               return ret_val;
+
+       /* Recalibrate the device back to 0 */
+       ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, 0);
+       if (ret_val)
+               return ret_val;
+
+       return ret_val;
+}
+
+/**
+ *  igb_read_xmdio_reg - Read XMDIO register
+ *  @hw: pointer to the HW structure
+ *  @addr: XMDIO address to program
+ *  @dev_addr: device address to program
+ *  @data: value to be read from the EMI address
+ **/
+s32 igb_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 *data)
+{
+       return __igb_access_xmdio_reg(hw, addr, dev_addr, data, true);
+}
+
+/**
+ *  igb_write_xmdio_reg - Write XMDIO register
+ *  @hw: pointer to the HW structure
+ *  @addr: XMDIO address to program
+ *  @dev_addr: device address to program
+ *  @data: value to be written to the XMDIO address
+ **/
+s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data)
+{
+       return __igb_access_xmdio_reg(hw, addr, dev_addr, &data, false);
+}
index e4e1a73b7c75a145a17641f3302355107e8b4f4c..bfc08e05c90741849d3445cbca337f98d466db2d 100644 (file)
@@ -45,6 +45,10 @@ extern s32 igb_read_nvm_i211(struct e1000_hw *hw, u16 offset, u16 words,
                               u16 *data);
 extern s32 igb_read_invm_version(struct e1000_hw *hw,
                                 struct e1000_fw_version *invm_ver);
+extern s32 igb_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr,
+                             u16 *data);
+extern s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr,
+                              u16 data);
 
 #define E1000_STM_OPCODE               0xDB00
 #define E1000_EEPROM_FLASH_SIZE_WORD   0x11
index a5c7200b9a71b8637a31de1c74fcfa6eb48018d3..2559d70a2321b717b05b167a2c1c3a5b4f9f44a6 100644 (file)
@@ -214,7 +214,7 @@ s32 igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add)
                else
                        vfta &= ~mask;
        }
-       if (hw->mac.type == e1000_i350)
+       if ((hw->mac.type == e1000_i350) || (hw->mac.type == e1000_i354))
                igb_write_vfta_i350(hw, index, vfta);
        else
                igb_write_vfta(hw, index, vfta);
@@ -230,8 +230,8 @@ s32 igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add)
  *  Checks the nvm for an alternate MAC address.  An alternate MAC address
  *  can be setup by pre-boot software and must be treated like a permanent
  *  address and must override the actual permanent MAC address.  If an
- *  alternate MAC address is fopund it is saved in the hw struct and
- *  prgrammed into RAR0 and the cuntion returns success, otherwise the
+ *  alternate MAC address is found it is saved in the hw struct and
+ *  programmed into RAR0 and the function returns success, otherwise the
  *  function returns an error.
  **/
 s32 igb_check_alt_mac_addr(struct e1000_hw *hw)
@@ -241,8 +241,7 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw)
        u16 offset, nvm_alt_mac_addr_offset, nvm_data;
        u8 alt_mac_addr[ETH_ALEN];
 
-       /*
-        * Alternate MAC address is handled by the option ROM for 82580
+       /* Alternate MAC address is handled by the option ROM for 82580
         * and newer. SW support not required.
         */
        if (hw->mac.type >= e1000_82580)
@@ -285,8 +284,7 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw)
                goto out;
        }
 
-       /*
-        * We have a valid alternate MAC address, and we want to treat it the
+       /* We have a valid alternate MAC address, and we want to treat it the
         * same as the normal permanent MAC address stored by the HW into the
         * RAR. Do this by mapping this address into RAR0.
         */
@@ -309,8 +307,7 @@ void igb_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
 {
        u32 rar_low, rar_high;
 
-       /*
-        * HW expects these in little endian so we reverse the byte order
+       /* 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] |
@@ -323,8 +320,7 @@ void igb_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
        if (rar_low || rar_high)
                rar_high |= E1000_RAH_AV;
 
-       /*
-        * Some bridges will combine consecutive 32-bit writes into
+       /* Some bridges will combine consecutive 32-bit writes into
         * a single burst write, which will malfunction on some parts.
         * The flushes avoid this.
         */
@@ -348,8 +344,7 @@ void igb_mta_set(struct e1000_hw *hw, u32 hash_value)
 {
        u32 hash_bit, hash_reg, mta;
 
-       /*
-        * The MTA is a register array of 32-bit registers. It is
+       /* The MTA is a register array of 32-bit registers. It is
         * treated like an array of (32*mta_reg_count) bits.  We want to
         * set bit BitArray[hash_value]. So we figure out what register
         * the bit is in, read it, OR in the new bit, then write
@@ -386,15 +381,13 @@ static u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr)
        /* Register count multiplied by bits per register */
        hash_mask = (hw->mac.mta_reg_count * 32) - 1;
 
-       /*
-        * For a mc_filter_type of 0, bit_shift is the number of left-shifts
+       /* For a mc_filter_type of 0, bit_shift is the number of left-shifts
         * where 0xFF would still fall within the hash mask.
         */
        while (hash_mask >> bit_shift != 0xFF)
                bit_shift++;
 
-       /*
-        * The portion of the address that is used for the hash table
+       /* The portion of the address that is used for the hash table
         * is determined by the mc_filter_type setting.
         * The algorithm is such that there is a total of 8 bits of shifting.
         * The bit_shift for a mc_filter_type of 0 represents the number of
@@ -536,8 +529,7 @@ s32 igb_check_for_copper_link(struct e1000_hw *hw)
        s32 ret_val;
        bool link;
 
-       /*
-        * We only want to go out to the PHY registers to see if Auto-Neg
+       /* We only want to go out to the PHY registers to see if Auto-Neg
         * has completed and/or if our link status has changed.  The
         * get_link_status flag is set upon receiving a Link Status
         * Change or Rx Sequence Error interrupt.
@@ -547,8 +539,7 @@ s32 igb_check_for_copper_link(struct e1000_hw *hw)
                goto out;
        }
 
-       /*
-        * First we want to see if the MII Status Register reports
+       /* First we want to see if the MII Status Register reports
         * link.  If so, then we want to get the current speed/duplex
         * of the PHY.
         */
@@ -561,14 +552,12 @@ s32 igb_check_for_copper_link(struct e1000_hw *hw)
 
        mac->get_link_status = false;
 
-       /*
-        * Check if there was DownShift, must be checked
+       /* Check if there was DownShift, must be checked
         * immediately after link-up
         */
        igb_check_downshift(hw);
 
-       /*
-        * If we are forcing speed/duplex, then we simply return since
+       /* If we are forcing speed/duplex, then we simply return since
         * we have already determined whether we have link or not.
         */
        if (!mac->autoneg) {
@@ -576,15 +565,13 @@ s32 igb_check_for_copper_link(struct e1000_hw *hw)
                goto out;
        }
 
-       /*
-        * Auto-Neg is enabled.  Auto Speed Detection takes care
+       /* Auto-Neg is enabled.  Auto Speed Detection takes care
         * of MAC speed/duplex configuration.  So we only need to
         * configure Collision Distance in the MAC.
         */
        igb_config_collision_dist(hw);
 
-       /*
-        * Configure Flow Control now that Auto-Neg has completed.
+       /* Configure Flow Control now that Auto-Neg has completed.
         * First, we need to restore the desired flow control
         * settings because we may have had to re-autoneg with a
         * different link partner.
@@ -611,15 +598,13 @@ s32 igb_setup_link(struct e1000_hw *hw)
 {
        s32 ret_val = 0;
 
-       /*
-        * In the case of the phy reset being blocked, we already have a link.
+       /* In the case of the phy reset being blocked, we already have a link.
         * We do not need to set it up again.
         */
        if (igb_check_reset_block(hw))
                goto out;
 
-       /*
-        * If requested flow control is set to default, set flow control
+       /* If requested flow control is set to default, set flow control
         * based on the EEPROM flow control settings.
         */
        if (hw->fc.requested_mode == e1000_fc_default) {
@@ -628,8 +613,7 @@ s32 igb_setup_link(struct e1000_hw *hw)
                        goto out;
        }
 
-       /*
-        * We want to save off the original Flow Control configuration just
+       /* We want to save off the original Flow Control configuration just
         * in case we get disconnected and then reconnected into a different
         * hub or switch with different Flow Control capabilities.
         */
@@ -642,8 +626,7 @@ s32 igb_setup_link(struct e1000_hw *hw)
        if (ret_val)
                goto out;
 
-       /*
-        * Initialize the flow control address, type, and PAUSE timer
+       /* Initialize the flow control address, type, and PAUSE timer
         * registers to their default values.  This is done even if flow
         * control is disabled, because it does not hurt anything to
         * initialize these registers.
@@ -696,16 +679,14 @@ static s32 igb_set_fc_watermarks(struct e1000_hw *hw)
        s32 ret_val = 0;
        u32 fcrtl = 0, fcrth = 0;
 
-       /*
-        * Set the flow control receive threshold registers.  Normally,
+       /* Set the flow control receive threshold registers.  Normally,
         * these registers will be set to a default threshold that may be
         * adjusted later by the driver's runtime code.  However, if the
         * ability to transmit pause frames is not enabled, then these
         * registers will be set to 0.
         */
        if (hw->fc.current_mode & e1000_fc_tx_pause) {
-               /*
-                * We need to set up the Receive Threshold high and low water
+               /* We need to set up the Receive Threshold high and low water
                 * marks as well as (optionally) enabling the transmission of
                 * XON frames.
                 */
@@ -733,8 +714,7 @@ static s32 igb_set_default_fc(struct e1000_hw *hw)
        s32 ret_val = 0;
        u16 nvm_data;
 
-       /*
-        * Read and store word 0x0F of the EEPROM. This word contains bits
+       /* Read and store word 0x0F of the EEPROM. This word contains bits
         * that determine the hardware's default PAUSE (flow control) mode,
         * a bit that determines whether the HW defaults to enabling or
         * disabling auto-negotiation, and the direction of the
@@ -778,8 +758,7 @@ s32 igb_force_mac_fc(struct e1000_hw *hw)
 
        ctrl = rd32(E1000_CTRL);
 
-       /*
-        * Because we didn't get link via the internal auto-negotiation
+       /* Because we didn't get link via the internal auto-negotiation
         * mechanism (we either forced link or we got link via PHY
         * auto-neg), we have to manually enable/disable transmit an
         * receive flow control.
@@ -843,8 +822,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
        u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
        u16 speed, duplex;
 
-       /*
-        * Check for the case where we have fiber media and auto-neg failed
+       /* Check for the case where we have fiber media and auto-neg failed
         * so we had to force link.  In this case, we need to force the
         * configuration of the MAC to match the "fc" parameter.
         */
@@ -861,15 +839,13 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                goto out;
        }
 
-       /*
-        * Check for the case where we have copper media and auto-neg is
+       /* Check for the case where we have copper media and auto-neg is
         * enabled.  In this case, we need to check and see if Auto-Neg
         * has completed, and if so, how the PHY and link partner has
         * flow control configured.
         */
        if ((hw->phy.media_type == e1000_media_type_copper) && mac->autoneg) {
-               /*
-                * Read the MII Status Register and check to see if AutoNeg
+               /* Read the MII Status Register and check to see if AutoNeg
                 * has completed.  We read this twice because this reg has
                 * some "sticky" (latched) bits.
                 */
@@ -888,8 +864,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                        goto out;
                }
 
-               /*
-                * The AutoNeg process has completed, so we now need to
+               /* The AutoNeg process has completed, so we now need to
                 * read both the Auto Negotiation Advertisement
                 * Register (Address 4) and the Auto_Negotiation Base
                 * Page Ability Register (Address 5) to determine how
@@ -904,8 +879,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                if (ret_val)
                        goto out;
 
-               /*
-                * Two bits in the Auto Negotiation Advertisement Register
+               /* Two bits in the Auto Negotiation Advertisement Register
                 * (Address 4) and two bits in the Auto Negotiation Base
                 * Page Ability Register (Address 5) determine flow control
                 * for both the PHY and the link partner.  The following
@@ -940,8 +914,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                 */
                if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
                    (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) {
-                       /*
-                        * Now we need to check if the user selected RX ONLY
+                       /* Now we need to check if the user selected RX ONLY
                         * of pause frames.  In this case, we had to advertise
                         * FULL flow control because we could not advertise RX
                         * ONLY. Hence, we must now check to see if we need to
@@ -956,8 +929,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                                       "RX PAUSE frames only.\r\n");
                        }
                }
-               /*
-                * For receiving PAUSE frames ONLY.
+               /* For receiving PAUSE frames ONLY.
                 *
                 *   LOCAL DEVICE  |   LINK PARTNER
                 * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
@@ -971,8 +943,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                        hw->fc.current_mode = e1000_fc_tx_pause;
                        hw_dbg("Flow Control = TX PAUSE frames only.\r\n");
                }
-               /*
-                * For transmitting PAUSE frames ONLY.
+               /* For transmitting PAUSE frames ONLY.
                 *
                 *   LOCAL DEVICE  |   LINK PARTNER
                 * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
@@ -986,8 +957,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                        hw->fc.current_mode = e1000_fc_rx_pause;
                        hw_dbg("Flow Control = RX PAUSE frames only.\r\n");
                }
-               /*
-                * Per the IEEE spec, at this point flow control should be
+               /* Per the IEEE spec, at this point flow control should be
                 * disabled.  However, we want to consider that we could
                 * be connected to a legacy switch that doesn't advertise
                 * desired flow control, but can be forced on the link
@@ -1007,9 +977,9 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                 * be asked to delay transmission of packets than asking
                 * our link partner to pause transmission of frames.
                 */
-               else if ((hw->fc.requested_mode == e1000_fc_none ||
-                         hw->fc.requested_mode == e1000_fc_tx_pause) ||
-                        hw->fc.strict_ieee) {
+               else if ((hw->fc.requested_mode == e1000_fc_none) ||
+                        (hw->fc.requested_mode == e1000_fc_tx_pause) ||
+                        (hw->fc.strict_ieee)) {
                        hw->fc.current_mode = e1000_fc_none;
                        hw_dbg("Flow Control = NONE.\r\n");
                } else {
@@ -1017,8 +987,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                        hw_dbg("Flow Control = RX PAUSE frames only.\r\n");
                }
 
-               /*
-                * Now we need to do one last check...  If we auto-
+               /* Now we need to do one last check...  If we auto-
                 * negotiated to HALF DUPLEX, flow control should not be
                 * enabled per IEEE 802.3 spec.
                 */
@@ -1031,8 +1000,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                if (duplex == HALF_DUPLEX)
                        hw->fc.current_mode = e1000_fc_none;
 
-               /*
-                * Now we call a subroutine to actually force the MAC
+               /* Now we call a subroutine to actually force the MAC
                 * controller to use the correct flow control settings.
                 */
                ret_val = igb_force_mac_fc(hw);
@@ -1203,6 +1171,17 @@ s32 igb_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
                hw_dbg("Half Duplex\n");
        }
 
+       /* Check if it is an I354 2.5Gb backplane connection. */
+       if (hw->mac.type == e1000_i354) {
+               if ((status & E1000_STATUS_2P5_SKU) &&
+                   !(status & E1000_STATUS_2P5_SKU_OVER)) {
+                       *speed = SPEED_2500;
+                       *duplex = FULL_DUPLEX;
+                       hw_dbg("2500 Mbs, ");
+                       hw_dbg("Full Duplex\n");
+               }
+       }
+
        return 0;
 }
 
@@ -1427,8 +1406,7 @@ s32 igb_blink_led(struct e1000_hw *hw)
        u32 ledctl_blink = 0;
        u32 i;
 
-       /*
-        * set the blink bit for each LED that's "on" (0x0E)
+       /* set the blink bit for each LED that's "on" (0x0E)
         * in ledctl_mode2
         */
        ledctl_blink = hw->mac.ledctl_mode2;
@@ -1467,7 +1445,7 @@ s32 igb_led_off(struct e1000_hw *hw)
  *  @hw: pointer to the HW structure
  *
  *  Returns 0 (0) if successful, else returns -10
- *  (-E1000_ERR_MASTER_REQUESTS_PENDING) if master disable bit has not casued
+ *  (-E1000_ERR_MASTER_REQUESTS_PENDING) if master disable bit has not caused
  *  the master requests to be disabled.
  *
  *  Disables PCI-Express master access and verifies there are no pending
index e6d6ce433261f8be8a125e00ee9fdf1bb114e606..5e13e83cc608358273d3e7dadb6bf488e426127b 100644 (file)
@@ -35,8 +35,7 @@
 #include "e1000_defines.h"
 #include "e1000_i210.h"
 
-/*
- * Functions that should not be called directly from drivers but can be used
+/* Functions that should not be called directly from drivers but can be used
  * by other files in this 'shared code'
  */
 s32  igb_blink_led(struct e1000_hw *hw);
@@ -49,15 +48,15 @@ s32  igb_get_auto_rd_done(struct e1000_hw *hw);
 s32  igb_get_bus_info_pcie(struct e1000_hw *hw);
 s32  igb_get_hw_semaphore(struct e1000_hw *hw);
 s32  igb_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
-                                      u16 *duplex);
+                                    u16 *duplex);
 s32  igb_id_led_init(struct e1000_hw *hw);
 s32  igb_led_off(struct e1000_hw *hw);
 void igb_update_mc_addr_list(struct e1000_hw *hw,
-                            u8 *mc_addr_list, u32 mc_addr_count);
+                            u8 *mc_addr_list, u32 mc_addr_count);
 s32  igb_setup_link(struct e1000_hw *hw);
 s32  igb_validate_mdi_setting(struct e1000_hw *hw);
 s32  igb_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg,
-                              u32 offset, u8 data);
+                            u32 offset, u8 data);
 
 void igb_clear_hw_cntrs_base(struct e1000_hw *hw);
 void igb_clear_vfta(struct e1000_hw *hw);
@@ -80,12 +79,12 @@ enum e1000_mng_mode {
        e1000_mng_mode_host_if_only
 };
 
-#define E1000_FACTPS_MNGCG    0x20000000
+#define E1000_FACTPS_MNGCG     0x20000000
 
-#define E1000_FWSM_MODE_MASK  0xE
-#define E1000_FWSM_MODE_SHIFT 1
+#define E1000_FWSM_MODE_MASK   0xE
+#define E1000_FWSM_MODE_SHIFT  1
 
-#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN    0x2
+#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN      0x2
 
 extern void e1000_init_function_pointers_82575(struct e1000_hw *hw);
 
index 38e0df35090422609b50d00b5834bb4b2a377916..dac1447fabf74620e562ebf38feb4f367db2ec4f 100644 (file)
@@ -196,7 +196,8 @@ out:
  *  returns SUCCESS if it successfully received a message notification and
  *  copied it into the receive buffer.
  **/
-static s32 igb_read_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id)
+static s32 igb_read_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size,
+                              u16 mbx_id)
 {
        struct e1000_mbx_info *mbx = &hw->mbx;
        s32 ret_val = -E1000_ERR_MBX;
@@ -222,7 +223,8 @@ out:
  *  returns SUCCESS if it successfully copied message into the buffer and
  *  received an ack to that message within delay * timeout period
  **/
-static s32 igb_write_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id)
+static s32 igb_write_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size,
+                               u16 mbx_id)
 {
        struct e1000_mbx_info *mbx = &hw->mbx;
        s32 ret_val = -E1000_ERR_MBX;
@@ -325,7 +327,6 @@ static s32 igb_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number)
        s32 ret_val = -E1000_ERR_MBX;
        u32 p2v_mailbox;
 
-
        /* Take ownership of the buffer */
        wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU);
 
@@ -347,7 +348,7 @@ static s32 igb_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number)
  *  returns SUCCESS if it successfully copied message into the buffer
  **/
 static s32 igb_write_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size,
-                              u16 vf_number)
+                           u16 vf_number)
 {
        s32 ret_val;
        u16 i;
@@ -388,7 +389,7 @@ out_no_write:
  *  a message due to a VF request so no polling for message is needed.
  **/
 static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size,
-                             u16 vf_number)
+                          u16 vf_number)
 {
        s32 ret_val;
        u16 i;
index c13b56d9edb25e8436e8ffbad9c74e8cdcd36e0b..de9bba41acf3c01b2f744e6cb30511ca97a514ac 100644 (file)
 
 #include "e1000_hw.h"
 
-#define E1000_P2VMAILBOX_STS   0x00000001 /* Initiate message send to VF */
-#define E1000_P2VMAILBOX_ACK   0x00000002 /* Ack message recv'd from VF */
-#define E1000_P2VMAILBOX_VFU   0x00000004 /* VF owns the mailbox buffer */
-#define E1000_P2VMAILBOX_PFU   0x00000008 /* PF owns the mailbox buffer */
-#define E1000_P2VMAILBOX_RVFU  0x00000010 /* Reset VFU - used when VF stuck */
+#define E1000_P2VMAILBOX_STS   0x00000001 /* Initiate message send to VF */
+#define E1000_P2VMAILBOX_ACK   0x00000002 /* Ack message recv'd from VF */
+#define E1000_P2VMAILBOX_VFU   0x00000004 /* VF owns the mailbox buffer */
+#define E1000_P2VMAILBOX_PFU   0x00000008 /* PF owns the mailbox buffer */
+#define E1000_P2VMAILBOX_RVFU  0x00000010 /* Reset VFU - used when VF stuck */
 
-#define E1000_MBVFICR_VFREQ_MASK 0x000000FF /* bits for VF messages */
-#define E1000_MBVFICR_VFREQ_VF1  0x00000001 /* bit for VF 1 message */
-#define E1000_MBVFICR_VFACK_MASK 0x00FF0000 /* bits for VF acks */
-#define E1000_MBVFICR_VFACK_VF1  0x00010000 /* bit for VF 1 ack */
+#define E1000_MBVFICR_VFREQ_MASK       0x000000FF /* bits for VF messages */
+#define E1000_MBVFICR_VFREQ_VF1                0x00000001 /* bit for VF 1 message */
+#define E1000_MBVFICR_VFACK_MASK       0x00FF0000 /* bits for VF acks */
+#define E1000_MBVFICR_VFACK_VF1                0x00010000 /* bit for VF 1 ack */
 
-#define E1000_VFMAILBOX_SIZE   16 /* 16 32 bit words - 64 bytes */
+#define E1000_VFMAILBOX_SIZE   16 /* 16 32 bit words - 64 bytes */
 
 /* If it's a E1000_VF_* msg then it originates in the VF and is sent to the
  * PF.  The reverse is true if it is E1000_PF_*.
  * Message ACK's are the value or'd with 0xF0000000
  */
-#define E1000_VT_MSGTYPE_ACK      0x80000000  /* Messages below or'd with
-                                               * this are the ACK */
-#define E1000_VT_MSGTYPE_NACK     0x40000000  /* Messages below or'd with
-                                               * this are the NACK */
-#define E1000_VT_MSGTYPE_CTS      0x20000000  /* Indicates that VF is still
-                                                 clear to send requests */
-#define E1000_VT_MSGINFO_SHIFT    16
+/* Messages below or'd with this are the ACK */
+#define E1000_VT_MSGTYPE_ACK   0x80000000
+/* Messages below or'd with this are the NACK */
+#define E1000_VT_MSGTYPE_NACK  0x40000000
+/* Indicates that VF is still clear to send requests */
+#define E1000_VT_MSGTYPE_CTS   0x20000000
+#define E1000_VT_MSGINFO_SHIFT 16
 /* bits 23:16 are used for exra info for certain messages */
-#define E1000_VT_MSGINFO_MASK     (0xFF << E1000_VT_MSGINFO_SHIFT)
+#define E1000_VT_MSGINFO_MASK  (0xFF << E1000_VT_MSGINFO_SHIFT)
 
-#define E1000_VF_RESET            0x01 /* VF requests reset */
-#define E1000_VF_SET_MAC_ADDR     0x02 /* VF requests to set MAC addr */
-#define E1000_VF_SET_MULTICAST    0x03 /* VF requests to set MC addr */
-#define E1000_VF_SET_VLAN         0x04 /* VF requests to set VLAN */
-#define E1000_VF_SET_LPE          0x05 /* VF requests to set VMOLR.LPE */
-#define E1000_VF_SET_PROMISC      0x06 /*VF requests to clear VMOLR.ROPE/MPME*/
-#define E1000_VF_SET_PROMISC_MULTICAST    (0x02 << E1000_VT_MSGINFO_SHIFT)
+#define E1000_VF_RESET         0x01 /* VF requests reset */
+#define E1000_VF_SET_MAC_ADDR  0x02 /* VF requests to set MAC addr */
+#define E1000_VF_SET_MULTICAST 0x03 /* VF requests to set MC addr */
+#define E1000_VF_SET_VLAN      0x04 /* VF requests to set VLAN */
+#define E1000_VF_SET_LPE       0x05 /* VF requests to set VMOLR.LPE */
+#define E1000_VF_SET_PROMISC   0x06 /*VF requests to clear VMOLR.ROPE/MPME*/
+#define E1000_VF_SET_PROMISC_MULTICAST (0x02 << E1000_VT_MSGINFO_SHIFT)
 
-#define E1000_PF_CONTROL_MSG      0x0100 /* PF control message */
+#define E1000_PF_CONTROL_MSG   0x0100 /* PF control message */
 
 s32 igb_read_mbx(struct e1000_hw *, u32 *, u16, u16);
 s32 igb_write_mbx(struct e1000_hw *, u32 *, u16, u16);
index 5b62adbe134de0f9bba71e23b25c01aa95e054b8..7f9cd7cbd353debcbb5eb20ba44ebd36ec61abcc 100644 (file)
@@ -289,15 +289,14 @@ static s32 igb_ready_nvm_eeprom(struct e1000_hw *hw)
                udelay(1);
                timeout = NVM_MAX_RETRY_SPI;
 
-               /*
-                * Read "Status Register" repeatedly until the LSB is cleared.
+               /* Read "Status Register" repeatedly until the LSB is cleared.
                 * The EEPROM will signal that the command has been completed
                 * by clearing bit 0 of the internal status register.  If it's
                 * not cleared within 'timeout', then error out.
                 */
                while (timeout) {
                        igb_shift_out_eec_bits(hw, NVM_RDSR_OPCODE_SPI,
-                                                hw->nvm.opcode_bits);
+                                              hw->nvm.opcode_bits);
                        spi_stat_reg = (u8)igb_shift_in_eec_bits(hw, 8);
                        if (!(spi_stat_reg & NVM_STATUS_RDY_SPI))
                                break;
@@ -335,8 +334,7 @@ s32 igb_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
        u16 word_in;
        u8 read_opcode = NVM_READ_OPCODE_SPI;
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* A check for invalid values:  offset too large, too many words,
         * and not enough words.
         */
        if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
@@ -363,8 +361,7 @@ s32 igb_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
        igb_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits);
        igb_shift_out_eec_bits(hw, (u16)(offset*2), nvm->address_bits);
 
-       /*
-        * Read the data.  SPI NVMs increment the address with each byte
+       /* Read the data.  SPI NVMs increment the address with each byte
         * read and will roll over if reading beyond the end.  This allows
         * us to read the whole NVM from any offset
         */
@@ -395,8 +392,7 @@ s32 igb_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
        u32 i, eerd = 0;
        s32 ret_val = 0;
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* A check for invalid values:  offset too large, too many words,
         * and not enough words.
         */
        if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
@@ -408,7 +404,7 @@ s32 igb_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
 
        for (i = 0; i < words; i++) {
                eerd = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) +
-                      E1000_NVM_RW_REG_START;
+                       E1000_NVM_RW_REG_START;
 
                wr32(E1000_EERD, eerd);
                ret_val = igb_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ);
@@ -441,8 +437,7 @@ s32 igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
        s32 ret_val = -E1000_ERR_NVM;
        u16 widx = 0;
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* A check for invalid values:  offset too large, too many words,
         * and not enough words.
         */
        if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
@@ -472,8 +467,7 @@ s32 igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
 
                igb_standby_nvm(hw);
 
-               /*
-                * Some SPI eeproms use the 8th address bit embedded in the
+               /* Some SPI eeproms use the 8th address bit embedded in the
                 * opcode
                 */
                if ((nvm->address_bits == 8) && (offset >= 128))
@@ -538,8 +532,7 @@ s32 igb_read_part_string(struct e1000_hw *hw, u8 *part_num, u32 part_num_size)
                goto out;
        }
 
-       /*
-        * if nvm_data is not ptr guard the PBA must be in legacy format which
+       /* if nvm_data is not ptr guard the PBA must be in legacy format which
         * means pointer is actually our second data word for the PBA number
         * and we can decode it into an ascii string
         */
@@ -728,6 +721,7 @@ void igb_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_vers)
        case e1000_82575:
        case e1000_82576:
        case e1000_82580:
+       case e1000_i354:
        case e1000_i350:
        case e1000_i210:
                break;
@@ -746,6 +740,7 @@ void igb_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_vers)
 
        switch (hw->mac.type) {
        case e1000_i210:
+       case e1000_i354:
        case e1000_i350:
                /* find combo image version */
                hw->nvm.ops.read(hw, NVM_COMB_VER_PTR, 1, &comb_offset);
index 2918c979b5bba54a74a5d3844ee265aaacf62680..fd46add6c4e43edced517b3bdcffbb77d1cad969 100644 (file)
 
 static s32  igb_phy_setup_autoneg(struct e1000_hw *hw);
 static void igb_phy_force_speed_duplex_setup(struct e1000_hw *hw,
-                                              u16 *phy_ctrl);
+                                            u16 *phy_ctrl);
 static s32  igb_wait_autoneg(struct e1000_hw *hw);
 static s32  igb_set_master_slave_mode(struct e1000_hw *hw);
 
 /* Cable length tables */
-static const u16 e1000_m88_cable_length_table[] =
-       0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED };
+static const u16 e1000_m88_cable_length_table[] = {
+       0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED };
 #define M88E1000_CABLE_LENGTH_TABLE_SIZE \
-                (sizeof(e1000_m88_cable_length_table) / \
-                 sizeof(e1000_m88_cable_length_table[0]))
-
-static const u16 e1000_igp_2_cable_length_table[] =
-    { 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21,
-      0, 0, 0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41,
-      6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61,
-      21, 26, 31, 35, 40, 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82,
-      40, 45, 51, 56, 61, 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104,
-      60, 66, 72, 77, 82, 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121,
-      83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124,
-      104, 109, 114, 118, 121, 124};
+       (sizeof(e1000_m88_cable_length_table) / \
+       sizeof(e1000_m88_cable_length_table[0]))
+
+static const u16 e1000_igp_2_cable_length_table[] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21,
+       0, 0, 0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41,
+       6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61,
+       21, 26, 31, 35, 40, 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82,
+       40, 45, 51, 56, 61, 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104,
+       60, 66, 72, 77, 82, 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121,
+       83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124,
+       104, 109, 114, 118, 121, 124};
 #define IGP02E1000_CABLE_LENGTH_TABLE_SIZE \
-               (sizeof(e1000_igp_2_cable_length_table) / \
-                sizeof(e1000_igp_2_cable_length_table[0]))
+       (sizeof(e1000_igp_2_cable_length_table) / \
+        sizeof(e1000_igp_2_cable_length_table[0]))
 
 /**
  *  igb_check_reset_block - Check if PHY reset is blocked
@@ -71,8 +71,7 @@ s32 igb_check_reset_block(struct e1000_hw *hw)
 
        manc = rd32(E1000_MANC);
 
-       return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ?
-              E1000_BLK_PHY_RESET : 0;
+       return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? E1000_BLK_PHY_RESET : 0;
 }
 
 /**
@@ -149,8 +148,7 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
                goto out;
        }
 
-       /*
-        * Set up Op-code, Phy Address, and register offset in the MDI
+       /* Set up Op-code, Phy Address, and register offset in the MDI
         * Control register.  The MAC will take care of interfacing with the
         * PHY to retrieve the desired data.
         */
@@ -160,8 +158,7 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
 
        wr32(E1000_MDIC, mdic);
 
-       /*
-        * Poll the ready bit to see if the MDI read completed
+       /* Poll the ready bit to see if the MDI read completed
         * Increasing the time out as testing showed failures with
         * the lower time out
         */
@@ -207,8 +204,7 @@ s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
                goto out;
        }
 
-       /*
-        * Set up Op-code, Phy Address, and register offset in the MDI
+       /* Set up Op-code, Phy Address, and register offset in the MDI
         * Control register.  The MAC will take care of interfacing with the
         * PHY to retrieve the desired data.
         */
@@ -219,8 +215,7 @@ s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
 
        wr32(E1000_MDIC, mdic);
 
-       /*
-        * Poll the ready bit to see if the MDI read completed
+       /* Poll the ready bit to see if the MDI read completed
         * Increasing the time out as testing showed failures with
         * the lower time out
         */
@@ -259,15 +254,13 @@ s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data)
        struct e1000_phy_info *phy = &hw->phy;
        u32 i, i2ccmd = 0;
 
-
-       /*
-        * Set up Op-code, Phy Address, and register address in the I2CCMD
+       /* Set up Op-code, Phy Address, and register address in the I2CCMD
         * register.  The MAC will take care of interfacing with the
         * PHY to retrieve the desired data.
         */
        i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
-                 (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
-                 (E1000_I2CCMD_OPCODE_READ));
+                 (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+                 (E1000_I2CCMD_OPCODE_READ));
 
        wr32(E1000_I2CCMD, i2ccmd);
 
@@ -317,15 +310,14 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data)
        /* Swap the data bytes for the I2C interface */
        phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00);
 
-       /*
-        * Set up Op-code, Phy Address, and register address in the I2CCMD
+       /* Set up Op-code, Phy Address, and register address in the I2CCMD
         * register.  The MAC will take care of interfacing with the
         * PHY to retrieve the desired data.
         */
        i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
-                 (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
-                 E1000_I2CCMD_OPCODE_WRITE |
-                 phy_data_swapped);
+                 (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+                 E1000_I2CCMD_OPCODE_WRITE |
+                 phy_data_swapped);
 
        wr32(E1000_I2CCMD, i2ccmd);
 
@@ -371,8 +363,8 @@ s32 igb_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data)
 
        if (offset > MAX_PHY_MULTI_PAGE_REG) {
                ret_val = igb_write_phy_reg_mdic(hw,
-                                                  IGP01E1000_PHY_PAGE_SELECT,
-                                                  (u16)offset);
+                                                IGP01E1000_PHY_PAGE_SELECT,
+                                                (u16)offset);
                if (ret_val) {
                        hw->phy.ops.release(hw);
                        goto out;
@@ -410,8 +402,8 @@ s32 igb_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
 
        if (offset > MAX_PHY_MULTI_PAGE_REG) {
                ret_val = igb_write_phy_reg_mdic(hw,
-                                                  IGP01E1000_PHY_PAGE_SELECT,
-                                                  (u16)offset);
+                                                IGP01E1000_PHY_PAGE_SELECT,
+                                                (u16)offset);
                if (ret_val) {
                        hw->phy.ops.release(hw);
                        goto out;
@@ -419,7 +411,7 @@ s32 igb_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
        }
 
        ret_val = igb_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
-                                          data);
+                                        data);
 
        hw->phy.ops.release(hw);
 
@@ -439,7 +431,6 @@ s32 igb_copper_link_setup_82580(struct e1000_hw *hw)
        s32 ret_val;
        u16 phy_data;
 
-
        if (phy->reset_disable) {
                ret_val = 0;
                goto out;
@@ -472,8 +463,7 @@ s32 igb_copper_link_setup_82580(struct e1000_hw *hw)
        if (ret_val)
                goto out;
        phy_data &= ~I82580_PHY_CTRL2_MDIX_CFG_MASK;
-       /*
-        * Options:
+       /* Options:
         *   0 - Auto (default)
         *   1 - MDI mode
         *   2 - MDI-X mode
@@ -520,8 +510,7 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw)
 
        phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
 
-       /*
-        * Options:
+       /* Options:
         *   MDI/MDI-X = 0 (default)
         *   0 - Auto for all speeds
         *   1 - MDI mode
@@ -546,8 +535,7 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw)
                break;
        }
 
-       /*
-        * Options:
+       /* Options:
         *   disable_polarity_correction = 0 (default)
         *       Automatic Correction for Reversed Cable Polarity
         *   0 - Disabled
@@ -562,12 +550,11 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw)
                goto out;
 
        if (phy->revision < E1000_REVISION_4) {
-               /*
-                * Force TX_CLK in the Extended PHY Specific Control Register
+               /* Force TX_CLK in the Extended PHY Specific Control Register
                 * to 25MHz clock.
                 */
                ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
-                                            &phy_data);
+                                           &phy_data);
                if (ret_val)
                        goto out;
 
@@ -630,8 +617,7 @@ s32 igb_copper_link_setup_m88_gen2(struct e1000_hw *hw)
        if (ret_val)
                goto out;
 
-       /*
-        * Options:
+       /* Options:
         *   MDI/MDI-X = 0 (default)
         *   0 - Auto for all speeds
         *   1 - MDI mode
@@ -659,8 +645,7 @@ s32 igb_copper_link_setup_m88_gen2(struct e1000_hw *hw)
                break;
        }
 
-       /*
-        * Options:
+       /* Options:
         *   disable_polarity_correction = 0 (default)
         *       Automatic Correction for Reversed Cable Polarity
         *   0 - Disabled
@@ -714,14 +699,12 @@ s32 igb_copper_link_setup_igp(struct e1000_hw *hw)
                goto out;
        }
 
-       /*
-        * Wait 100ms for MAC to configure PHY from NVM settings, to avoid
+       /* Wait 100ms for MAC to configure PHY from NVM settings, to avoid
         * timeout issues when LFS is enabled.
         */
        msleep(100);
 
-       /*
-        * The NVM settings will configure LPLU in D3 for
+       /* The NVM settings will configure LPLU in D3 for
         * non-IGP1 PHYs.
         */
        if (phy->type == e1000_phy_igp) {
@@ -765,8 +748,7 @@ s32 igb_copper_link_setup_igp(struct e1000_hw *hw)
 
        /* set auto-master slave resolution settings */
        if (hw->mac.autoneg) {
-               /*
-                * when autonegotiation advertisement is only 1000Mbps then we
+               /* when autonegotiation advertisement is only 1000Mbps then we
                 * should disable SmartSpeed and enable Auto MasterSlave
                 * resolution as hardware default.
                 */
@@ -844,14 +826,12 @@ static s32 igb_copper_link_autoneg(struct e1000_hw *hw)
        s32 ret_val;
        u16 phy_ctrl;
 
-       /*
-        * Perform some bounds checking on the autoneg advertisement
+       /* Perform some bounds checking on the autoneg advertisement
         * parameter.
         */
        phy->autoneg_advertised &= phy->autoneg_mask;
 
-       /*
-        * If autoneg_advertised is zero, we assume it was not defaulted
+       /* If autoneg_advertised is zero, we assume it was not defaulted
         * by the calling code so we set to advertise full capability.
         */
        if (phy->autoneg_advertised == 0)
@@ -865,8 +845,7 @@ static s32 igb_copper_link_autoneg(struct e1000_hw *hw)
        }
        hw_dbg("Restarting Auto-Neg\n");
 
-       /*
-        * Restart auto-negotiation by setting the Auto Neg Enable bit and
+       /* Restart auto-negotiation by setting the Auto Neg Enable bit and
         * the Auto Neg Restart bit in the PHY control register.
         */
        ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
@@ -878,8 +857,7 @@ static s32 igb_copper_link_autoneg(struct e1000_hw *hw)
        if (ret_val)
                goto out;
 
-       /*
-        * Does the user want to wait for Auto-Neg to complete here, or
+       /* Does the user want to wait for Auto-Neg to complete here, or
         * check at a later time (for example, callback routine).
         */
        if (phy->autoneg_wait_to_complete) {
@@ -928,16 +906,14 @@ static s32 igb_phy_setup_autoneg(struct e1000_hw *hw)
                        goto out;
        }
 
-       /*
-        * Need to parse both autoneg_advertised and fc and set up
+       /* Need to parse both autoneg_advertised and fc and set up
         * the appropriate PHY registers.  First we will parse for
         * autoneg_advertised software override.  Since we can advertise
         * a plethora of combinations, we need to check each bit
         * individually.
         */
 
-       /*
-        * First we clear all the 10/100 mb speed bits in the Auto-Neg
+       /* First we clear all the 10/100 mb speed bits in the Auto-Neg
         * Advertisement Register (Address 4) and the 1000 mb speed bits in
         * the  1000Base-T Control Register (Address 9).
         */
@@ -983,8 +959,7 @@ static s32 igb_phy_setup_autoneg(struct e1000_hw *hw)
                mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
        }
 
-       /*
-        * Check for a software override of the flow control settings, and
+       /* Check for a software override of the flow control settings, and
         * setup the PHY advertisement registers accordingly.  If
         * auto-negotiation is enabled, then software will have to set the
         * "PAUSE" bits to the correct value in the Auto-Negotiation
@@ -1003,15 +978,13 @@ static s32 igb_phy_setup_autoneg(struct e1000_hw *hw)
         */
        switch (hw->fc.current_mode) {
        case e1000_fc_none:
-               /*
-                * Flow control (RX & TX) is completely disabled by a
+               /* Flow control (RX & TX) is completely disabled by a
                 * software over-ride.
                 */
                mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
                break;
        case e1000_fc_rx_pause:
-               /*
-                * RX Flow control is enabled, and TX Flow control is
+               /* RX Flow control is enabled, and TX Flow control is
                 * disabled, by a software over-ride.
                 *
                 * Since there really isn't a way to advertise that we are
@@ -1023,16 +996,14 @@ static s32 igb_phy_setup_autoneg(struct e1000_hw *hw)
                mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
                break;
        case e1000_fc_tx_pause:
-               /*
-                * TX Flow control is enabled, and RX Flow control is
+               /* TX Flow control is enabled, and RX Flow control is
                 * disabled, by a software over-ride.
                 */
                mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
                mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
                break;
        case e1000_fc_full:
-               /*
-                * Flow control (both RX and TX) is enabled by a software
+               /* Flow control (both RX and TX) is enabled by a software
                 * over-ride.
                 */
                mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
@@ -1075,18 +1046,15 @@ s32 igb_setup_copper_link(struct e1000_hw *hw)
        s32 ret_val;
        bool link;
 
-
        if (hw->mac.autoneg) {
-               /*
-                * Setup autoneg and flow control advertisement and perform
+               /* Setup autoneg and flow control advertisement and perform
                 * autonegotiation.
                 */
                ret_val = igb_copper_link_autoneg(hw);
                if (ret_val)
                        goto out;
        } else {
-               /*
-                * PHY will be set to 10H, 10F, 100H or 100F
+               /* PHY will be set to 10H, 10F, 100H or 100F
                 * depending on user settings.
                 */
                hw_dbg("Forcing Speed and Duplex\n");
@@ -1097,14 +1065,10 @@ s32 igb_setup_copper_link(struct e1000_hw *hw)
                }
        }
 
-       /*
-        * Check link status. Wait up to 100 microseconds for link to become
+       /* Check link status. Wait up to 100 microseconds for link to become
         * valid.
         */
-       ret_val = igb_phy_has_link(hw,
-                                  COPPER_LINK_UP_LIMIT,
-                                  10,
-                                  &link);
+       ret_val = igb_phy_has_link(hw, COPPER_LINK_UP_LIMIT, 10, &link);
        if (ret_val)
                goto out;
 
@@ -1145,8 +1109,7 @@ s32 igb_phy_force_speed_duplex_igp(struct e1000_hw *hw)
        if (ret_val)
                goto out;
 
-       /*
-        * Clear Auto-Crossover to force MDI manually.  IGP requires MDI
+       /* Clear Auto-Crossover to force MDI manually.  IGP requires MDI
         * forced whenever speed and duplex are forced.
         */
        ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data);
@@ -1167,10 +1130,7 @@ s32 igb_phy_force_speed_duplex_igp(struct e1000_hw *hw)
        if (phy->autoneg_wait_to_complete) {
                hw_dbg("Waiting for forced speed/duplex link on IGP phy.\n");
 
-               ret_val = igb_phy_has_link(hw,
-                                                    PHY_FORCE_LIMIT,
-                                                    100000,
-                                                    &link);
+               ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, 100000, &link);
                if (ret_val)
                        goto out;
 
@@ -1178,10 +1138,7 @@ s32 igb_phy_force_speed_duplex_igp(struct e1000_hw *hw)
                        hw_dbg("Link taking longer than expected.\n");
 
                /* Try once more */
-               ret_val = igb_phy_has_link(hw,
-                                                    PHY_FORCE_LIMIT,
-                                                    100000,
-                                                    &link);
+               ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, 100000, &link);
                if (ret_val)
                        goto out;
        }
@@ -1209,8 +1166,7 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
 
        /* I210 and I211 devices support Auto-Crossover in forced operation. */
        if (phy->type != e1000_phy_i210) {
-               /*
-                * Clear Auto-Crossover to force MDI manually.  M88E1000
+               /* Clear Auto-Crossover to force MDI manually.  M88E1000
                 * requires MDI forced whenever speed and duplex are forced.
                 */
                ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL,
@@ -1266,13 +1222,12 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
                        if (!reset_dsp)
                                hw_dbg("Link taking longer than expected.\n");
                        else {
-                               /*
-                                * We didn't get link.
+                               /* We didn't get link.
                                 * Reset the DSP and cross our fingers.
                                 */
                                ret_val = phy->ops.write_reg(hw,
-                                                            M88E1000_PHY_PAGE_SELECT,
-                                                            0x001d);
+                                               M88E1000_PHY_PAGE_SELECT,
+                                               0x001d);
                                if (ret_val)
                                        goto out;
                                ret_val = igb_phy_reset_dsp(hw);
@@ -1298,8 +1253,7 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
        if (ret_val)
                goto out;
 
-       /*
-        * Resetting the phy means we need to re-force TX_CLK in the
+       /* Resetting the phy means we need to re-force TX_CLK in the
         * Extended PHY Specific Control Register to 25MHz clock from
         * the reset value of 2.5MHz.
         */
@@ -1308,8 +1262,7 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
        if (ret_val)
                goto out;
 
-       /*
-        * In addition, we must re-enable CRS on Tx for both half and full
+       /* In addition, we must re-enable CRS on Tx for both half and full
         * duplex.
         */
        ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
@@ -1336,7 +1289,7 @@ out:
  *  take affect.
  **/
 static void igb_phy_force_speed_duplex_setup(struct e1000_hw *hw,
-                                              u16 *phy_ctrl)
+                                            u16 *phy_ctrl)
 {
        struct e1000_mac_info *mac = &hw->mac;
        u32 ctrl;
@@ -1417,8 +1370,7 @@ s32 igb_set_d3_lplu_state(struct e1000_hw *hw, bool active)
                                             data);
                if (ret_val)
                        goto out;
-               /*
-                * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
+               /* LPLU and SmartSpeed are mutually exclusive.  LPLU is used
                 * during Dx states where the power conservation is most
                 * important.  During driver activity we should enable
                 * SmartSpeed, so performance is maintained.
@@ -1461,13 +1413,13 @@ s32 igb_set_d3_lplu_state(struct e1000_hw *hw, bool active)
 
                /* When LPLU is enabled, we should disable SmartSpeed */
                ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
-                                            &data);
+                                           &data);
                if (ret_val)
                        goto out;
 
                data &= ~IGP01E1000_PSCFR_SMART_SPEED;
                ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
-                                             data);
+                                            data);
        }
 
 out:
@@ -1556,8 +1508,7 @@ static s32 igb_check_polarity_igp(struct e1000_hw *hw)
        s32 ret_val;
        u16 data, offset, mask;
 
-       /*
-        * Polarity is determined based on the speed of
+       /* Polarity is determined based on the speed of
         * our connection.
         */
        ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data);
@@ -1569,8 +1520,7 @@ static s32 igb_check_polarity_igp(struct e1000_hw *hw)
                offset  = IGP01E1000_PHY_PCS_INIT_REG;
                mask    = IGP01E1000_PHY_POLARITY_MASK;
        } else {
-               /*
-                * This really only applies to 10Mbps since
+               /* This really only applies to 10Mbps since
                 * there is no polarity for 100Mbps (always 0).
                 */
                offset  = IGP01E1000_PHY_PORT_STATUS;
@@ -1589,7 +1539,7 @@ out:
 }
 
 /**
- *  igb_wait_autoneg - Wait for auto-neg compeletion
+ *  igb_wait_autoneg - Wait for auto-neg completion
  *  @hw: pointer to the HW structure
  *
  *  Waits for auto-negotiation to complete or for the auto-negotiation time
@@ -1613,8 +1563,7 @@ static s32 igb_wait_autoneg(struct e1000_hw *hw)
                msleep(100);
        }
 
-       /*
-        * PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation
+       /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation
         * has completed.
         */
        return ret_val;
@@ -1630,21 +1579,19 @@ static s32 igb_wait_autoneg(struct e1000_hw *hw)
  *  Polls the PHY status register for link, 'iterations' number of times.
  **/
 s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
-                              u32 usec_interval, bool *success)
+                    u32 usec_interval, bool *success)
 {
        s32 ret_val = 0;
        u16 i, phy_status;
 
        for (i = 0; i < iterations; i++) {
-               /*
-                * Some PHYs require the PHY_STATUS register to be read
+               /* Some PHYs require the PHY_STATUS register to be read
                 * twice due to the link bit being sticky.  No harm doing
                 * it across the board.
                 */
                ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
                if (ret_val) {
-                       /*
-                        * If the first read fails, another entity may have
+                       /* If the first read fails, another entity may have
                         * ownership of the resources, wait and try again to
                         * see if they have relinquished the resources yet.
                         */
@@ -1735,6 +1682,7 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw)
                phy->max_cable_length = phy_data / (is_cm ? 100 : 1);
                phy->cable_length = phy_data / (is_cm ? 100 : 1);
                break;
+       case M88E1545_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,
@@ -1834,10 +1782,10 @@ s32 igb_get_cable_length_igp_2(struct e1000_hw *hw)
        u16 cur_agc_index, max_agc_index = 0;
        u16 min_agc_index = IGP02E1000_CABLE_LENGTH_TABLE_SIZE - 1;
        static const u16 agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] = {
-              IGP02E1000_PHY_AGC_A,
-              IGP02E1000_PHY_AGC_B,
-              IGP02E1000_PHY_AGC_C,
-              IGP02E1000_PHY_AGC_D
+               IGP02E1000_PHY_AGC_A,
+               IGP02E1000_PHY_AGC_B,
+               IGP02E1000_PHY_AGC_C,
+               IGP02E1000_PHY_AGC_D
        };
 
        /* Read the AGC registers for all channels */
@@ -1846,8 +1794,7 @@ s32 igb_get_cable_length_igp_2(struct e1000_hw *hw)
                if (ret_val)
                        goto out;
 
-               /*
-                * Getting bits 15:9, which represent the combination of
+               /* Getting bits 15:9, which represent the combination of
                 * coarse and fine gain values.  The result is a number
                 * that can be put into the lookup table to obtain the
                 * approximate cable length.
@@ -2167,15 +2114,13 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
        hw->phy.ops.write_reg(hw, 0x1796, 0x0008);
        /* Change cg_icount + enable integbp for channels BCD */
        hw->phy.ops.write_reg(hw, 0x1798, 0xD008);
-       /*
-        * Change cg_icount + enable integbp + change prop_factor_master
+       /* Change cg_icount + enable integbp + change prop_factor_master
         * to 8 for channel A
         */
        hw->phy.ops.write_reg(hw, 0x1898, 0xD918);
        /* Disable AHT in Slave mode on channel A */
        hw->phy.ops.write_reg(hw, 0x187A, 0x0800);
-       /*
-        * Enable LPLU and disable AN to 1000 in non-D0a states,
+       /* Enable LPLU and disable AN to 1000 in non-D0a states,
         * Enable SPD+B2B
         */
        hw->phy.ops.write_reg(hw, 0x0019, 0x008D);
@@ -2257,8 +2202,8 @@ static s32 igb_check_polarity_82580(struct e1000_hw *hw)
 
        if (!ret_val)
                phy->cable_polarity = (data & I82580_PHY_STATUS2_REV_POLARITY)
-                                     ? e1000_rev_polarity_reversed
-                                     : e1000_rev_polarity_normal;
+                                     ? e1000_rev_polarity_reversed
+                                     : e1000_rev_polarity_normal;
 
        return ret_val;
 }
@@ -2278,7 +2223,6 @@ s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw)
        u16 phy_data;
        bool link;
 
-
        ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
        if (ret_val)
                goto out;
@@ -2289,8 +2233,7 @@ s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw)
        if (ret_val)
                goto out;
 
-       /*
-        * Clear Auto-Crossover to force MDI manually.  82580 requires MDI
+       /* Clear Auto-Crossover to force MDI manually.  82580 requires MDI
         * forced whenever speed and duplex are forced.
         */
        ret_val = phy->ops.read_reg(hw, I82580_PHY_CTRL_2, &phy_data);
@@ -2310,10 +2253,7 @@ s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw)
        if (phy->autoneg_wait_to_complete) {
                hw_dbg("Waiting for forced speed/duplex link on 82580 phy\n");
 
-               ret_val = igb_phy_has_link(hw,
-                                          PHY_FORCE_LIMIT,
-                                          100000,
-                                          &link);
+               ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, 100000, &link);
                if (ret_val)
                        goto out;
 
@@ -2321,10 +2261,7 @@ s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw)
                        hw_dbg("Link taking longer than expected.\n");
 
                /* Try once more */
-               ret_val = igb_phy_has_link(hw,
-                                          PHY_FORCE_LIMIT,
-                                          100000,
-                                          &link);
+               ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, 100000, &link);
                if (ret_val)
                        goto out;
        }
@@ -2349,7 +2286,6 @@ s32 igb_get_phy_info_82580(struct e1000_hw *hw)
        u16 data;
        bool link;
 
-
        ret_val = igb_phy_has_link(hw, 1, 0, &link);
        if (ret_val)
                goto out;
@@ -2383,12 +2319,12 @@ s32 igb_get_phy_info_82580(struct e1000_hw *hw)
                        goto out;
 
                phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS)
-                               ? e1000_1000t_rx_status_ok
-                               : e1000_1000t_rx_status_not_ok;
+                               ? e1000_1000t_rx_status_ok
+                               : e1000_1000t_rx_status_not_ok;
 
                phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS)
-                                ? e1000_1000t_rx_status_ok
-                                : e1000_1000t_rx_status_not_ok;
+                                ? e1000_1000t_rx_status_ok
+                                : e1000_1000t_rx_status_not_ok;
        } else {
                phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
                phy->local_rx = e1000_1000t_rx_status_undefined;
@@ -2412,13 +2348,12 @@ s32 igb_get_cable_length_82580(struct e1000_hw *hw)
        s32 ret_val;
        u16 phy_data, length;
 
-
        ret_val = phy->ops.read_reg(hw, I82580_PHY_DIAG_STATUS, &phy_data);
        if (ret_val)
                goto out;
 
        length = (phy_data & I82580_DSTATUS_CABLE_LENGTH) >>
-                I82580_DSTATUS_CABLE_LENGTH_SHIFT;
+                I82580_DSTATUS_CABLE_LENGTH_SHIFT;
 
        if (length == E1000_CABLE_LENGTH_UNDEFINED)
                ret_val = -E1000_ERR_PHY;
index 15343286082e009acc711eecc4d2555875dd807d..82632c6c53afcb37239f2898eea9a01f5f95edfd 100644 (file)
@@ -65,6 +65,7 @@
 #define E1000_TIPG     0x00410  /* TX Inter-packet gap -RW */
 #define E1000_AIT      0x00458  /* Adaptive Interframe Spacing Throttle - RW */
 #define E1000_LEDCTL   0x00E00  /* LED Control - RW */
+#define E1000_LEDMUX   0x08130  /* LED MUX Control */
 #define E1000_PBA      0x01000  /* Packet Buffer Allocation - RW */
 #define E1000_PBS      0x01008  /* Packet Buffer Size */
 #define E1000_EEMNGCTL 0x01010  /* MNG EEprom Control */
@@ -83,6 +84,9 @@
 #define E1000_I2C_DATA_IN   0x00001000  /* I2C- Data In */
 #define E1000_I2C_CLK_OE_N  0x00002000  /* I2C- Clock Output Enable */
 #define E1000_I2C_CLK_IN    0x00004000  /* I2C- Clock In */
+#define E1000_MPHY_ADDR_CTRL   0x0024 /* GbE MPHY Address Control */
+#define E1000_MPHY_DATA                0x0E10 /* GBE MPHY Data */
+#define E1000_MPHY_STAT                0x0E0C /* GBE MPHY Statistics */
 
 /* IEEE 1588 TIMESYNCH */
 #define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */
 #define E1000_RQDPC(_n) (0x0C030 + ((_n) * 0x40))
 
 /* DMA Coalescing registers */
-#define E1000_DMACR             0x02508 /* Control Register */
-#define E1000_DMCTXTH           0x03550 /* Transmit Threshold */
-#define E1000_DMCTLX            0x02514 /* Time to Lx Request */
-#define E1000_DMCRTRH           0x05DD0 /* Receive Packet Rate Threshold */
-#define E1000_DMCCNT            0x05DD4 /* Current Rx Count */
-#define E1000_FCRTC             0x02170 /* Flow Control Rx high watermark */
-#define E1000_PCIEMISC          0x05BB8 /* PCIE misc config register */
+#define E1000_DMACR    0x02508 /* Control Register */
+#define E1000_DMCTXTH  0x03550 /* Transmit Threshold */
+#define E1000_DMCTLX   0x02514 /* Time to Lx Request */
+#define E1000_DMCRTRH  0x05DD0 /* Receive Packet Rate Threshold */
+#define E1000_DMCCNT   0x05DD4 /* Current Rx Count */
+#define E1000_FCRTC    0x02170 /* Flow Control Rx high watermark */
+#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */
 
 /* TX Rate Limit Registers */
-#define E1000_RTTDQSEL 0x3604  /* Tx Desc Plane Queue Select - WO */
-#define E1000_RTTBCNRM 0x3690  /* Tx BCN Rate-scheduler MMW */
-#define E1000_RTTBCNRC 0x36B0  /* Tx BCN Rate-Scheduler Config - WO */
+#define E1000_RTTDQSEL 0x3604 /* Tx Desc Plane Queue Select - WO */
+#define E1000_RTTBCNRM 0x3690 /* Tx BCN Rate-scheduler MMW */
+#define E1000_RTTBCNRC 0x36B0 /* Tx BCN Rate-Scheduler Config - WO */
 
 /* Split and Replication RX Control - RW */
-#define E1000_RXPBS    0x02404  /* Rx Packet Buffer Size - RW */
+#define E1000_RXPBS    0x02404 /* Rx Packet Buffer Size - RW */
 
 /* Thermal sensor configuration and status registers */
 #define E1000_THMJT    0x08100 /* Junction Temperature */
 #define E1000_THHIGHTC 0x0810C /* High Threshold Control */
 #define E1000_THSTAT   0x08110 /* Thermal Sensor Status */
 
-/*
- * Convenience macros
+/* Convenience macros
  *
  * Note: "_n" is the queue number of the register to be written to.
  *
 #define E1000_RFCTL    0x05008  /* Receive Filter Control*/
 #define E1000_MTA      0x05200  /* Multicast Table Array - RW Array */
 #define E1000_RA       0x05400  /* Receive Address - RW Array */
-#define E1000_RA2      0x054E0  /* 2nd half of receive address array - RW Array */
+#define E1000_RA2      0x054E0  /* 2nd half of Rx address array - RW Array */
 #define E1000_PSRTYPE(_i)       (0x05480 + ((_i) * 4))
 #define E1000_RAL(_i)  (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
                                        (0x054E0 + ((_i - 16) * 8)))
        (readl(hw->hw_addr + reg + ((offset) << 2)))
 
 /* DMA Coalescing registers */
-#define E1000_PCIEMISC          0x05BB8 /* PCIE misc config register */
+#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */
 
 /* Energy Efficient Ethernet "EEE" register */
-#define E1000_IPCNFG  0x0E38  /* Internal PHY Configuration */
-#define E1000_EEER    0x0E30  /* Energy Efficient Ethernet */
-#define E1000_EEE_SU  0X0E34  /* EEE Setup */
+#define E1000_IPCNFG   0x0E38 /* Internal PHY Configuration */
+#define E1000_EEER     0x0E30 /* Energy Efficient Ethernet */
+#define E1000_EEE_SU   0X0E34 /* EEE Setup */
+#define E1000_EMIADD   0x10   /* Extended Memory Indirect Address */
+#define E1000_EMIDATA  0x11   /* Extended Memory Indirect Data */
+#define E1000_MMDAC    13     /* MMD Access Control */
+#define E1000_MMDAAD   14     /* MMD Access Address/Data */
 
 /* Thermal Sensor Register */
-#define E1000_THSTAT    0x08110 /* Thermal Sensor Status */
+#define E1000_THSTAT   0x08110 /* Thermal Sensor Status */
 
 /* OS2BMC Registers */
-#define E1000_B2OSPC    0x08FE0 /* BMC2OS packets sent by BMC */
-#define E1000_B2OGPRC   0x04158 /* BMC2OS packets received by host */
-#define E1000_O2BGPTC   0x08FE4 /* OS2BMC packets received by BMC */
-#define E1000_O2BSPC    0x0415C /* OS2BMC packets transmitted by host */
+#define E1000_B2OSPC   0x08FE0 /* BMC2OS packets sent by BMC */
+#define E1000_B2OGPRC  0x04158 /* BMC2OS packets received by host */
+#define E1000_O2BGPTC  0x08FE4 /* OS2BMC packets received by BMC */
+#define E1000_O2BSPC   0x0415C /* OS2BMC packets transmitted by host */
 
 #define E1000_SRWR             0x12018  /* Shadow Ram Write Register - RW */
 #define E1000_I210_FLMNGCTL    0x12038
index ab577a763a20d96cb68c151b8f3ceb483af6f8bf..9d6c075e232d9c5c2885cebcfa58c297e03b049e 100644 (file)
 
 struct igb_adapter;
 
-#define E1000_PCS_CFG_IGN_SD               1
+#define E1000_PCS_CFG_IGN_SD   1
 
 /* Interrupt defines */
-#define IGB_START_ITR                    648 /* ~6000 ints/sec */
-#define IGB_4K_ITR                       980
-#define IGB_20K_ITR                      196
-#define IGB_70K_ITR                       56
+#define IGB_START_ITR          648 /* ~6000 ints/sec */
+#define IGB_4K_ITR             980
+#define IGB_20K_ITR            196
+#define IGB_70K_ITR            56
 
 /* TX/RX descriptor defines */
-#define IGB_DEFAULT_TXD                  256
-#define IGB_DEFAULT_TX_WORK             128
-#define IGB_MIN_TXD                       80
-#define IGB_MAX_TXD                     4096
+#define IGB_DEFAULT_TXD                256
+#define IGB_DEFAULT_TX_WORK    128
+#define IGB_MIN_TXD            80
+#define IGB_MAX_TXD            4096
 
-#define IGB_DEFAULT_RXD                  256
-#define IGB_MIN_RXD                       80
-#define IGB_MAX_RXD                     4096
+#define IGB_DEFAULT_RXD                256
+#define IGB_MIN_RXD            80
+#define IGB_MAX_RXD            4096
 
-#define IGB_DEFAULT_ITR                    3 /* dynamic */
-#define IGB_MAX_ITR_USECS              10000
-#define IGB_MIN_ITR_USECS                 10
-#define NON_Q_VECTORS                      1
-#define MAX_Q_VECTORS                      8
+#define IGB_DEFAULT_ITR                3 /* dynamic */
+#define IGB_MAX_ITR_USECS      10000
+#define IGB_MIN_ITR_USECS      10
+#define NON_Q_VECTORS          1
+#define MAX_Q_VECTORS          8
 
 /* Transmit and receive queues */
-#define IGB_MAX_RX_QUEUES                  8
-#define IGB_MAX_RX_QUEUES_82575            4
-#define IGB_MAX_RX_QUEUES_I211             2
-#define IGB_MAX_TX_QUEUES                  8
-#define IGB_MAX_VF_MC_ENTRIES              30
-#define IGB_MAX_VF_FUNCTIONS               8
-#define IGB_MAX_VFTA_ENTRIES               128
-#define IGB_82576_VF_DEV_ID                0x10CA
-#define IGB_I350_VF_DEV_ID                 0x1520
+#define IGB_MAX_RX_QUEUES      8
+#define IGB_MAX_RX_QUEUES_82575        4
+#define IGB_MAX_RX_QUEUES_I211 2
+#define IGB_MAX_TX_QUEUES      8
+#define IGB_MAX_VF_MC_ENTRIES  30
+#define IGB_MAX_VF_FUNCTIONS   8
+#define IGB_MAX_VFTA_ENTRIES   128
+#define IGB_82576_VF_DEV_ID    0x10CA
+#define IGB_I350_VF_DEV_ID     0x1520
 
 /* NVM version defines */
-#define IGB_MAJOR_MASK                 0xF000
-#define IGB_MINOR_MASK                 0x0FF0
-#define IGB_BUILD_MASK                 0x000F
-#define IGB_COMB_VER_MASK              0x00FF
-#define IGB_MAJOR_SHIFT                        12
-#define IGB_MINOR_SHIFT                        4
-#define IGB_COMB_VER_SHFT              8
-#define IGB_NVM_VER_INVALID            0xFFFF
-#define IGB_ETRACK_SHIFT               16
-#define NVM_ETRACK_WORD                        0x0042
-#define NVM_COMB_VER_OFF               0x0083
-#define NVM_COMB_VER_PTR               0x003d
+#define IGB_MAJOR_MASK         0xF000
+#define IGB_MINOR_MASK         0x0FF0
+#define IGB_BUILD_MASK         0x000F
+#define IGB_COMB_VER_MASK      0x00FF
+#define IGB_MAJOR_SHIFT                12
+#define IGB_MINOR_SHIFT                4
+#define IGB_COMB_VER_SHFT      8
+#define IGB_NVM_VER_INVALID    0xFFFF
+#define IGB_ETRACK_SHIFT       16
+#define NVM_ETRACK_WORD                0x0042
+#define NVM_COMB_VER_OFF       0x0083
+#define NVM_COMB_VER_PTR       0x003d
 
 struct vf_data_storage {
        unsigned char vf_mac_addresses[ETH_ALEN];
@@ -103,6 +103,7 @@ struct vf_data_storage {
        u16 pf_vlan; /* When set, guest VLAN config not allowed. */
        u16 pf_qos;
        u16 tx_rate;
+       bool spoofchk_enabled;
 };
 
 #define IGB_VF_FLAG_CTS            0x00000001 /* VF is clear to send data */
@@ -121,14 +122,14 @@ struct vf_data_storage {
  *           descriptors until either it has this many to write back, or the
  *           ITR timer expires.
  */
-#define IGB_RX_PTHRESH                     8
-#define IGB_RX_HTHRESH                     8
-#define IGB_TX_PTHRESH                     8
-#define IGB_TX_HTHRESH                     1
-#define IGB_RX_WTHRESH                     ((hw->mac.type == e1000_82576 && \
-                                            adapter->msix_entries) ? 1 : 4)
-#define IGB_TX_WTHRESH                     ((hw->mac.type == e1000_82576 && \
-                                            adapter->msix_entries) ? 1 : 16)
+#define IGB_RX_PTHRESH ((hw->mac.type == e1000_i354) ? 12 : 8)
+#define IGB_RX_HTHRESH 8
+#define IGB_TX_PTHRESH ((hw->mac.type == e1000_i354) ? 20 : 8)
+#define IGB_TX_HTHRESH 1
+#define IGB_RX_WTHRESH ((hw->mac.type == e1000_82576 && \
+                         adapter->msix_entries) ? 1 : 4)
+#define IGB_TX_WTHRESH ((hw->mac.type == e1000_82576 && \
+                         adapter->msix_entries) ? 1 : 16)
 
 /* this is the size past which hardware will drop packets when setting LPE=0 */
 #define MAXIMUM_ETHERNET_VLAN_SIZE 1522
@@ -140,17 +141,17 @@ struct vf_data_storage {
 #define IGB_RX_BUFSZ           IGB_RXBUFFER_2048
 
 /* How many Rx Buffers do we bundle into one write to the hardware ? */
-#define IGB_RX_BUFFER_WRITE    16      /* Must be power of 2 */
+#define IGB_RX_BUFFER_WRITE    16 /* Must be power of 2 */
 
-#define AUTO_ALL_MODES            0
-#define IGB_EEPROM_APME         0x0400
+#define AUTO_ALL_MODES         0
+#define IGB_EEPROM_APME                0x0400
 
 #ifndef IGB_MASTER_SLAVE
 /* Switch to override PHY master/slave setting */
 #define IGB_MASTER_SLAVE       e1000_ms_hw_default
 #endif
 
-#define IGB_MNG_VLAN_NONE -1
+#define IGB_MNG_VLAN_NONE      -1
 
 enum igb_tx_flags {
        /* cmd_type flags */
@@ -164,11 +165,10 @@ enum igb_tx_flags {
 };
 
 /* VLAN info */
-#define IGB_TX_FLAGS_VLAN_MASK         0xffff0000
+#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000
 #define IGB_TX_FLAGS_VLAN_SHIFT        16
 
-/*
- * The largest size we can write to the descriptor is 65535.  In order to
+/* The largest size we can write to the descriptor is 65535.  In order to
  * maintain a power of two alignment we have to limit ourselves to 32K.
  */
 #define IGB_MAX_TXD_PWR        15
@@ -178,8 +178,17 @@ enum igb_tx_flags {
 #define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IGB_MAX_DATA_PER_TXD)
 #define DESC_NEEDED (MAX_SKB_FRAGS + 4)
 
+/* EEPROM byte offsets */
+#define IGB_SFF_8472_SWAP              0x5C
+#define IGB_SFF_8472_COMP              0x5E
+
+/* Bitmasks */
+#define IGB_SFF_ADDRESSING_MODE                0x4
+#define IGB_SFF_8472_UNSUP             0x00
+
 /* wrapper around a pointer to a socket buffer,
- * so a DMA handle can be stored along with the buffer */
+ * so a DMA handle can be stored along with the buffer
+ */
 struct igb_tx_buffer {
        union e1000_adv_tx_desc *next_to_watch;
        unsigned long time_stamp;
@@ -290,11 +299,11 @@ enum e1000_ring_flags_t {
 
 #define IGB_TXD_DCMD (E1000_ADVTXD_DCMD_EOP | E1000_ADVTXD_DCMD_RS)
 
-#define IGB_RX_DESC(R, i)          \
+#define IGB_RX_DESC(R, i)      \
        (&(((union e1000_adv_rx_desc *)((R)->desc))[i]))
-#define IGB_TX_DESC(R, i)          \
+#define IGB_TX_DESC(R, i)      \
        (&(((union e1000_adv_tx_desc *)((R)->desc))[i]))
-#define IGB_TX_CTXTDESC(R, i)      \
+#define IGB_TX_CTXTDESC(R, i)  \
        (&(((struct e1000_adv_tx_context_desc *)((R)->desc))[i]))
 
 /* igb_test_staterr - tests bits within Rx descriptor status and error fields */
@@ -453,12 +462,12 @@ struct igb_adapter {
 #define IGB_FLAG_WOL_SUPPORTED         (1 << 8)
 
 /* DMA Coalescing defines */
-#define IGB_MIN_TXPBSIZE           20408
-#define IGB_TX_BUF_4096            4096
-#define IGB_DMCTLX_DCFLUSH_DIS     0x80000000  /* Disable DMA Coal Flush */
+#define IGB_MIN_TXPBSIZE       20408
+#define IGB_TX_BUF_4096                4096
+#define IGB_DMCTLX_DCFLUSH_DIS 0x80000000  /* Disable DMA Coal Flush */
 
-#define IGB_82576_TSYNC_SHIFT 19
-#define IGB_TS_HDR_LEN        16
+#define IGB_82576_TSYNC_SHIFT  19
+#define IGB_TS_HDR_LEN         16
 enum e1000_state_t {
        __IGB_TESTING,
        __IGB_RESETTING,
index a3830a8ba4c18a7558bc1177bb6f8c97b248cd67..48b594701efa7f51f5b975ec37aac3e70602b1f1 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
 #include <linux/highmem.h>
+#include <linux/mdio.h>
 
 #include "igb.h"
 
@@ -178,44 +179,67 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
 
                ecmd->port = PORT_TP;
                ecmd->phy_address = hw->phy.addr;
+               ecmd->transceiver = XCVR_INTERNAL;
        } else {
-               ecmd->supported   = (SUPPORTED_1000baseT_Full |
-                                    SUPPORTED_FIBRE |
-                                    SUPPORTED_Autoneg);
+               ecmd->supported = (SUPPORTED_1000baseT_Full |
+                                  SUPPORTED_100baseT_Full |
+                                  SUPPORTED_FIBRE |
+                                  SUPPORTED_Autoneg |
+                                  SUPPORTED_Pause);
+               if (hw->mac.type == e1000_i354)
+                               ecmd->supported |= SUPPORTED_2500baseX_Full;
 
-               ecmd->advertising = (ADVERTISED_1000baseT_Full |
-                                    ADVERTISED_FIBRE |
-                                    ADVERTISED_Autoneg |
-                                    ADVERTISED_Pause);
+               ecmd->advertising = ADVERTISED_FIBRE;
+
+               switch (adapter->link_speed) {
+               case SPEED_2500:
+                       ecmd->advertising = ADVERTISED_2500baseX_Full;
+                       break;
+               case SPEED_1000:
+                       ecmd->advertising = ADVERTISED_1000baseT_Full;
+                       break;
+               case SPEED_100:
+                       ecmd->advertising = ADVERTISED_100baseT_Full;
+                       break;
+               default:
+                       break;
+               }
+
+               if (hw->mac.autoneg == 1)
+                       ecmd->advertising |= ADVERTISED_Autoneg;
 
                ecmd->port = PORT_FIBRE;
+               ecmd->transceiver = XCVR_EXTERNAL;
        }
 
-       ecmd->transceiver = XCVR_INTERNAL;
-
        status = rd32(E1000_STATUS);
 
        if (status & E1000_STATUS_LU) {
-
-               if ((status & E1000_STATUS_SPEED_1000) ||
-                   hw->phy.media_type != e1000_media_type_copper)
-                       ethtool_cmd_speed_set(ecmd, SPEED_1000);
+               if ((hw->mac.type == e1000_i354) &&
+                   (status & E1000_STATUS_2P5_SKU) &&
+                   !(status & E1000_STATUS_2P5_SKU_OVER))
+                       ecmd->speed = SPEED_2500;
+               else if (status & E1000_STATUS_SPEED_1000)
+                       ecmd->speed = SPEED_1000;
                else if (status & E1000_STATUS_SPEED_100)
-                       ethtool_cmd_speed_set(ecmd, SPEED_100);
+                       ecmd->speed = SPEED_100;
                else
-                       ethtool_cmd_speed_set(ecmd, SPEED_10);
-
+                       ecmd->speed = SPEED_10;
                if ((status & E1000_STATUS_FD) ||
                    hw->phy.media_type != e1000_media_type_copper)
                        ecmd->duplex = DUPLEX_FULL;
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
+               ecmd->speed = -1;
                ecmd->duplex = -1;
        }
 
-       ecmd->autoneg = hw->mac.autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+       if ((hw->phy.media_type == e1000_media_type_fiber) ||
+           hw->mac.autoneg)
+               ecmd->autoneg = AUTONEG_ENABLE;
+       else
+               ecmd->autoneg = AUTONEG_DISABLE;
 
        /* MDI-X => 2; MDI =>1; Invalid =>0 */
        if (hw->phy.media_type == e1000_media_type_copper)
@@ -238,15 +262,15 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        struct e1000_hw *hw = &adapter->hw;
 
        /* When SoL/IDER sessions are active, autoneg/speed/duplex
-        * cannot be changed */
+        * cannot be changed
+        */
        if (igb_check_reset_block(hw)) {
                dev_err(&adapter->pdev->dev,
                        "Cannot change link characteristics when SoL/IDER is active.\n");
                return -EINVAL;
        }
 
-       /*
-        * MDI setting is only allowed when autoneg enabled because
+       /* MDI setting is only allowed when autoneg enabled because
         * some hardware doesn't allow MDI setting when speed or
         * duplex is forced.
         */
@@ -266,9 +290,31 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
 
        if (ecmd->autoneg == AUTONEG_ENABLE) {
                hw->mac.autoneg = 1;
-               hw->phy.autoneg_advertised = ecmd->advertising |
-                                            ADVERTISED_TP |
-                                            ADVERTISED_Autoneg;
+               if (hw->phy.media_type == e1000_media_type_fiber) {
+                       hw->phy.autoneg_advertised = ecmd->advertising |
+                                                    ADVERTISED_FIBRE |
+                                                    ADVERTISED_Autoneg;
+                       switch (adapter->link_speed) {
+                       case SPEED_2500:
+                               hw->phy.autoneg_advertised =
+                                       ADVERTISED_2500baseX_Full;
+                               break;
+                       case SPEED_1000:
+                               hw->phy.autoneg_advertised =
+                                       ADVERTISED_1000baseT_Full;
+                               break;
+                       case SPEED_100:
+                               hw->phy.autoneg_advertised =
+                                       ADVERTISED_100baseT_Full;
+                               break;
+                       default:
+                               break;
+                       }
+               } else {
+                       hw->phy.autoneg_advertised = ecmd->advertising |
+                                                    ADVERTISED_TP |
+                                                    ADVERTISED_Autoneg;
+               }
                ecmd->advertising = hw->phy.autoneg_advertised;
                if (adapter->fc_autoneg)
                        hw->fc.requested_mode = e1000_fc_default;
@@ -283,8 +329,7 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
 
        /* MDI-X => 2; MDI => 1; Auto => 3 */
        if (ecmd->eth_tp_mdix_ctrl) {
-               /*
-                * fix up the value for auto (3 => 0) as zero is mapped
+               /* fix up the value for auto (3 => 0) as zero is mapped
                 * internally to auto
                 */
                if (ecmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO)
@@ -309,8 +354,7 @@ static u32 igb_get_link(struct net_device *netdev)
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_mac_info *mac = &adapter->hw.mac;
 
-       /*
-        * If the link is not reported up to netdev, interrupts are disabled,
+       /* If the link is not reported up to netdev, interrupts are disabled,
         * and so the physical link state may have changed since we last
         * looked. Set get_link_status to make sure that the true link
         * state is interrogated, rather than pulling a cached and possibly
@@ -430,7 +474,8 @@ static void igb_get_regs(struct net_device *netdev,
 
        /* Interrupt */
        /* Reading EICS for EICR because they read the
-        * same but EICS does not clear on read */
+        * same but EICS does not clear on read
+        */
        regs_buff[13] = rd32(E1000_EICS);
        regs_buff[14] = rd32(E1000_EICS);
        regs_buff[15] = rd32(E1000_EIMS);
@@ -438,7 +483,8 @@ static void igb_get_regs(struct net_device *netdev,
        regs_buff[17] = rd32(E1000_EIAC);
        regs_buff[18] = rd32(E1000_EIAM);
        /* Reading ICS for ICR because they read the
-        * same but ICS does not clear on read */
+        * same but ICS does not clear on read
+        */
        regs_buff[19] = rd32(E1000_ICS);
        regs_buff[20] = rd32(E1000_ICS);
        regs_buff[21] = rd32(E1000_IMS);
@@ -688,12 +734,12 @@ static int igb_get_eeprom(struct net_device *netdev,
 
        if (hw->nvm.type == e1000_nvm_eeprom_spi)
                ret_val = hw->nvm.ops.read(hw, first_word,
-                                           last_word - first_word + 1,
-                                           eeprom_buff);
+                                          last_word - first_word + 1,
+                                          eeprom_buff);
        else {
                for (i = 0; i < last_word - first_word + 1; i++) {
                        ret_val = hw->nvm.ops.read(hw, first_word + i, 1,
-                                                   &eeprom_buff[i]);
+                                                  &eeprom_buff[i]);
                        if (ret_val)
                                break;
                }
@@ -740,15 +786,17 @@ static int igb_set_eeprom(struct net_device *netdev,
        ptr = (void *)eeprom_buff;
 
        if (eeprom->offset & 1) {
-               /* need read/modify/write of first changed EEPROM word */
-               /* only the second byte of the word is being modified */
+               /* need read/modify/write of first changed EEPROM word
+                * only the second byte of the word is being modified
+                */
                ret_val = hw->nvm.ops.read(hw, first_word, 1,
                                            &eeprom_buff[0]);
                ptr++;
        }
        if (((eeprom->offset + eeprom->len) & 1) && (ret_val == 0)) {
-               /* need read/modify/write of last changed EEPROM word */
-               /* only the first byte of the word is being modified */
+               /* need read/modify/write of last changed EEPROM word
+                * only the first byte of the word is being modified
+                */
                ret_val = hw->nvm.ops.read(hw, last_word, 1,
                                   &eeprom_buff[last_word - first_word]);
        }
@@ -763,10 +811,11 @@ static int igb_set_eeprom(struct net_device *netdev,
                eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]);
 
        ret_val = hw->nvm.ops.write(hw, first_word,
-                                    last_word - first_word + 1, eeprom_buff);
+                                   last_word - first_word + 1, eeprom_buff);
 
        /* Update the checksum over the first part of the EEPROM if needed
-        * and flush shadow RAM for 82573 controllers */
+        * and flush shadow RAM for 82573 controllers
+        */
        if ((ret_val == 0) && ((first_word <= NVM_CHECKSUM_REG)))
                hw->nvm.ops.update(hw);
 
@@ -783,8 +832,7 @@ static void igb_get_drvinfo(struct net_device *netdev,
        strlcpy(drvinfo->driver,  igb_driver_name, sizeof(drvinfo->driver));
        strlcpy(drvinfo->version, igb_driver_version, sizeof(drvinfo->version));
 
-       /*
-        * EEPROM image version # is reported as firmware version # for
+       /* EEPROM image version # is reported as firmware version # for
         * 82575 controllers
         */
        strlcpy(drvinfo->fw_version, adapter->fw_version,
@@ -847,9 +895,11 @@ static int igb_set_ringparam(struct net_device *netdev,
        }
 
        if (adapter->num_tx_queues > adapter->num_rx_queues)
-               temp_ring = vmalloc(adapter->num_tx_queues * sizeof(struct igb_ring));
+               temp_ring = vmalloc(adapter->num_tx_queues *
+                                   sizeof(struct igb_ring));
        else
-               temp_ring = vmalloc(adapter->num_rx_queues * sizeof(struct igb_ring));
+               temp_ring = vmalloc(adapter->num_rx_queues *
+                                   sizeof(struct igb_ring));
 
        if (!temp_ring) {
                err = -ENOMEM;
@@ -858,10 +908,9 @@ static int igb_set_ringparam(struct net_device *netdev,
 
        igb_down(adapter);
 
-       /*
-        * We can't just free everything and then setup again,
+       /* We can't just free everything and then setup again,
         * because the ISRs in MSI-X mode get passed pointers
-        * to the tx and rx ring structs.
+        * to the Tx and Rx ring structs.
         */
        if (new_tx_count != adapter->tx_ring_count) {
                for (i = 0; i < adapter->num_tx_queues; i++) {
@@ -1199,6 +1248,7 @@ static int igb_reg_test(struct igb_adapter *adapter, u64 *data)
 
        switch (adapter->hw.mac.type) {
        case e1000_i350:
+       case e1000_i354:
                test = reg_test_i350;
                toggle = 0x7FEFF3FF;
                break;
@@ -1361,6 +1411,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
                ics_mask = 0x77DCFED5;
                break;
        case e1000_i350:
+       case e1000_i354:
        case e1000_i210:
        case e1000_i211:
                ics_mask = 0x77DCFED5;
@@ -1723,8 +1774,8 @@ static int igb_check_lbtest_frame(struct igb_rx_buffer *rx_buffer,
 }
 
 static int igb_clean_test_rings(struct igb_ring *rx_ring,
-                                struct igb_ring *tx_ring,
-                                unsigned int size)
+                               struct igb_ring *tx_ring,
+                               unsigned int size)
 {
        union e1000_adv_rx_desc *rx_desc;
        struct igb_rx_buffer *rx_buffer_info;
@@ -1737,7 +1788,7 @@ static int igb_clean_test_rings(struct igb_ring *rx_ring,
        rx_desc = IGB_RX_DESC(rx_ring, rx_ntc);
 
        while (igb_test_staterr(rx_desc, E1000_RXD_STAT_DD)) {
-               /* check rx buffer */
+               /* check Rx buffer */
                rx_buffer_info = &rx_ring->rx_buffer_info[rx_ntc];
 
                /* sync Rx buffer for CPU read */
@@ -1756,11 +1807,11 @@ static int igb_clean_test_rings(struct igb_ring *rx_ring,
                                           IGB_RX_BUFSZ,
                                           DMA_FROM_DEVICE);
 
-               /* unmap buffer on tx side */
+               /* unmap buffer on Tx side */
                tx_buffer_info = &tx_ring->tx_buffer_info[tx_ntc];
                igb_unmap_and_free_tx_resource(tx_ring, tx_buffer_info);
 
-               /* increment rx/tx next to clean counters */
+               /* increment Rx/Tx next to clean counters */
                rx_ntc++;
                if (rx_ntc == rx_ring->count)
                        rx_ntc = 0;
@@ -1801,8 +1852,7 @@ static int igb_run_loopback_test(struct igb_adapter *adapter)
        igb_create_lbtest_frame(skb, size);
        skb_put(skb, size);
 
-       /*
-        * Calculate the loop count based on the largest descriptor ring
+       /* Calculate the loop count based on the largest descriptor ring
         * The idea is to wrap the largest ring a number of times using 64
         * send/receive pairs during each loop
         */
@@ -1829,7 +1879,7 @@ static int igb_run_loopback_test(struct igb_adapter *adapter)
                        break;
                }
 
-               /* allow 200 milliseconds for packets to go from tx to rx */
+               /* allow 200 milliseconds for packets to go from Tx to Rx */
                msleep(200);
 
                good_cnt = igb_clean_test_rings(rx_ring, tx_ring, size);
@@ -1848,13 +1898,21 @@ static int igb_run_loopback_test(struct igb_adapter *adapter)
 static int igb_loopback_test(struct igb_adapter *adapter, u64 *data)
 {
        /* PHY loopback cannot be performed if SoL/IDER
-        * sessions are active */
+        * sessions are active
+        */
        if (igb_check_reset_block(&adapter->hw)) {
                dev_err(&adapter->pdev->dev,
                        "Cannot do PHY loopback test when SoL/IDER is active.\n");
                *data = 0;
                goto out;
        }
+
+       if (adapter->hw.mac.type == e1000_i354) {
+               dev_info(&adapter->pdev->dev,
+                       "Loopback test not supported on i354.\n");
+               *data = 0;
+               goto out;
+       }
        *data = igb_setup_desc_rings(adapter);
        if (*data)
                goto out;
@@ -1879,7 +1937,8 @@ static int igb_link_test(struct igb_adapter *adapter, u64 *data)
                hw->mac.serdes_has_link = false;
 
                /* On some blade server designs, link establishment
-                * could take as long as 2-3 minutes */
+                * could take as long as 2-3 minutes
+                */
                do {
                        hw->mac.ops.check_for_link(&adapter->hw);
                        if (hw->mac.serdes_has_link)
@@ -1922,7 +1981,8 @@ static void igb_diag_test(struct net_device *netdev,
                igb_power_up_link(adapter);
 
                /* Link test performed before hardware reset so autoneg doesn't
-                * interfere with test result */
+                * interfere with test result
+                */
                if (igb_link_test(adapter, &data[4]))
                        eth_test->flags |= ETH_TEST_FL_FAILED;
 
@@ -1987,8 +2047,8 @@ static void igb_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
        struct igb_adapter *adapter = netdev_priv(netdev);
 
        wol->supported = WAKE_UCAST | WAKE_MCAST |
-                        WAKE_BCAST | WAKE_MAGIC |
-                        WAKE_PHY;
+                        WAKE_BCAST | WAKE_MAGIC |
+                        WAKE_PHY;
        wol->wolopts = 0;
 
        if (!(adapter->flags & IGB_FLAG_WOL_SUPPORTED))
@@ -2263,7 +2323,7 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
                        sprintf(p, "rx_queue_%u_alloc_failed", i);
                        p += ETH_GSTRING_LEN;
                }
-/*             BUG_ON(p - data != IGB_STATS_LEN * ETH_GSTRING_LEN); */
+               /* BUG_ON(p - data != IGB_STATS_LEN * ETH_GSTRING_LEN); */
                break;
        }
 }
@@ -2283,6 +2343,7 @@ static int igb_get_ts_info(struct net_device *dev,
        case e1000_82576:
        case e1000_82580:
        case e1000_i350:
+       case e1000_i354:
        case e1000_i210:
        case e1000_i211:
                info->so_timestamping =
@@ -2362,7 +2423,7 @@ static int igb_get_rss_hash_opts(struct igb_adapter *adapter,
 }
 
 static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
-                          u32 *rule_locs)
+                        u32 *rule_locs)
 {
        struct igb_adapter *adapter = netdev_priv(dev);
        int ret = -EOPNOTSUPP;
@@ -2506,7 +2567,8 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       u32 ipcnfg, eeer;
+       u32 ipcnfg, eeer, ret_val;
+       u16 phy_data;
 
        if ((hw->mac.type < e1000_i350) ||
            (hw->phy.media_type != e1000_media_type_copper))
@@ -2525,6 +2587,32 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
        if (ipcnfg & E1000_IPCNFG_EEE_100M_AN)
                edata->advertised |= ADVERTISED_100baseT_Full;
 
+       /* EEE Link Partner Advertised */
+       switch (hw->mac.type) {
+       case e1000_i350:
+               ret_val = igb_read_emi_reg(hw, E1000_EEE_LP_ADV_ADDR_I350,
+                                          &phy_data);
+               if (ret_val)
+                       return -ENODATA;
+
+               edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data);
+
+               break;
+       case e1000_i210:
+       case e1000_i211:
+               ret_val = igb_read_xmdio_reg(hw, E1000_EEE_LP_ADV_ADDR_I210,
+                                            E1000_EEE_LP_ADV_DEV_I210,
+                                            &phy_data);
+               if (ret_val)
+                       return -ENODATA;
+
+               edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data);
+
+               break;
+       default:
+               break;
+       }
+
        if (eeer & E1000_EEER_EEE_NEG)
                edata->eee_active = true;
 
@@ -2600,6 +2688,85 @@ static int igb_set_eee(struct net_device *netdev,
        return 0;
 }
 
+static int igb_get_module_info(struct net_device *netdev,
+                              struct ethtool_modinfo *modinfo)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       struct e1000_hw *hw = &adapter->hw;
+       u32 status = E1000_SUCCESS;
+       u16 sff8472_rev, addr_mode;
+       bool page_swap = false;
+
+       if ((hw->phy.media_type == e1000_media_type_copper) ||
+           (hw->phy.media_type == e1000_media_type_unknown))
+               return -EOPNOTSUPP;
+
+       /* Check whether we support SFF-8472 or not */
+       status = igb_read_phy_reg_i2c(hw, IGB_SFF_8472_COMP, &sff8472_rev);
+       if (status != E1000_SUCCESS)
+               return -EIO;
+
+       /* addressing mode is not supported */
+       status = igb_read_phy_reg_i2c(hw, IGB_SFF_8472_SWAP, &addr_mode);
+       if (status != E1000_SUCCESS)
+               return -EIO;
+
+       /* addressing mode is not supported */
+       if ((addr_mode & 0xFF) & IGB_SFF_ADDRESSING_MODE) {
+               hw_dbg("Address change required to access page 0xA2, but not supported. Please report the module type to the driver maintainers.\n");
+               page_swap = true;
+       }
+
+       if ((sff8472_rev & 0xFF) == IGB_SFF_8472_UNSUP || page_swap) {
+               /* We have an SFP, but it does not support SFF-8472 */
+               modinfo->type = ETH_MODULE_SFF_8079;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
+       } else {
+               /* We have an SFP which supports a revision of SFF-8472 */
+               modinfo->type = ETH_MODULE_SFF_8472;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+       }
+
+       return 0;
+}
+
+static int igb_get_module_eeprom(struct net_device *netdev,
+                                struct ethtool_eeprom *ee, u8 *data)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       struct e1000_hw *hw = &adapter->hw;
+       u32 status = E1000_SUCCESS;
+       u16 *dataword;
+       u16 first_word, last_word;
+       int i = 0;
+
+       if (ee->len == 0)
+               return -EINVAL;
+
+       first_word = ee->offset >> 1;
+       last_word = (ee->offset + ee->len - 1) >> 1;
+
+       dataword = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+                          GFP_KERNEL);
+       if (!dataword)
+               return -ENOMEM;
+
+       /* Read EEPROM block, SFF-8079/SFF-8472, word at a time */
+       for (i = 0; i < last_word - first_word + 1; i++) {
+               status = igb_read_phy_reg_i2c(hw, first_word + i, &dataword[i]);
+               if (status != E1000_SUCCESS)
+                       /* Error occurred while reading module */
+                       return -EIO;
+
+               be16_to_cpus(&dataword[i]);
+       }
+
+       memcpy(data, (u8 *)dataword + (ee->offset & 1), ee->len);
+       kfree(dataword);
+
+       return 0;
+}
+
 static int igb_ethtool_begin(struct net_device *netdev)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
@@ -2614,36 +2781,38 @@ static void igb_ethtool_complete(struct net_device *netdev)
 }
 
 static const struct ethtool_ops igb_ethtool_ops = {
-       .get_settings           = igb_get_settings,
-       .set_settings           = igb_set_settings,
-       .get_drvinfo            = igb_get_drvinfo,
-       .get_regs_len           = igb_get_regs_len,
-       .get_regs               = igb_get_regs,
-       .get_wol                = igb_get_wol,
-       .set_wol                = igb_set_wol,
-       .get_msglevel           = igb_get_msglevel,
-       .set_msglevel           = igb_set_msglevel,
-       .nway_reset             = igb_nway_reset,
-       .get_link               = igb_get_link,
-       .get_eeprom_len         = igb_get_eeprom_len,
-       .get_eeprom             = igb_get_eeprom,
-       .set_eeprom             = igb_set_eeprom,
-       .get_ringparam          = igb_get_ringparam,
-       .set_ringparam          = igb_set_ringparam,
-       .get_pauseparam         = igb_get_pauseparam,
-       .set_pauseparam         = igb_set_pauseparam,
-       .self_test              = igb_diag_test,
-       .get_strings            = igb_get_strings,
-       .set_phys_id            = igb_set_phys_id,
-       .get_sset_count         = igb_get_sset_count,
-       .get_ethtool_stats      = igb_get_ethtool_stats,
-       .get_coalesce           = igb_get_coalesce,
-       .set_coalesce           = igb_set_coalesce,
-       .get_ts_info            = igb_get_ts_info,
+       .get_settings           = igb_get_settings,
+       .set_settings           = igb_set_settings,
+       .get_drvinfo            = igb_get_drvinfo,
+       .get_regs_len           = igb_get_regs_len,
+       .get_regs               = igb_get_regs,
+       .get_wol                = igb_get_wol,
+       .set_wol                = igb_set_wol,
+       .get_msglevel           = igb_get_msglevel,
+       .set_msglevel           = igb_set_msglevel,
+       .nway_reset             = igb_nway_reset,
+       .get_link               = igb_get_link,
+       .get_eeprom_len         = igb_get_eeprom_len,
+       .get_eeprom             = igb_get_eeprom,
+       .set_eeprom             = igb_set_eeprom,
+       .get_ringparam          = igb_get_ringparam,
+       .set_ringparam          = igb_set_ringparam,
+       .get_pauseparam         = igb_get_pauseparam,
+       .set_pauseparam         = igb_set_pauseparam,
+       .self_test              = igb_diag_test,
+       .get_strings            = igb_get_strings,
+       .set_phys_id            = igb_set_phys_id,
+       .get_sset_count         = igb_get_sset_count,
+       .get_ethtool_stats      = igb_get_ethtool_stats,
+       .get_coalesce           = igb_get_coalesce,
+       .set_coalesce           = igb_set_coalesce,
+       .get_ts_info            = igb_get_ts_info,
        .get_rxnfc              = igb_get_rxnfc,
        .set_rxnfc              = igb_set_rxnfc,
        .get_eee                = igb_get_eee,
        .set_eee                = igb_set_eee,
+       .get_module_info        = igb_get_module_info,
+       .get_module_eeprom      = igb_get_module_eeprom,
        .begin                  = igb_ethtool_begin,
        .complete               = igb_ethtool_complete,
 };
index 0478a1abe54110d1374c6d77d950f6a4d7bf0327..58f1ce967aebfed707ed2986c968654dd49fa829 100644 (file)
@@ -45,21 +45,21 @@ static struct i2c_board_info i350_sensor_info = {
 
 /* hwmon callback functions */
 static ssize_t igb_hwmon_show_location(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
+                                      struct device_attribute *attr,
+                                      char *buf)
 {
        struct hwmon_attr *igb_attr = container_of(attr, struct hwmon_attr,
-                                                    dev_attr);
+                                                  dev_attr);
        return sprintf(buf, "loc%u\n",
                       igb_attr->sensor->location);
 }
 
 static ssize_t igb_hwmon_show_temp(struct device *dev,
-                                    struct device_attribute *attr,
-                                    char *buf)
+                                  struct device_attribute *attr,
+                                  char *buf)
 {
        struct hwmon_attr *igb_attr = container_of(attr, struct hwmon_attr,
-                                                    dev_attr);
+                                                  dev_attr);
        unsigned int value;
 
        /* reset the temp field */
@@ -74,11 +74,11 @@ static ssize_t igb_hwmon_show_temp(struct device *dev,
 }
 
 static ssize_t igb_hwmon_show_cautionthresh(struct device *dev,
-                                    struct device_attribute *attr,
-                                    char *buf)
+                                           struct device_attribute *attr,
+                                           char *buf)
 {
        struct hwmon_attr *igb_attr = container_of(attr, struct hwmon_attr,
-                                                    dev_attr);
+                                                  dev_attr);
        unsigned int value = igb_attr->sensor->caution_thresh;
 
        /* display millidegree */
@@ -88,11 +88,11 @@ static ssize_t igb_hwmon_show_cautionthresh(struct device *dev,
 }
 
 static ssize_t igb_hwmon_show_maxopthresh(struct device *dev,
-                                    struct device_attribute *attr,
-                                    char *buf)
+                                         struct device_attribute *attr,
+                                         char *buf)
 {
        struct hwmon_attr *igb_attr = container_of(attr, struct hwmon_attr,
-                                                    dev_attr);
+                                                  dev_attr);
        unsigned int value = igb_attr->sensor->max_op_thresh;
 
        /* display millidegree */
@@ -111,7 +111,8 @@ static ssize_t igb_hwmon_show_maxopthresh(struct device *dev,
  * the data structures we need to get the data to display.
  */
 static int igb_add_hwmon_attr(struct igb_adapter *adapter,
-                               unsigned int offset, int type) {
+                             unsigned int offset, int type)
+{
        int rc;
        unsigned int n_attr;
        struct hwmon_attr *igb_attr;
@@ -217,7 +218,7 @@ int igb_sysfs_init(struct igb_adapter *adapter)
         */
        n_attrs = E1000_MAX_SENSORS * 4;
        igb_hwmon->hwmon_list = kcalloc(n_attrs, sizeof(struct hwmon_attr),
-                                         GFP_KERNEL);
+                                       GFP_KERNEL);
        if (!igb_hwmon->hwmon_list) {
                rc = -ENOMEM;
                goto err;
index 64f75291e3a5ca5402dfbee6dccb7a68bf1f1dd0..dcaa35481dd7997048b8f22139cb572ddf518fb6 100644 (file)
@@ -77,6 +77,9 @@ static const struct e1000_info *igb_info_tbl[] = {
 };
 
 static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
+       { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_1GBPS) },
+       { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII) },
+       { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I211_COPPER), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_FIBER), board_82575 },
@@ -156,8 +159,8 @@ static int igb_ioctl(struct net_device *, struct ifreq *, int cmd);
 static void igb_tx_timeout(struct net_device *);
 static void igb_reset_task(struct work_struct *);
 static void igb_vlan_mode(struct net_device *netdev, netdev_features_t features);
-static int igb_vlan_rx_add_vid(struct net_device *, u16);
-static int igb_vlan_rx_kill_vid(struct net_device *, u16);
+static int igb_vlan_rx_add_vid(struct net_device *, __be16, u16);
+static int igb_vlan_rx_kill_vid(struct net_device *, __be16, u16);
 static void igb_restore_vlan(struct igb_adapter *);
 static void igb_rar_set_qsel(struct igb_adapter *, u8 *, u32 , u8);
 static void igb_ping_all_vfs(struct igb_adapter *);
@@ -169,6 +172,8 @@ static int igb_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac);
 static int igb_ndo_set_vf_vlan(struct net_device *netdev,
                               int vf, u16 vlan, u8 qos);
 static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate);
+static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
+                                  bool setting);
 static int igb_ndo_get_vf_config(struct net_device *netdev, int vf,
                                 struct ifla_vf_info *ivi);
 static void igb_check_vf_rate_limit(struct igb_adapter *);
@@ -292,9 +297,7 @@ static const struct igb_reg_info igb_reg_info_tbl[] = {
        {}
 };
 
-/*
- * igb_regdump - register printout routine
- */
+/* igb_regdump - register printout routine */
 static void igb_regdump(struct e1000_hw *hw, struct igb_reg_info *reginfo)
 {
        int n = 0;
@@ -360,9 +363,7 @@ static void igb_regdump(struct e1000_hw *hw, struct igb_reg_info *reginfo)
                regs[2], regs[3]);
 }
 
-/*
- * igb_dump - Print registers, tx-rings and rx-rings
- */
+/* igb_dump - Print registers, Tx-rings and Rx-rings */
 static void igb_dump(struct igb_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
@@ -569,12 +570,13 @@ exit:
        return;
 }
 
-/*  igb_get_i2c_data - Reads the I2C SDA data bit
+/**
+ *  igb_get_i2c_data - Reads the I2C SDA data bit
  *  @hw: pointer to hardware structure
  *  @i2cctl: Current value of I2CCTL register
  *
  *  Returns the I2C data bit value
- */
+ **/
 static int igb_get_i2c_data(void *data)
 {
        struct igb_adapter *adapter = (struct igb_adapter *)data;
@@ -584,12 +586,13 @@ static int igb_get_i2c_data(void *data)
        return ((i2cctl & E1000_I2C_DATA_IN) != 0);
 }
 
-/* igb_set_i2c_data - Sets the I2C data bit
+/**
+ *  igb_set_i2c_data - Sets the I2C data bit
  *  @data: pointer to hardware structure
  *  @state: I2C data value (0 or 1) to set
  *
  *  Sets the I2C data bit
- */
+ **/
 static void igb_set_i2c_data(void *data, int state)
 {
        struct igb_adapter *adapter = (struct igb_adapter *)data;
@@ -608,12 +611,13 @@ static void igb_set_i2c_data(void *data, int state)
 
 }
 
-/* igb_set_i2c_clk - Sets the I2C SCL clock
+/**
+ *  igb_set_i2c_clk - Sets the I2C SCL clock
  *  @data: pointer to hardware structure
  *  @state: state to set clock
  *
  *  Sets the I2C clock line to state
- */
+ **/
 static void igb_set_i2c_clk(void *data, int state)
 {
        struct igb_adapter *adapter = (struct igb_adapter *)data;
@@ -631,11 +635,12 @@ static void igb_set_i2c_clk(void *data, int state)
        wrfl();
 }
 
-/* igb_get_i2c_clk - Gets the I2C SCL clock state
+/**
+ *  igb_get_i2c_clk - Gets the I2C SCL clock state
  *  @data: pointer to hardware structure
  *
  *  Gets the I2C clock state
- */
+ **/
 static int igb_get_i2c_clk(void *data)
 {
        struct igb_adapter *adapter = (struct igb_adapter *)data;
@@ -655,8 +660,10 @@ static const struct i2c_algo_bit_data igb_i2c_algo = {
 };
 
 /**
- * igb_get_hw_dev - return device
- * used by hardware layer to print debugging information
+ *  igb_get_hw_dev - return device
+ *  @hw: pointer to hardware structure
+ *
+ *  used by hardware layer to print debugging information
  **/
 struct net_device *igb_get_hw_dev(struct e1000_hw *hw)
 {
@@ -665,10 +672,10 @@ struct net_device *igb_get_hw_dev(struct e1000_hw *hw)
 }
 
 /**
- * igb_init_module - Driver Registration Routine
+ *  igb_init_module - Driver Registration Routine
  *
- * igb_init_module is the first routine called when the driver is
- * loaded. All it does is register with the PCI subsystem.
+ *  igb_init_module is the first routine called when the driver is
+ *  loaded. All it does is register with the PCI subsystem.
  **/
 static int __init igb_init_module(void)
 {
@@ -688,10 +695,10 @@ static int __init igb_init_module(void)
 module_init(igb_init_module);
 
 /**
- * igb_exit_module - Driver Exit Cleanup Routine
+ *  igb_exit_module - Driver Exit Cleanup Routine
  *
- * igb_exit_module is called just before the driver is removed
- * from memory.
+ *  igb_exit_module is called just before the driver is removed
+ *  from memory.
  **/
 static void __exit igb_exit_module(void)
 {
@@ -705,11 +712,11 @@ module_exit(igb_exit_module);
 
 #define Q_IDX_82576(i) (((i & 0x1) << 3) + (i >> 1))
 /**
- * igb_cache_ring_register - Descriptor ring to register mapping
- * @adapter: board private structure to initialize
+ *  igb_cache_ring_register - Descriptor ring to register mapping
+ *  @adapter: board private structure to initialize
  *
- * Once we know the feature-set enabled for the device, we'll cache
- * the register offset the descriptor ring is assigned to.
+ *  Once we know the feature-set enabled for the device, we'll cache
+ *  the register offset the descriptor ring is assigned to.
  **/
 static void igb_cache_ring_register(struct igb_adapter *adapter)
 {
@@ -726,11 +733,12 @@ static void igb_cache_ring_register(struct igb_adapter *adapter)
                if (adapter->vfs_allocated_count) {
                        for (; i < adapter->rss_queues; i++)
                                adapter->rx_ring[i]->reg_idx = rbase_offset +
-                                                              Q_IDX_82576(i);
+                                                              Q_IDX_82576(i);
                }
        case e1000_82575:
        case e1000_82580:
        case e1000_i350:
+       case e1000_i354:
        case e1000_i210:
        case e1000_i211:
        default:
@@ -785,9 +793,10 @@ static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector)
        switch (hw->mac.type) {
        case e1000_82575:
                /* The 82575 assigns vectors using a bitmask, which matches the
-                  bitmask for the EICR/EIMS/EIMC registers.  To assign one
-                  or more queues to a vector, we write the appropriate bits
-                  into the MSIXBM register for that vector. */
+                * bitmask for the EICR/EIMS/EIMC registers.  To assign one
+                * or more queues to a vector, we write the appropriate bits
+                * into the MSIXBM register for that vector.
+                */
                if (rx_queue > IGB_N0_QUEUE)
                        msixbm = E1000_EICR_RX_QUEUE0 << rx_queue;
                if (tx_queue > IGB_N0_QUEUE)
@@ -798,8 +807,7 @@ static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector)
                q_vector->eims_value = msixbm;
                break;
        case e1000_82576:
-               /*
-                * 82576 uses a table that essentially consists of 2 columns
+               /* 82576 uses a table that essentially consists of 2 columns
                 * with 8 rows.  The ordering is column-major so we use the
                 * lower 3 bits as the row index, and the 4th bit as the
                 * column offset.
@@ -816,10 +824,10 @@ static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector)
                break;
        case e1000_82580:
        case e1000_i350:
+       case e1000_i354:
        case e1000_i210:
        case e1000_i211:
-               /*
-                * On 82580 and newer adapters the scheme is similar to 82576
+               /* On 82580 and newer adapters the scheme is similar to 82576
                 * however instead of ordering column-major we have things
                 * ordered row-major.  So we traverse the table by using
                 * bit 0 as the column offset, and the remaining bits as the
@@ -848,10 +856,11 @@ static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector)
 }
 
 /**
- * igb_configure_msix - Configure MSI-X hardware
+ *  igb_configure_msix - Configure MSI-X hardware
+ *  @adapter: board private structure to initialize
  *
- * igb_configure_msix sets up the hardware to properly
- * generate MSI-X interrupts.
+ *  igb_configure_msix sets up the hardware to properly
+ *  generate MSI-X interrupts.
  **/
 static void igb_configure_msix(struct igb_adapter *adapter)
 {
@@ -875,8 +884,7 @@ static void igb_configure_msix(struct igb_adapter *adapter)
                wr32(E1000_CTRL_EXT, tmp);
 
                /* enable msix_other interrupt */
-               array_wr32(E1000_MSIXBM(0), vector++,
-                                     E1000_EIMS_OTHER);
+               array_wr32(E1000_MSIXBM(0), vector++, E1000_EIMS_OTHER);
                adapter->eims_other = E1000_EIMS_OTHER;
 
                break;
@@ -884,13 +892,15 @@ static void igb_configure_msix(struct igb_adapter *adapter)
        case e1000_82576:
        case e1000_82580:
        case e1000_i350:
+       case e1000_i354:
        case e1000_i210:
        case e1000_i211:
                /* Turn on MSI-X capability first, or our settings
-                * won't stick.  And it will take days to debug. */
+                * won't stick.  And it will take days to debug.
+                */
                wr32(E1000_GPIE, E1000_GPIE_MSIX_MODE |
-                               E1000_GPIE_PBA | E1000_GPIE_EIAME |
-                               E1000_GPIE_NSICR);
+                    E1000_GPIE_PBA | E1000_GPIE_EIAME |
+                    E1000_GPIE_NSICR);
 
                /* enable msix_other interrupt */
                adapter->eims_other = 1 << vector;
@@ -912,10 +922,11 @@ static void igb_configure_msix(struct igb_adapter *adapter)
 }
 
 /**
- * igb_request_msix - Initialize MSI-X interrupts
+ *  igb_request_msix - Initialize MSI-X interrupts
+ *  @adapter: board private structure to initialize
  *
- * igb_request_msix allocates MSI-X vectors and requests interrupts from the
- * kernel.
+ *  igb_request_msix allocates MSI-X vectors and requests interrupts from the
+ *  kernel.
  **/
 static int igb_request_msix(struct igb_adapter *adapter)
 {
@@ -924,7 +935,7 @@ static int igb_request_msix(struct igb_adapter *adapter)
        int i, err = 0, vector = 0, free_vector = 0;
 
        err = request_irq(adapter->msix_entries[vector].vector,
-                         igb_msix_other, 0, netdev->name, adapter);
+                         igb_msix_other, 0, netdev->name, adapter);
        if (err)
                goto err_out;
 
@@ -948,8 +959,8 @@ static int igb_request_msix(struct igb_adapter *adapter)
                        sprintf(q_vector->name, "%s-unused", netdev->name);
 
                err = request_irq(adapter->msix_entries[vector].vector,
-                                 igb_msix_ring, 0, q_vector->name,
-                                 q_vector);
+                                 igb_msix_ring, 0, q_vector->name,
+                                 q_vector);
                if (err)
                        goto err_free;
        }
@@ -982,13 +993,13 @@ static void igb_reset_interrupt_capability(struct igb_adapter *adapter)
 }
 
 /**
- * igb_free_q_vector - Free memory allocated for specific interrupt vector
- * @adapter: board private structure to initialize
- * @v_idx: Index of vector to be freed
+ *  igb_free_q_vector - Free memory allocated for specific interrupt vector
+ *  @adapter: board private structure to initialize
+ *  @v_idx: Index of vector to be freed
  *
- * This function frees the memory allocated to the q_vector.  In addition if
- * NAPI is enabled it will delete any references to the NAPI struct prior
- * to freeing the q_vector.
+ *  This function frees the memory allocated to the q_vector.  In addition if
+ *  NAPI is enabled it will delete any references to the NAPI struct prior
+ *  to freeing the q_vector.
  **/
 static void igb_free_q_vector(struct igb_adapter *adapter, int v_idx)
 {
@@ -1003,20 +1014,19 @@ static void igb_free_q_vector(struct igb_adapter *adapter, int v_idx)
        adapter->q_vector[v_idx] = NULL;
        netif_napi_del(&q_vector->napi);
 
-       /*
-        * ixgbe_get_stats64() might access the rings on this vector,
+       /* ixgbe_get_stats64() might access the rings on this vector,
         * we must wait a grace period before freeing it.
         */
        kfree_rcu(q_vector, rcu);
 }
 
 /**
- * igb_free_q_vectors - Free memory allocated for interrupt vectors
- * @adapter: board private structure to initialize
+ *  igb_free_q_vectors - Free memory allocated for interrupt vectors
+ *  @adapter: board private structure to initialize
  *
- * This function frees the memory allocated to the q_vectors.  In addition if
- * NAPI is enabled it will delete any references to the NAPI struct prior
- * to freeing the q_vector.
+ *  This function frees the memory allocated to the q_vectors.  In addition if
+ *  NAPI is enabled it will delete any references to the NAPI struct prior
+ *  to freeing the q_vector.
  **/
 static void igb_free_q_vectors(struct igb_adapter *adapter)
 {
@@ -1031,10 +1041,11 @@ static void igb_free_q_vectors(struct igb_adapter *adapter)
 }
 
 /**
- * igb_clear_interrupt_scheme - reset the device to a state of no interrupts
+ *  igb_clear_interrupt_scheme - reset the device to a state of no interrupts
+ *  @adapter: board private structure to initialize
  *
- * This function resets the device so that it has 0 rx queues, tx queues, and
- * MSI-X interrupts allocated.
+ *  This function resets the device so that it has 0 Rx queues, Tx queues, and
+ *  MSI-X interrupts allocated.
  */
 static void igb_clear_interrupt_scheme(struct igb_adapter *adapter)
 {
@@ -1043,10 +1054,12 @@ static void igb_clear_interrupt_scheme(struct igb_adapter *adapter)
 }
 
 /**
- * igb_set_interrupt_capability - set MSI or MSI-X if supported
+ *  igb_set_interrupt_capability - set MSI or MSI-X if supported
+ *  @adapter: board private structure to initialize
+ *  @msix: boolean value of MSIX capability
  *
- * Attempt to configure interrupts using the best available
- * capabilities of the hardware and kernel.
+ *  Attempt to configure interrupts using the best available
+ *  capabilities of the hardware and kernel.
  **/
 static void igb_set_interrupt_capability(struct igb_adapter *adapter, bool msix)
 {
@@ -1063,10 +1076,10 @@ static void igb_set_interrupt_capability(struct igb_adapter *adapter, bool msix)
        else
                adapter->num_tx_queues = adapter->rss_queues;
 
-       /* start with one vector for every rx queue */
+       /* start with one vector for every Rx queue */
        numvecs = adapter->num_rx_queues;
 
-       /* if tx handler is separate add 1 for every tx queue */
+       /* if Tx handler is separate add 1 for every Tx queue */
        if (!(adapter->flags & IGB_FLAG_QUEUE_PAIRS))
                numvecs += adapter->num_tx_queues;
 
@@ -1128,16 +1141,16 @@ static void igb_add_ring(struct igb_ring *ring,
 }
 
 /**
- * igb_alloc_q_vector - Allocate memory for a single interrupt vector
- * @adapter: board private structure to initialize
- * @v_count: q_vectors allocated on adapter, used for ring interleaving
- * @v_idx: index of vector in adapter struct
- * @txr_count: total number of Tx rings to allocate
- * @txr_idx: index of first Tx ring to allocate
- * @rxr_count: total number of Rx rings to allocate
- * @rxr_idx: index of first Rx ring to allocate
+ *  igb_alloc_q_vector - Allocate memory for a single interrupt vector
+ *  @adapter: board private structure to initialize
+ *  @v_count: q_vectors allocated on adapter, used for ring interleaving
+ *  @v_idx: index of vector in adapter struct
+ *  @txr_count: total number of Tx rings to allocate
+ *  @txr_idx: index of first Tx ring to allocate
+ *  @rxr_count: total number of Rx rings to allocate
+ *  @rxr_idx: index of first Rx ring to allocate
  *
- * We allocate one q_vector.  If allocation fails we return -ENOMEM.
+ *  We allocate one q_vector.  If allocation fails we return -ENOMEM.
  **/
 static int igb_alloc_q_vector(struct igb_adapter *adapter,
                              int v_count, int v_idx,
@@ -1179,6 +1192,17 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
        /* initialize pointer to rings */
        ring = q_vector->ring;
 
+       /* intialize ITR */
+       if (rxr_count) {
+               /* rx or rx/tx vector */
+               if (!adapter->rx_itr_setting || adapter->rx_itr_setting > 3)
+                       q_vector->itr_val = adapter->rx_itr_setting;
+       } else {
+               /* tx only vector */
+               if (!adapter->tx_itr_setting || adapter->tx_itr_setting > 3)
+                       q_vector->itr_val = adapter->tx_itr_setting;
+       }
+
        if (txr_count) {
                /* assign generic ring traits */
                ring->dev = &adapter->pdev->dev;
@@ -1221,9 +1245,9 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
                        set_bit(IGB_RING_FLAG_RX_SCTP_CSUM, &ring->flags);
 
                /*
-                * On i350, i210, and i211, loopback VLAN packets
+                * On i350, i354, i210, and i211, loopback VLAN packets
                 * have the tag byte-swapped.
-                * */
+                */
                if (adapter->hw.mac.type >= e1000_i350)
                        set_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &ring->flags);
 
@@ -1240,11 +1264,11 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
 
 
 /**
- * igb_alloc_q_vectors - Allocate memory for interrupt vectors
- * @adapter: board private structure to initialize
+ *  igb_alloc_q_vectors - Allocate memory for interrupt vectors
+ *  @adapter: board private structure to initialize
  *
- * We allocate one q_vector per queue interrupt.  If allocation fails we
- * return -ENOMEM.
+ *  We allocate one q_vector per queue interrupt.  If allocation fails we
+ *  return -ENOMEM.
  **/
 static int igb_alloc_q_vectors(struct igb_adapter *adapter)
 {
@@ -1298,9 +1322,11 @@ err_out:
 }
 
 /**
- * igb_init_interrupt_scheme - initialize interrupts, allocate queues/vectors
+ *  igb_init_interrupt_scheme - initialize interrupts, allocate queues/vectors
+ *  @adapter: board private structure to initialize
+ *  @msix: boolean value of MSIX capability
  *
- * This function initializes the interrupts and allocates all of the queues.
+ *  This function initializes the interrupts and allocates all of the queues.
  **/
 static int igb_init_interrupt_scheme(struct igb_adapter *adapter, bool msix)
 {
@@ -1325,10 +1351,11 @@ err_alloc_q_vectors:
 }
 
 /**
- * igb_request_irq - initialize interrupts
+ *  igb_request_irq - initialize interrupts
+ *  @adapter: board private structure to initialize
  *
- * Attempts to configure interrupts using the best available
- * capabilities of the hardware and kernel.
+ *  Attempts to configure interrupts using the best available
+ *  capabilities of the hardware and kernel.
  **/
 static int igb_request_irq(struct igb_adapter *adapter)
 {
@@ -1394,15 +1421,14 @@ static void igb_free_irq(struct igb_adapter *adapter)
 }
 
 /**
- * igb_irq_disable - Mask off interrupt generation on the NIC
- * @adapter: board private structure
+ *  igb_irq_disable - Mask off interrupt generation on the NIC
+ *  @adapter: board private structure
  **/
 static void igb_irq_disable(struct igb_adapter *adapter)
 {
        struct e1000_hw *hw = &adapter->hw;
 
-       /*
-        * we need to be careful when disabling interrupts.  The VFs are also
+       /* we need to be careful when disabling interrupts.  The VFs are also
         * mapped into these registers and so clearing the bits can cause
         * issues on the VF drivers so we only need to clear what we set
         */
@@ -1427,8 +1453,8 @@ static void igb_irq_disable(struct igb_adapter *adapter)
 }
 
 /**
- * igb_irq_enable - Enable default interrupt generation settings
- * @adapter: board private structure
+ *  igb_irq_enable - Enable default interrupt generation settings
+ *  @adapter: board private structure
  **/
 static void igb_irq_enable(struct igb_adapter *adapter)
 {
@@ -1477,13 +1503,12 @@ static void igb_update_mng_vlan(struct igb_adapter *adapter)
 }
 
 /**
- * igb_release_hw_control - release control of the h/w to f/w
- * @adapter: address of board private structure
- *
- * igb_release_hw_control resets CTRL_EXT:DRV_LOAD bit.
- * For ASF and Pass Through versions of f/w this means that the
- * driver is no longer loaded.
+ *  igb_release_hw_control - release control of the h/w to f/w
+ *  @adapter: address of board private structure
  *
+ *  igb_release_hw_control resets CTRL_EXT:DRV_LOAD bit.
+ *  For ASF and Pass Through versions of f/w this means that the
+ *  driver is no longer loaded.
  **/
 static void igb_release_hw_control(struct igb_adapter *adapter)
 {
@@ -1497,13 +1522,12 @@ static void igb_release_hw_control(struct igb_adapter *adapter)
 }
 
 /**
- * igb_get_hw_control - get control of the h/w from f/w
- * @adapter: address of board private structure
- *
- * igb_get_hw_control sets CTRL_EXT:DRV_LOAD bit.
- * For ASF and Pass Through versions of f/w this means that
- * the driver is loaded.
+ *  igb_get_hw_control - get control of the h/w from f/w
+ *  @adapter: address of board private structure
  *
+ *  igb_get_hw_control sets CTRL_EXT:DRV_LOAD bit.
+ *  For ASF and Pass Through versions of f/w this means that
+ *  the driver is loaded.
  **/
 static void igb_get_hw_control(struct igb_adapter *adapter)
 {
@@ -1517,8 +1541,8 @@ static void igb_get_hw_control(struct igb_adapter *adapter)
 }
 
 /**
- * igb_configure - configure the hardware for RX and TX
- * @adapter: private board structure
+ *  igb_configure - configure the hardware for RX and TX
+ *  @adapter: private board structure
  **/
 static void igb_configure(struct igb_adapter *adapter)
 {
@@ -1541,7 +1565,8 @@ static void igb_configure(struct igb_adapter *adapter)
 
        /* call igb_desc_unused which always leaves
         * at least 1 descriptor unused to make sure
-        * next_to_use != next_to_clean */
+        * next_to_use != next_to_clean
+        */
        for (i = 0; i < adapter->num_rx_queues; i++) {
                struct igb_ring *ring = adapter->rx_ring[i];
                igb_alloc_rx_buffers(ring, igb_desc_unused(ring));
@@ -1549,8 +1574,8 @@ static void igb_configure(struct igb_adapter *adapter)
 }
 
 /**
- * igb_power_up_link - Power up the phy/serdes link
- * @adapter: address of board private structure
+ *  igb_power_up_link - Power up the phy/serdes link
+ *  @adapter: address of board private structure
  **/
 void igb_power_up_link(struct igb_adapter *adapter)
 {
@@ -1563,8 +1588,8 @@ void igb_power_up_link(struct igb_adapter *adapter)
 }
 
 /**
- * igb_power_down_link - Power down the phy/serdes link
- * @adapter: address of board private structure
+ *  igb_power_down_link - Power down the phy/serdes link
+ *  @adapter: address of board private structure
  */
 static void igb_power_down_link(struct igb_adapter *adapter)
 {
@@ -1575,8 +1600,8 @@ static void igb_power_down_link(struct igb_adapter *adapter)
 }
 
 /**
- * igb_up - Open the interface and prepare it to handle traffic
- * @adapter: board private structure
+ *  igb_up - Open the interface and prepare it to handle traffic
+ *  @adapter: board private structure
  **/
 int igb_up(struct igb_adapter *adapter)
 {
@@ -1624,7 +1649,8 @@ void igb_down(struct igb_adapter *adapter)
        int i;
 
        /* signal that we're down so the interrupt handler does not
-        * reschedule our watchdog timer */
+        * reschedule our watchdog timer
+        */
        set_bit(__IGB_DOWN, &adapter->state);
 
        /* disable receives in the hardware */
@@ -1694,6 +1720,7 @@ void igb_reset(struct igb_adapter *adapter)
         */
        switch (mac->type) {
        case e1000_i350:
+       case e1000_i354:
        case e1000_82580:
                pba = rd32(E1000_RXPBS);
                pba = igb_rxpbs_adjust_82580(pba);
@@ -1720,14 +1747,16 @@ void igb_reset(struct igb_adapter *adapter)
                 * rounded up to the next 1KB and expressed in KB.  Likewise,
                 * the Rx FIFO should be large enough to accommodate at least
                 * one full receive packet and is similarly rounded up and
-                * expressed in KB. */
+                * expressed in KB.
+                */
                pba = rd32(E1000_PBA);
                /* upper 16 bits has Tx packet buffer allocation size in KB */
                tx_space = pba >> 16;
                /* lower 16 bits has Rx packet buffer allocation size in KB */
                pba &= 0xffff;
-               /* the tx fifo also stores 16 bytes of information about the tx
-                * but don't include ethernet FCS because hardware appends it */
+               /* the Tx fifo also stores 16 bytes of information about the Tx
+                * but don't include ethernet FCS because hardware appends it
+                */
                min_tx_space = (adapter->max_frame_size +
                                sizeof(union e1000_adv_tx_desc) -
                                ETH_FCS_LEN) * 2;
@@ -1740,13 +1769,15 @@ void igb_reset(struct igb_adapter *adapter)
 
                /* If current Tx allocation is less than the min Tx FIFO size,
                 * and the min Tx FIFO size is less than the current Rx FIFO
-                * allocation, take space away from current Rx allocation */
+                * allocation, take space away from current Rx allocation
+                */
                if (tx_space < min_tx_space &&
                    ((min_tx_space - tx_space) < pba)) {
                        pba = pba - (min_tx_space - tx_space);
 
-                       /* if short on rx space, rx wins and must trump tx
-                        * adjustment */
+                       /* if short on Rx space, Rx wins and must trump Tx
+                        * adjustment
+                        */
                        if (pba < min_rx_space)
                                pba = min_rx_space;
                }
@@ -1758,7 +1789,8 @@ void igb_reset(struct igb_adapter *adapter)
         * (or the size used for early receive) above it in the Rx FIFO.
         * Set it to the lower of:
         * - 90% of the Rx FIFO size, or
-        * - the full Rx FIFO size minus one full frame */
+        * - the full Rx FIFO size minus one full frame
+        */
        hwm = min(((pba << 10) * 9 / 10),
                        ((pba << 10) - 2 * adapter->max_frame_size));
 
@@ -1789,8 +1821,7 @@ void igb_reset(struct igb_adapter *adapter)
        if (hw->mac.ops.init_hw(hw))
                dev_err(&pdev->dev, "Hardware Error\n");
 
-       /*
-        * Flow control settings reset on hardware reset, so guarantee flow
+       /* Flow control settings reset on hardware reset, so guarantee flow
         * control is off when forcing speed.
         */
        if (!hw->mac.autoneg)
@@ -1826,14 +1857,13 @@ void igb_reset(struct igb_adapter *adapter)
 static netdev_features_t igb_fix_features(struct net_device *netdev,
        netdev_features_t features)
 {
-       /*
-        * Since there is no support for separate rx/tx vlan accel
-        * enable/disable make sure tx flag is always in same state as rx.
+       /* Since there is no support for separate Rx/Tx vlan accel
+        * enable/disable make sure Tx flag is always in same state as Rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -1844,7 +1874,7 @@ static int igb_set_features(struct net_device *netdev,
        netdev_features_t changed = netdev->features ^ features;
        struct igb_adapter *adapter = netdev_priv(netdev);
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                igb_vlan_mode(netdev, features);
 
        if (!(changed & NETIF_F_RXALL))
@@ -1876,6 +1906,7 @@ static const struct net_device_ops igb_netdev_ops = {
        .ndo_set_vf_mac         = igb_ndo_set_vf_mac,
        .ndo_set_vf_vlan        = igb_ndo_set_vf_vlan,
        .ndo_set_vf_tx_rate     = igb_ndo_set_vf_bw,
+       .ndo_set_vf_spoofchk    = igb_ndo_set_vf_spoofchk,
        .ndo_get_vf_config      = igb_ndo_get_vf_config,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = igb_netpoll,
@@ -1887,7 +1918,6 @@ static const struct net_device_ops igb_netdev_ops = {
 /**
  * igb_set_fw_version - Configure version string for ethtool
  * @adapter: adapter struct
- *
  **/
 void igb_set_fw_version(struct igb_adapter *adapter)
 {
@@ -1923,10 +1953,10 @@ void igb_set_fw_version(struct igb_adapter *adapter)
        return;
 }
 
-/*  igb_init_i2c - Init I2C interface
+/**
+ *  igb_init_i2c - Init I2C interface
  *  @adapter: pointer to adapter structure
- *
- */
+ **/
 static s32 igb_init_i2c(struct igb_adapter *adapter)
 {
        s32 status = E1000_SUCCESS;
@@ -1951,15 +1981,15 @@ static s32 igb_init_i2c(struct igb_adapter *adapter)
 }
 
 /**
- * igb_probe - Device Initialization Routine
- * @pdev: PCI device information struct
- * @ent: entry in igb_pci_tbl
+ *  igb_probe - Device Initialization Routine
+ *  @pdev: PCI device information struct
+ *  @ent: entry in igb_pci_tbl
  *
- * Returns 0 on success, negative on failure
+ *  Returns 0 on success, negative on failure
  *
- * igb_probe initializes an adapter identified by a pci_dev structure.
- * The OS initialization, configuring of the adapter private structure,
- * and a hardware reset occur.
+ *  igb_probe initializes an adapter identified by a pci_dev structure.
+ *  The OS initialization, configuring of the adapter private structure,
+ *  and a hardware reset occur.
  **/
 static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
@@ -1996,18 +2026,19 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        } else {
                err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
                if (err) {
-                       err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+                       err = dma_set_coherent_mask(&pdev->dev,
+                                                   DMA_BIT_MASK(32));
                        if (err) {
-                               dev_err(&pdev->dev, "No usable DMA "
-                                       "configuration, aborting\n");
+                               dev_err(&pdev->dev,
+                                       "No usable DMA configuration, aborting\n");
                                goto err_dma;
                        }
                }
        }
 
        err = pci_request_selected_regions(pdev, pci_select_bars(pdev,
-                                          IORESOURCE_MEM),
-                                          igb_driver_name);
+                                          IORESOURCE_MEM),
+                                          igb_driver_name);
        if (err)
                goto err_pci_reg;
 
@@ -2085,8 +2116,7 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev_info(&pdev->dev,
                        "PHY reset is blocked due to SOL/IDER session.\n");
 
-       /*
-        * features is initialized to 0 in allocation, it might have bits
+       /* features is initialized to 0 in allocation, it might have bits
         * set by igb_sw_init so we should use an or instead of an
         * assignment.
         */
@@ -2097,15 +2127,15 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                            NETIF_F_TSO6 |
                            NETIF_F_RXHASH |
                            NETIF_F_RXCSUM |
-                           NETIF_F_HW_VLAN_RX |
-                           NETIF_F_HW_VLAN_TX;
+                           NETIF_F_HW_VLAN_CTAG_RX |
+                           NETIF_F_HW_VLAN_CTAG_TX;
 
        /* copy netdev features into list of user selectable features */
        netdev->hw_features |= netdev->features;
        netdev->hw_features |= NETIF_F_RXALL;
 
        /* set this bit last since it cannot be part of hw_features */
-       netdev->features |= NETIF_F_HW_VLAN_FILTER;
+       netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        netdev->vlan_features |= NETIF_F_TSO |
                                 NETIF_F_TSO6 |
@@ -2130,11 +2160,11 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        adapter->en_mng_pt = igb_enable_mng_pass_thru(hw);
 
        /* before reading the NVM, reset the controller to put the device in a
-        * known good starting state */
+        * known good starting state
+        */
        hw->mac.ops.reset_hw(hw);
 
-       /*
-        * make sure the NVM is good , i211 parts have special NVM that
+       /* make sure the NVM is good , i211 parts have special NVM that
         * doesn't contain a checksum
         */
        if (hw->mac.type != e1000_i211) {
@@ -2161,9 +2191,9 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        igb_set_fw_version(adapter);
 
        setup_timer(&adapter->watchdog_timer, igb_watchdog,
-                   (unsigned long) adapter);
+                   (unsigned long) adapter);
        setup_timer(&adapter->phy_info_timer, igb_update_phy_info,
-                   (unsigned long) adapter);
+                   (unsigned long) adapter);
 
        INIT_WORK(&adapter->reset_task, igb_reset_task);
        INIT_WORK(&adapter->watchdog_task, igb_watchdog_task);
@@ -2185,8 +2215,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* Check the NVM for wake support on non-port A ports */
        if (hw->mac.type >= e1000_82580)
                hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A +
-                                NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1,
-                                &eeprom_data);
+                                NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1,
+                                &eeprom_data);
        else if (hw->bus.func == 1)
                hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
 
@@ -2195,7 +2225,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        /* now that we have the eeprom settings, apply the special cases where
         * the eeprom may be wrong or the board simply won't support wake on
-        * lan on a particular port */
+        * lan on a particular port
+        */
        switch (pdev->device) {
        case E1000_DEV_ID_82575GB_QUAD_COPPER:
                adapter->flags &= ~IGB_FLAG_WOL_SUPPORTED;
@@ -2204,7 +2235,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        case E1000_DEV_ID_82576_FIBER:
        case E1000_DEV_ID_82576_SERDES:
                /* Wake events only supported on port A for dual fiber
-                * regardless of eeprom setting */
+                * regardless of eeprom setting
+                */
                if (rd32(E1000_STATUS) & E1000_STATUS_FUNC_1)
                        adapter->flags &= ~IGB_FLAG_WOL_SUPPORTED;
                break;
@@ -2274,8 +2306,7 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (hw->mac.type == e1000_i350 && hw->bus.func == 0) {
                u16 ets_word;
 
-               /*
-                * Read the NVM to determine if this i350 device supports an
+               /* Read the NVM to determine if this i350 device supports an
                 * external thermal sensor.
                 */
                hw->nvm.ops.read(hw, NVM_ETS_CFG, 1, &ets_word);
@@ -2294,17 +2325,20 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        igb_ptp_init(adapter);
 
        dev_info(&pdev->dev, "Intel(R) Gigabit Ethernet Network Connection\n");
-       /* print bus type/speed/width info */
-       dev_info(&pdev->dev, "%s: (PCIe:%s:%s) %pM\n",
-                netdev->name,
-                ((hw->bus.speed == e1000_bus_speed_2500) ? "2.5Gb/s" :
-                 (hw->bus.speed == e1000_bus_speed_5000) ? "5.0Gb/s" :
-                                                           "unknown"),
-                ((hw->bus.width == e1000_bus_width_pcie_x4) ? "Width x4" :
-                 (hw->bus.width == e1000_bus_width_pcie_x2) ? "Width x2" :
-                 (hw->bus.width == e1000_bus_width_pcie_x1) ? "Width x1" :
-                  "unknown"),
-                netdev->dev_addr);
+       /* print bus type/speed/width info, not applicable to i354 */
+       if (hw->mac.type != e1000_i354) {
+               dev_info(&pdev->dev, "%s: (PCIe:%s:%s) %pM\n",
+                        netdev->name,
+                        ((hw->bus.speed == e1000_bus_speed_2500) ? "2.5Gb/s" :
+                         (hw->bus.speed == e1000_bus_speed_5000) ? "5.0Gb/s" :
+                          "unknown"),
+                        ((hw->bus.width == e1000_bus_width_pcie_x4) ?
+                         "Width x4" :
+                         (hw->bus.width == e1000_bus_width_pcie_x2) ?
+                         "Width x2" :
+                         (hw->bus.width == e1000_bus_width_pcie_x1) ?
+                         "Width x1" : "unknown"), netdev->dev_addr);
+       }
 
        ret_val = igb_read_part_string(hw, part_str, E1000_PBANUM_LENGTH);
        if (ret_val)
@@ -2321,6 +2355,13 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        case e1000_i211:
                igb_set_eee_i350(hw);
                break;
+       case e1000_i354:
+               if (hw->phy.media_type == e1000_media_type_copper) {
+                       if ((rd32(E1000_CTRL_EXT) &
+                           E1000_CTRL_EXT_LINK_MODE_SGMII))
+                               igb_set_eee_i354(hw);
+               }
+               break;
        default:
                break;
        }
@@ -2344,7 +2385,7 @@ err_ioremap:
        free_netdev(netdev);
 err_alloc_etherdev:
        pci_release_selected_regions(pdev,
-                                    pci_select_bars(pdev, IORESOURCE_MEM));
+                                    pci_select_bars(pdev, IORESOURCE_MEM));
 err_pci_reg:
 err_dma:
        pci_disable_device(pdev);
@@ -2444,26 +2485,24 @@ out:
 }
 
 #endif
-/*
+/**
  *  igb_remove_i2c - Cleanup  I2C interface
  *  @adapter: pointer to adapter structure
- *
- */
+ **/
 static void igb_remove_i2c(struct igb_adapter *adapter)
 {
-
        /* free the adapter bus structure */
        i2c_del_adapter(&adapter->i2c_adap);
 }
 
 /**
- * igb_remove - Device Removal Routine
- * @pdev: PCI device information struct
+ *  igb_remove - Device Removal Routine
+ *  @pdev: PCI device information struct
  *
- * igb_remove is called by the PCI subsystem to alert the driver
- * that it should release a PCI device.  The could be caused by a
- * Hot-Plug event, or because the driver is going to be removed from
- * memory.
+ *  igb_remove is called by the PCI subsystem to alert the driver
+ *  that it should release a PCI device.  The could be caused by a
+ *  Hot-Plug event, or because the driver is going to be removed from
+ *  memory.
  **/
 static void igb_remove(struct pci_dev *pdev)
 {
@@ -2477,8 +2516,7 @@ static void igb_remove(struct pci_dev *pdev)
 #endif
        igb_remove_i2c(adapter);
        igb_ptp_stop(adapter);
-       /*
-        * The watchdog timer may be rescheduled, so explicitly
+       /* The watchdog timer may be rescheduled, so explicitly
         * disable watchdog from being rescheduled.
         */
        set_bit(__IGB_DOWN, &adapter->state);
@@ -2498,7 +2536,8 @@ static void igb_remove(struct pci_dev *pdev)
 #endif
 
        /* Release control of h/w to f/w.  If f/w is AMT enabled, this
-        * would have already happened in close and is redundant. */
+        * would have already happened in close and is redundant.
+        */
        igb_release_hw_control(adapter);
 
        unregister_netdev(netdev);
@@ -2513,7 +2552,7 @@ static void igb_remove(struct pci_dev *pdev)
        if (hw->flash_address)
                iounmap(hw->flash_address);
        pci_release_selected_regions(pdev,
-                                    pci_select_bars(pdev, IORESOURCE_MEM));
+                                    pci_select_bars(pdev, IORESOURCE_MEM));
 
        kfree(adapter->shadow_vfta);
        free_netdev(netdev);
@@ -2524,13 +2563,13 @@ static void igb_remove(struct pci_dev *pdev)
 }
 
 /**
- * igb_probe_vfs - Initialize vf data storage and add VFs to pci config space
- * @adapter: board private structure to initialize
+ *  igb_probe_vfs - Initialize vf data storage and add VFs to pci config space
+ *  @adapter: board private structure to initialize
  *
- * This function initializes the vf specific data storage and then attempts to
- * allocate the VFs.  The reason for ordering it this way is because it is much
- * mor expensive time wise to disable SR-IOV than it is to allocate and free
- * the memory for the VFs.
+ *  This function initializes the vf specific data storage and then attempts to
+ *  allocate the VFs.  The reason for ordering it this way is because it is much
+ *  mor expensive time wise to disable SR-IOV than it is to allocate and free
+ *  the memory for the VFs.
  **/
 static void igb_probe_vfs(struct igb_adapter *adapter)
 {
@@ -2576,6 +2615,7 @@ static void igb_init_queue_configuration(struct igb_adapter *adapter)
                }
                /* fall through */
        case e1000_82580:
+       case e1000_i354:
        default:
                max_rss_queues = IGB_MAX_RX_QUEUES;
                break;
@@ -2590,8 +2630,7 @@ static void igb_init_queue_configuration(struct igb_adapter *adapter)
                /* Device supports enough interrupts without queue pairing. */
                break;
        case e1000_82576:
-               /*
-                * If VFs are going to be allocated with RSS queues then we
+               /* If VFs are going to be allocated with RSS queues then we
                 * should pair the queues in order to conserve interrupts due
                 * to limited supply.
                 */
@@ -2601,10 +2640,10 @@ static void igb_init_queue_configuration(struct igb_adapter *adapter)
                /* fall through */
        case e1000_82580:
        case e1000_i350:
+       case e1000_i354:
        case e1000_i210:
        default:
-               /*
-                * If rss_queues > half of max_rss_queues, pair the queues in
+               /* If rss_queues > half of max_rss_queues, pair the queues in
                 * order to conserve interrupts due to limited supply.
                 */
                if (adapter->rss_queues > (max_rss_queues / 2))
@@ -2614,12 +2653,12 @@ static void igb_init_queue_configuration(struct igb_adapter *adapter)
 }
 
 /**
- * igb_sw_init - Initialize general software structures (struct igb_adapter)
- * @adapter: board private structure to initialize
+ *  igb_sw_init - Initialize general software structures (struct igb_adapter)
+ *  @adapter: board private structure to initialize
  *
- * igb_sw_init initializes the Adapter private data structure.
- * Fields are initialized based on PCI device information and
- * OS network device settings (MTU size).
+ *  igb_sw_init initializes the Adapter private data structure.
+ *  Fields are initialized based on PCI device information and
+ *  OS network device settings (MTU size).
  **/
 static int igb_sw_init(struct igb_adapter *adapter)
 {
@@ -2689,16 +2728,16 @@ static int igb_sw_init(struct igb_adapter *adapter)
 }
 
 /**
- * igb_open - Called when a network interface is made active
- * @netdev: network interface device structure
+ *  igb_open - Called when a network interface is made active
+ *  @netdev: network interface device structure
  *
- * Returns 0 on success, negative value on failure
+ *  Returns 0 on success, negative value on failure
  *
- * The open entry point is called when a network interface is made
- * active by the system (IFF_UP).  At this point all resources needed
- * for transmit and receive operations are allocated, the interrupt
- * handler is registered with the OS, the watchdog timer is started,
- * and the stack is notified that the interface is ready.
+ *  The open entry point is called when a network interface is made
+ *  active by the system (IFF_UP).  At this point all resources needed
+ *  for transmit and receive operations are allocated, the interrupt
+ *  handler is registered with the OS, the watchdog timer is started,
+ *  and the stack is notified that the interface is ready.
  **/
 static int __igb_open(struct net_device *netdev, bool resuming)
 {
@@ -2734,7 +2773,8 @@ static int __igb_open(struct net_device *netdev, bool resuming)
        /* before we allocate an interrupt, we must be ready to handle it.
         * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt
         * as soon as we call pci_request_irq, so we have to setup our
-        * clean_rx handler before we do so.  */
+        * clean_rx handler before we do so.
+        */
        igb_configure(adapter);
 
        err = igb_request_irq(adapter);
@@ -2803,15 +2843,15 @@ static int igb_open(struct net_device *netdev)
 }
 
 /**
- * igb_close - Disables a network interface
- * @netdev: network interface device structure
+ *  igb_close - Disables a network interface
+ *  @netdev: network interface device structure
  *
- * Returns 0, this is not allowed to fail
+ *  Returns 0, this is not allowed to fail
  *
- * The close entry point is called when an interface is de-activated
- * by the OS.  The hardware is still under the driver's control, but
- * needs to be disabled.  A global MAC reset is issued to stop the
- * hardware, and all transmit and receive resources are freed.
+ *  The close entry point is called when an interface is de-activated
+ *  by the OS.  The hardware is still under the driver's control, but
+ *  needs to be disabled.  A global MAC reset is issued to stop the
+ *  hardware, and all transmit and receive resources are freed.
  **/
 static int __igb_close(struct net_device *netdev, bool suspending)
 {
@@ -2840,10 +2880,10 @@ static int igb_close(struct net_device *netdev)
 }
 
 /**
- * igb_setup_tx_resources - allocate Tx resources (Descriptors)
- * @tx_ring: tx descriptor ring (for a specific queue) to setup
+ *  igb_setup_tx_resources - allocate Tx resources (Descriptors)
+ *  @tx_ring: tx descriptor ring (for a specific queue) to setup
  *
- * Return 0 on success, negative on failure
+ *  Return 0 on success, negative on failure
  **/
 int igb_setup_tx_resources(struct igb_ring *tx_ring)
 {
@@ -2878,11 +2918,11 @@ err:
 }
 
 /**
- * igb_setup_all_tx_resources - wrapper to allocate Tx resources
- *                               (Descriptors) for all queues
- * @adapter: board private structure
+ *  igb_setup_all_tx_resources - wrapper to allocate Tx resources
+ *                              (Descriptors) for all queues
+ *  @adapter: board private structure
  *
- * Return 0 on success, negative on failure
+ *  Return 0 on success, negative on failure
  **/
 static int igb_setup_all_tx_resources(struct igb_adapter *adapter)
 {
@@ -2904,8 +2944,8 @@ static int igb_setup_all_tx_resources(struct igb_adapter *adapter)
 }
 
 /**
- * igb_setup_tctl - configure the transmit control registers
- * @adapter: Board private structure
+ *  igb_setup_tctl - configure the transmit control registers
+ *  @adapter: Board private structure
  **/
 void igb_setup_tctl(struct igb_adapter *adapter)
 {
@@ -2930,11 +2970,11 @@ void igb_setup_tctl(struct igb_adapter *adapter)
 }
 
 /**
- * igb_configure_tx_ring - Configure transmit ring after Reset
- * @adapter: board private structure
- * @ring: tx ring to configure
+ *  igb_configure_tx_ring - Configure transmit ring after Reset
+ *  @adapter: board private structure
+ *  @ring: tx ring to configure
  *
- * Configure a transmit ring after a reset.
+ *  Configure a transmit ring after a reset.
  **/
 void igb_configure_tx_ring(struct igb_adapter *adapter,
                            struct igb_ring *ring)
@@ -2950,9 +2990,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter,
        mdelay(10);
 
        wr32(E1000_TDLEN(reg_idx),
-                       ring->count * sizeof(union e1000_adv_tx_desc));
+            ring->count * sizeof(union e1000_adv_tx_desc));
        wr32(E1000_TDBAL(reg_idx),
-                       tdba & 0x00000000ffffffffULL);
+            tdba & 0x00000000ffffffffULL);
        wr32(E1000_TDBAH(reg_idx), tdba >> 32);
 
        ring->tail = hw->hw_addr + E1000_TDT(reg_idx);
@@ -2968,10 +3008,10 @@ void igb_configure_tx_ring(struct igb_adapter *adapter,
 }
 
 /**
- * igb_configure_tx - Configure transmit Unit after Reset
- * @adapter: board private structure
+ *  igb_configure_tx - Configure transmit Unit after Reset
+ *  @adapter: board private structure
  *
- * Configure the Tx unit of the MAC after a reset.
+ *  Configure the Tx unit of the MAC after a reset.
  **/
 static void igb_configure_tx(struct igb_adapter *adapter)
 {
@@ -2982,10 +3022,10 @@ static void igb_configure_tx(struct igb_adapter *adapter)
 }
 
 /**
- * igb_setup_rx_resources - allocate Rx resources (Descriptors)
- * @rx_ring:    rx descriptor ring (for a specific queue) to setup
+ *  igb_setup_rx_resources - allocate Rx resources (Descriptors)
+ *  @rx_ring: Rx descriptor ring (for a specific queue) to setup
  *
- * Returns 0 on success, negative on failure
+ *  Returns 0 on success, negative on failure
  **/
 int igb_setup_rx_resources(struct igb_ring *rx_ring)
 {
@@ -3021,11 +3061,11 @@ err:
 }
 
 /**
- * igb_setup_all_rx_resources - wrapper to allocate Rx resources
- *                               (Descriptors) for all queues
- * @adapter: board private structure
+ *  igb_setup_all_rx_resources - wrapper to allocate Rx resources
+ *                              (Descriptors) for all queues
+ *  @adapter: board private structure
  *
- * Return 0 on success, negative on failure
+ *  Return 0 on success, negative on failure
  **/
 static int igb_setup_all_rx_resources(struct igb_adapter *adapter)
 {
@@ -3047,8 +3087,8 @@ static int igb_setup_all_rx_resources(struct igb_adapter *adapter)
 }
 
 /**
- * igb_setup_mrqc - configure the multiple receive queue control registers
- * @adapter: Board private structure
+ *  igb_setup_mrqc - configure the multiple receive queue control registers
+ *  @adapter: Board private structure
  **/
 static void igb_setup_mrqc(struct igb_adapter *adapter)
 {
@@ -3081,8 +3121,7 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
                break;
        }
 
-       /*
-        * Populate the indirection table 4 entries at a time.  To do this
+       /* Populate the indirection table 4 entries at a time.  To do this
         * we are generating the results for n and n+2 and then interleaving
         * those with the results with n+1 and n+3.
         */
@@ -3098,8 +3137,7 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
                wr32(E1000_RETA(j), reta);
        }
 
-       /*
-        * Disable raw packet checksumming so that RSS hash is placed in
+       /* Disable raw packet checksumming so that RSS hash is placed in
         * descriptor on writeback.  No need to enable TCP/UDP/IP checksum
         * offloads as they are enabled by default
         */
@@ -3129,7 +3167,8 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
 
        /* If VMDq is enabled then we set the appropriate mode for that, else
         * we default to RSS so that an RSS hash is calculated per packet even
-        * if we are only using one queue */
+        * if we are only using one queue
+        */
        if (adapter->vfs_allocated_count) {
                if (hw->mac.type > e1000_82575) {
                        /* Set the default pool for the PF's first queue */
@@ -3154,8 +3193,8 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
 }
 
 /**
- * igb_setup_rctl - configure the receive control registers
- * @adapter: Board private structure
+ *  igb_setup_rctl - configure the receive control registers
+ *  @adapter: Board private structure
  **/
 void igb_setup_rctl(struct igb_adapter *adapter)
 {
@@ -3170,8 +3209,7 @@ void igb_setup_rctl(struct igb_adapter *adapter)
        rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_RDMTS_HALF |
                (hw->mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
 
-       /*
-        * enable stripping of CRC. It's unlikely this will break BMC
+       /* enable stripping of CRC. It's unlikely this will break BMC
         * redirection as it did with e1000. Newer features require
         * that the HW strips the CRC.
         */
@@ -3198,7 +3236,8 @@ void igb_setup_rctl(struct igb_adapter *adapter)
        /* This is useful for sniffing bad packets. */
        if (adapter->netdev->features & NETIF_F_RXALL) {
                /* UPE and MPE will be handled by normal PROMISC logic
-                * in e1000e_set_rx_mode */
+                * in e1000e_set_rx_mode
+                */
                rctl |= (E1000_RCTL_SBP | /* Receive bad packets */
                         E1000_RCTL_BAM | /* RX All Bcast Pkts */
                         E1000_RCTL_PMCF); /* RX All MAC Ctrl Pkts */
@@ -3221,7 +3260,8 @@ static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size,
        u32 vmolr;
 
        /* if it isn't the PF check to see if VFs are enabled and
-        * increase the size to support vlan tags */
+        * increase the size to support vlan tags
+        */
        if (vfn < adapter->vfs_allocated_count &&
            adapter->vf_data[vfn].vlans_enabled)
                size += VLAN_TAG_SIZE;
@@ -3235,10 +3275,10 @@ static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size,
 }
 
 /**
- * igb_rlpml_set - set maximum receive packet size
- * @adapter: board private structure
+ *  igb_rlpml_set - set maximum receive packet size
+ *  @adapter: board private structure
  *
- * Configure maximum receivable packet size.
+ *  Configure maximum receivable packet size.
  **/
 static void igb_rlpml_set(struct igb_adapter *adapter)
 {
@@ -3248,8 +3288,7 @@ static void igb_rlpml_set(struct igb_adapter *adapter)
 
        if (pf_id) {
                igb_set_vf_rlpml(adapter, max_frame_size, pf_id);
-               /*
-                * If we're in VMDQ or SR-IOV mode, then set global RLPML
+               /* If we're in VMDQ or SR-IOV mode, then set global RLPML
                 * to our max jumbo frame size, in case we need to enable
                 * jumbo frames on one of the rings later.
                 * This will not pass over-length frames into the default
@@ -3267,17 +3306,16 @@ static inline void igb_set_vmolr(struct igb_adapter *adapter,
        struct e1000_hw *hw = &adapter->hw;
        u32 vmolr;
 
-       /*
-        * This register exists only on 82576 and newer so if we are older then
+       /* This register exists only on 82576 and newer so if we are older then
         * we should exit and do nothing
         */
        if (hw->mac.type < e1000_82576)
                return;
 
        vmolr = rd32(E1000_VMOLR(vfn));
-       vmolr |= E1000_VMOLR_STRVLAN;      /* Strip vlan tags */
+       vmolr |= E1000_VMOLR_STRVLAN; /* Strip vlan tags */
        if (aupe)
-               vmolr |= E1000_VMOLR_AUPE;        /* Accept untagged packets */
+               vmolr |= E1000_VMOLR_AUPE; /* Accept untagged packets */
        else
                vmolr &= ~(E1000_VMOLR_AUPE); /* Tagged packets ONLY */
 
@@ -3286,25 +3324,24 @@ static inline void igb_set_vmolr(struct igb_adapter *adapter,
 
        if (adapter->rss_queues > 1 && vfn == adapter->vfs_allocated_count)
                vmolr |= E1000_VMOLR_RSSE; /* enable RSS */
-       /*
-        * for VMDq only allow the VFs and pool 0 to accept broadcast and
+       /* for VMDq only allow the VFs and pool 0 to accept broadcast and
         * multicast packets
         */
        if (vfn <= adapter->vfs_allocated_count)
-               vmolr |= E1000_VMOLR_BAM;          /* Accept broadcast */
+               vmolr |= E1000_VMOLR_BAM; /* Accept broadcast */
 
        wr32(E1000_VMOLR(vfn), vmolr);
 }
 
 /**
- * igb_configure_rx_ring - Configure a receive ring after Reset
- * @adapter: board private structure
- * @ring: receive ring to be configured
+ *  igb_configure_rx_ring - Configure a receive ring after Reset
+ *  @adapter: board private structure
+ *  @ring: receive ring to be configured
  *
- * Configure the Rx unit of the MAC after a reset.
+ *  Configure the Rx unit of the MAC after a reset.
  **/
 void igb_configure_rx_ring(struct igb_adapter *adapter,
-                           struct igb_ring *ring)
+                          struct igb_ring *ring)
 {
        struct e1000_hw *hw = &adapter->hw;
        u64 rdba = ring->dma;
@@ -3319,7 +3356,7 @@ void igb_configure_rx_ring(struct igb_adapter *adapter,
             rdba & 0x00000000ffffffffULL);
        wr32(E1000_RDBAH(reg_idx), rdba >> 32);
        wr32(E1000_RDLEN(reg_idx),
-                      ring->count * sizeof(union e1000_adv_rx_desc));
+            ring->count * sizeof(union e1000_adv_rx_desc));
 
        /* initialize head and tail */
        ring->tail = hw->hw_addr + E1000_RDT(reg_idx);
@@ -3351,10 +3388,10 @@ void igb_configure_rx_ring(struct igb_adapter *adapter,
 }
 
 /**
- * igb_configure_rx - Configure receive Unit after Reset
- * @adapter: board private structure
+ *  igb_configure_rx - Configure receive Unit after Reset
+ *  @adapter: board private structure
  *
- * Configure the Rx unit of the MAC after a reset.
+ *  Configure the Rx unit of the MAC after a reset.
  **/
 static void igb_configure_rx(struct igb_adapter *adapter)
 {
@@ -3365,19 +3402,20 @@ static void igb_configure_rx(struct igb_adapter *adapter)
 
        /* set the correct pool for the PF default MAC address in entry 0 */
        igb_rar_set_qsel(adapter, adapter->hw.mac.addr, 0,
-                        adapter->vfs_allocated_count);
+                        adapter->vfs_allocated_count);
 
        /* Setup the HW Rx Head and Tail Descriptor Pointers and
-        * the Base and Length of the Rx Descriptor Ring */
+        * the Base and Length of the Rx Descriptor Ring
+        */
        for (i = 0; i < adapter->num_rx_queues; i++)
                igb_configure_rx_ring(adapter, adapter->rx_ring[i]);
 }
 
 /**
- * igb_free_tx_resources - Free Tx Resources per Queue
- * @tx_ring: Tx descriptor ring for a specific queue
+ *  igb_free_tx_resources - Free Tx Resources per Queue
+ *  @tx_ring: Tx descriptor ring for a specific queue
  *
- * Free all transmit software resources
+ *  Free all transmit software resources
  **/
 void igb_free_tx_resources(struct igb_ring *tx_ring)
 {
@@ -3397,10 +3435,10 @@ void igb_free_tx_resources(struct igb_ring *tx_ring)
 }
 
 /**
- * igb_free_all_tx_resources - Free Tx Resources for All Queues
- * @adapter: board private structure
+ *  igb_free_all_tx_resources - Free Tx Resources for All Queues
+ *  @adapter: board private structure
  *
- * Free all transmit software resources
+ *  Free all transmit software resources
  **/
 static void igb_free_all_tx_resources(struct igb_adapter *adapter)
 {
@@ -3433,8 +3471,8 @@ void igb_unmap_and_free_tx_resource(struct igb_ring *ring,
 }
 
 /**
- * igb_clean_tx_ring - Free Tx Buffers
- * @tx_ring: ring to be cleaned
+ *  igb_clean_tx_ring - Free Tx Buffers
+ *  @tx_ring: ring to be cleaned
  **/
 static void igb_clean_tx_ring(struct igb_ring *tx_ring)
 {
@@ -3464,8 +3502,8 @@ static void igb_clean_tx_ring(struct igb_ring *tx_ring)
 }
 
 /**
- * igb_clean_all_tx_rings - Free Tx Buffers for all queues
- * @adapter: board private structure
+ *  igb_clean_all_tx_rings - Free Tx Buffers for all queues
+ *  @adapter: board private structure
  **/
 static void igb_clean_all_tx_rings(struct igb_adapter *adapter)
 {
@@ -3476,10 +3514,10 @@ static void igb_clean_all_tx_rings(struct igb_adapter *adapter)
 }
 
 /**
- * igb_free_rx_resources - Free Rx Resources
- * @rx_ring: ring to clean the resources from
+ *  igb_free_rx_resources - Free Rx Resources
+ *  @rx_ring: ring to clean the resources from
  *
- * Free all receive software resources
+ *  Free all receive software resources
  **/
 void igb_free_rx_resources(struct igb_ring *rx_ring)
 {
@@ -3499,10 +3537,10 @@ void igb_free_rx_resources(struct igb_ring *rx_ring)
 }
 
 /**
- * igb_free_all_rx_resources - Free Rx Resources for All Queues
- * @adapter: board private structure
+ *  igb_free_all_rx_resources - Free Rx Resources for All Queues
+ *  @adapter: board private structure
  *
- * Free all receive software resources
+ *  Free all receive software resources
  **/
 static void igb_free_all_rx_resources(struct igb_adapter *adapter)
 {
@@ -3513,8 +3551,8 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter)
 }
 
 /**
- * igb_clean_rx_ring - Free Rx Buffers per Queue
- * @rx_ring: ring to free buffers from
+ *  igb_clean_rx_ring - Free Rx Buffers per Queue
+ *  @rx_ring: ring to free buffers from
  **/
 static void igb_clean_rx_ring(struct igb_ring *rx_ring)
 {
@@ -3556,8 +3594,8 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring)
 }
 
 /**
- * igb_clean_all_rx_rings - Free Rx Buffers for all queues
- * @adapter: board private structure
+ *  igb_clean_all_rx_rings - Free Rx Buffers for all queues
+ *  @adapter: board private structure
  **/
 static void igb_clean_all_rx_rings(struct igb_adapter *adapter)
 {
@@ -3568,11 +3606,11 @@ static void igb_clean_all_rx_rings(struct igb_adapter *adapter)
 }
 
 /**
- * igb_set_mac - Change the Ethernet Address of the NIC
- * @netdev: network interface device structure
- * @p: pointer to an address structure
+ *  igb_set_mac - Change the Ethernet Address of the NIC
+ *  @netdev: network interface device structure
+ *  @p: pointer to an address structure
  *
- * Returns 0 on success, negative on failure
+ *  Returns 0 on success, negative on failure
  **/
 static int igb_set_mac(struct net_device *netdev, void *p)
 {
@@ -3588,19 +3626,19 @@ static int igb_set_mac(struct net_device *netdev, void *p)
 
        /* set the correct pool for the new PF MAC address in entry 0 */
        igb_rar_set_qsel(adapter, hw->mac.addr, 0,
-                        adapter->vfs_allocated_count);
+                        adapter->vfs_allocated_count);
 
        return 0;
 }
 
 /**
- * igb_write_mc_addr_list - write multicast addresses to MTA
- * @netdev: network interface device structure
+ *  igb_write_mc_addr_list - write multicast addresses to MTA
+ *  @netdev: network interface device structure
  *
- * Writes multicast address list to the MTA hash table.
- * Returns: -ENOMEM on failure
- *                0 on no addresses written
- *                X on writing X addresses to MTA
+ *  Writes multicast address list to the MTA hash table.
+ *  Returns: -ENOMEM on failure
+ *           0 on no addresses written
+ *           X on writing X addresses to MTA
  **/
 static int igb_write_mc_addr_list(struct net_device *netdev)
 {
@@ -3633,13 +3671,13 @@ static int igb_write_mc_addr_list(struct net_device *netdev)
 }
 
 /**
- * igb_write_uc_addr_list - write unicast addresses to RAR table
- * @netdev: network interface device structure
+ *  igb_write_uc_addr_list - write unicast addresses to RAR table
+ *  @netdev: network interface device structure
  *
- * Writes unicast address list to the RAR table.
- * Returns: -ENOMEM on failure/insufficient address space
- *                0 on no addresses written
- *                X on writing X addresses to the RAR table
+ *  Writes unicast address list to the RAR table.
+ *  Returns: -ENOMEM on failure/insufficient address space
+ *           0 on no addresses written
+ *           X on writing X addresses to the RAR table
  **/
 static int igb_write_uc_addr_list(struct net_device *netdev)
 {
@@ -3660,8 +3698,8 @@ static int igb_write_uc_addr_list(struct net_device *netdev)
                        if (!rar_entries)
                                break;
                        igb_rar_set_qsel(adapter, ha->addr,
-                                        rar_entries--,
-                                        vfn);
+                                        rar_entries--,
+                                        vfn);
                        count++;
                }
        }
@@ -3676,13 +3714,13 @@ static int igb_write_uc_addr_list(struct net_device *netdev)
 }
 
 /**
- * igb_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
- * @netdev: network interface device structure
+ *  igb_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
+ *  @netdev: network interface device structure
  *
- * The set_rx_mode entry point is called whenever the unicast or multicast
- * address lists or the network interface flags are updated.  This routine is
- * responsible for configuring the hardware for proper unicast, multicast,
- * promiscuous mode, and all-multi behavior.
+ *  The set_rx_mode entry point is called whenever the unicast or multicast
+ *  address lists or the network interface flags are updated.  This routine is
+ *  responsible for configuring the hardware for proper unicast, multicast,
+ *  promiscuous mode, and all-multi behavior.
  **/
 static void igb_set_rx_mode(struct net_device *netdev)
 {
@@ -3706,8 +3744,7 @@ static void igb_set_rx_mode(struct net_device *netdev)
                        rctl |= E1000_RCTL_MPE;
                        vmolr |= E1000_VMOLR_MPME;
                } else {
-                       /*
-                        * Write addresses to the MTA, if the attempt fails
+                       /* Write addresses to the MTA, if the attempt fails
                         * then we should just turn on promiscuous mode so
                         * that we can at least receive multicast traffic
                         */
@@ -3719,8 +3756,7 @@ static void igb_set_rx_mode(struct net_device *netdev)
                                vmolr |= E1000_VMOLR_ROMPE;
                        }
                }
-               /*
-                * Write addresses to available RAR registers, if there is not
+               /* Write addresses to available RAR registers, if there is not
                 * sufficient space to store all the addresses then enable
                 * unicast promiscuous mode
                 */
@@ -3733,8 +3769,7 @@ static void igb_set_rx_mode(struct net_device *netdev)
        }
        wr32(E1000_RCTL, rctl);
 
-       /*
-        * In order to support SR-IOV and eventually VMDq it is necessary to set
+       /* In order to support SR-IOV and eventually VMDq it is necessary to set
         * the VMOLR to enable the appropriate modes.  Without this workaround
         * we will have issues with VLAN tag stripping not being done for frames
         * that are only arriving because we are the default pool
@@ -3743,7 +3778,7 @@ static void igb_set_rx_mode(struct net_device *netdev)
                return;
 
        vmolr |= rd32(E1000_VMOLR(vfn)) &
-                ~(E1000_VMOLR_ROPE | E1000_VMOLR_MPME | E1000_VMOLR_ROMPE);
+                ~(E1000_VMOLR_ROPE | E1000_VMOLR_MPME | E1000_VMOLR_ROMPE);
        wr32(E1000_VMOLR(vfn), vmolr);
        igb_restore_vf_multicasts(adapter);
 }
@@ -3788,7 +3823,8 @@ static void igb_spoof_check(struct igb_adapter *adapter)
 }
 
 /* Need to wait a few seconds after link up to get diagnostic information from
- * the phy */
+ * the phy
+ */
 static void igb_update_phy_info(unsigned long data)
 {
        struct igb_adapter *adapter = (struct igb_adapter *) data;
@@ -3796,8 +3832,8 @@ static void igb_update_phy_info(unsigned long data)
 }
 
 /**
- * igb_has_link - check shared code for link and determine up/down
- * @adapter: pointer to driver private info
+ *  igb_has_link - check shared code for link and determine up/down
+ *  @adapter: pointer to driver private info
  **/
 bool igb_has_link(struct igb_adapter *adapter)
 {
@@ -3842,17 +3878,16 @@ static bool igb_thermal_sensor_event(struct e1000_hw *hw, u32 event)
                ctrl_ext = rd32(E1000_CTRL_EXT);
 
                if ((hw->phy.media_type == e1000_media_type_copper) &&
-                   !(ctrl_ext & E1000_CTRL_EXT_LINK_MODE_SGMII)) {
+                   !(ctrl_ext & E1000_CTRL_EXT_LINK_MODE_SGMII))
                        ret = !!(thstat & event);
-               }
        }
 
        return ret;
 }
 
 /**
- * igb_watchdog - Timer Call-back
- * @data: pointer to adapter cast into an unsigned long
+ *  igb_watchdog - Timer Call-back
+ *  @data: pointer to adapter cast into an unsigned long
  **/
 static void igb_watchdog(unsigned long data)
 {
@@ -3864,8 +3899,8 @@ static void igb_watchdog(unsigned long data)
 static void igb_watchdog_task(struct work_struct *work)
 {
        struct igb_adapter *adapter = container_of(work,
-                                                  struct igb_adapter,
-                                                   watchdog_task);
+                                                  struct igb_adapter,
+                                                  watchdog_task);
        struct e1000_hw *hw = &adapter->hw;
        struct net_device *netdev = adapter->netdev;
        u32 link;
@@ -3879,8 +3914,8 @@ static void igb_watchdog_task(struct work_struct *work)
                if (!netif_carrier_ok(netdev)) {
                        u32 ctrl;
                        hw->mac.ops.get_speed_and_duplex(hw,
-                                                        &adapter->link_speed,
-                                                        &adapter->link_duplex);
+                                                        &adapter->link_speed,
+                                                        &adapter->link_duplex);
 
                        ctrl = rd32(E1000_CTRL);
                        /* Links status message must follow this format */
@@ -3963,7 +3998,8 @@ static void igb_watchdog_task(struct work_struct *work)
                        /* We've lost link, so the controller stops DMA,
                         * but we've got queued Tx work that's never going
                         * to get done, so reset controller to flush Tx.
-                        * (Do the reset outside of interrupt context). */
+                        * (Do the reset outside of interrupt context).
+                        */
                        if (igb_desc_unused(tx_ring) + 1 < tx_ring->count) {
                                adapter->tx_timeout_count++;
                                schedule_work(&adapter->reset_task);
@@ -3976,7 +4012,7 @@ static void igb_watchdog_task(struct work_struct *work)
                set_bit(IGB_RING_FLAG_TX_DETECT_HANG, &tx_ring->flags);
        }
 
-       /* Cause software interrupt to ensure rx ring is cleaned */
+       /* Cause software interrupt to ensure Rx ring is cleaned */
        if (adapter->msix_entries) {
                u32 eics = 0;
                for (i = 0; i < adapter->num_q_vectors; i++)
@@ -4003,20 +4039,20 @@ enum latency_range {
 };
 
 /**
- * igb_update_ring_itr - update the dynamic ITR value based on packet size
+ *  igb_update_ring_itr - update the dynamic ITR value based on packet size
+ *  @q_vector: pointer to q_vector
  *
- *      Stores a new ITR value based on strictly on packet size.  This
- *      algorithm is less sophisticated than that used in igb_update_itr,
- *      due to the difficulty of synchronizing statistics across multiple
- *      receive rings.  The divisors and thresholds used by this function
- *      were determined based on theoretical maximum wire speed and testing
- *      data, in order to minimize response time while increasing bulk
- *      throughput.
- *      This functionality is controlled by the InterruptThrottleRate module
- *      parameter (see igb_param.c)
- *      NOTE:  This function is called only when operating in a multiqueue
- *             receive environment.
- * @q_vector: pointer to q_vector
+ *  Stores a new ITR value based on strictly on packet size.  This
+ *  algorithm is less sophisticated than that used in igb_update_itr,
+ *  due to the difficulty of synchronizing statistics across multiple
+ *  receive rings.  The divisors and thresholds used by this function
+ *  were determined based on theoretical maximum wire speed and testing
+ *  data, in order to minimize response time while increasing bulk
+ *  throughput.
+ *  This functionality is controlled by the InterruptThrottleRate module
+ *  parameter (see igb_param.c)
+ *  NOTE:  This function is called only when operating in a multiqueue
+ *         receive environment.
  **/
 static void igb_update_ring_itr(struct igb_q_vector *q_vector)
 {
@@ -4077,20 +4113,21 @@ clear_counts:
 }
 
 /**
- * igb_update_itr - update the dynamic ITR value based on statistics
- *      Stores a new ITR value based on packets and byte
- *      counts during the last interrupt.  The advantage of per interrupt
- *      computation is faster updates and more accurate ITR for the current
- *      traffic pattern.  Constants in this function were computed
- *      based on theoretical maximum wire speed and thresholds were set based
- *      on testing data as well as attempting to minimize response time
- *      while increasing bulk throughput.
- *      this functionality is controlled by the InterruptThrottleRate module
- *      parameter (see igb_param.c)
- *      NOTE:  These calculations are only valid when operating in a single-
- *             queue environment.
- * @q_vector: pointer to q_vector
- * @ring_container: ring info to update the itr for
+ *  igb_update_itr - update the dynamic ITR value based on statistics
+ *  @q_vector: pointer to q_vector
+ *  @ring_container: ring info to update the itr for
+ *
+ *  Stores a new ITR value based on packets and byte
+ *  counts during the last interrupt.  The advantage of per interrupt
+ *  computation is faster updates and more accurate ITR for the current
+ *  traffic pattern.  Constants in this function were computed
+ *  based on theoretical maximum wire speed and thresholds were set based
+ *  on testing data as well as attempting to minimize response time
+ *  while increasing bulk throughput.
+ *  this functionality is controlled by the InterruptThrottleRate module
+ *  parameter (see igb_param.c)
+ *  NOTE:  These calculations are only valid when operating in a single-
+ *         queue environment.
  **/
 static void igb_update_itr(struct igb_q_vector *q_vector,
                           struct igb_ring_container *ring_container)
@@ -4188,12 +4225,12 @@ set_itr_now:
        if (new_itr != q_vector->itr_val) {
                /* this attempts to bias the interrupt rate towards Bulk
                 * by adding intermediate steps when interrupt rate is
-                * increasing */
+                * increasing
+                */
                new_itr = new_itr > q_vector->itr_val ?
-                            max((new_itr * q_vector->itr_val) /
-                                (new_itr + (q_vector->itr_val >> 2)),
-                                new_itr) :
-                            new_itr;
+                         max((new_itr * q_vector->itr_val) /
+                         (new_itr + (q_vector->itr_val >> 2)),
+                         new_itr) : new_itr;
                /* Don't write the value here; it resets the adapter's
                 * internal timer, and causes us to delay far longer than
                 * we should between interrupts.  Instead, we write the ITR
@@ -4320,8 +4357,8 @@ static void igb_tx_csum(struct igb_ring *tx_ring, struct igb_tx_buffer *first)
                default:
                        if (unlikely(net_ratelimit())) {
                                dev_warn(tx_ring->dev,
-                                "partial checksum but proto=%x!\n",
-                                first->protocol);
+                                        "partial checksum but proto=%x!\n",
+                                        first->protocol);
                        }
                        break;
                }
@@ -4344,8 +4381,8 @@ static void igb_tx_csum(struct igb_ring *tx_ring, struct igb_tx_buffer *first)
                default:
                        if (unlikely(net_ratelimit())) {
                                dev_warn(tx_ring->dev,
-                                "partial checksum but l4 proto=%x!\n",
-                                l4_hdr);
+                                        "partial checksum but l4 proto=%x!\n",
+                                        l4_hdr);
                        }
                        break;
                }
@@ -4497,8 +4534,7 @@ static void igb_tx_map(struct igb_ring *tx_ring,
        /* set the timestamp */
        first->time_stamp = jiffies;
 
-       /*
-        * Force memory writes to complete before letting h/w know there
+       /* 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).
         *
@@ -4519,7 +4555,8 @@ static void igb_tx_map(struct igb_ring *tx_ring,
        writel(i, tx_ring->tail);
 
        /* we need this if more than one processor can write to our tail
-        * at a time, it syncronizes IO on IA64/Altix systems */
+        * at a time, it synchronizes IO on IA64/Altix systems
+        */
        mmiowb();
 
        return;
@@ -4549,11 +4586,13 @@ static int __igb_maybe_stop_tx(struct igb_ring *tx_ring, const u16 size)
 
        /* Herbert's original patch had:
         *  smp_mb__after_netif_stop_queue();
-        * but since that doesn't exist yet, just open code it. */
+        * but since that doesn't exist yet, just open code it.
+        */
        smp_mb();
 
        /* We need to check again in a case another CPU has just
-        * made room available. */
+        * made room available.
+        */
        if (igb_desc_unused(tx_ring) < size)
                return -EBUSY;
 
@@ -4577,7 +4616,6 @@ static inline int igb_maybe_stop_tx(struct igb_ring *tx_ring, const u16 size)
 netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
                                struct igb_ring *tx_ring)
 {
-       struct igb_adapter *adapter = netdev_priv(tx_ring->netdev);
        struct igb_tx_buffer *first;
        int tso;
        u32 tx_flags = 0;
@@ -4612,15 +4650,18 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
 
        skb_tx_timestamp(skb);
 
-       if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
-                    !(adapter->ptp_tx_skb))) {
-               skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
-               tx_flags |= IGB_TX_FLAGS_TSTAMP;
+       if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+               struct igb_adapter *adapter = netdev_priv(tx_ring->netdev);
 
-               adapter->ptp_tx_skb = skb_get(skb);
-               adapter->ptp_tx_start = jiffies;
-               if (adapter->hw.mac.type == e1000_82576)
-                       schedule_work(&adapter->ptp_tx_work);
+               if (!(adapter->ptp_tx_skb)) {
+                       skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+                       tx_flags |= IGB_TX_FLAGS_TSTAMP;
+
+                       adapter->ptp_tx_skb = skb_get(skb);
+                       adapter->ptp_tx_start = jiffies;
+                       if (adapter->hw.mac.type == e1000_82576)
+                               schedule_work(&adapter->ptp_tx_work);
+               }
        }
 
        if (vlan_tx_tag_present(skb)) {
@@ -4677,8 +4718,7 @@ static netdev_tx_t igb_xmit_frame(struct sk_buff *skb,
                return NETDEV_TX_OK;
        }
 
-       /*
-        * The minimum packet size with TCTL.PSP set is 17 so pad the skb
+       /* The minimum packet size with TCTL.PSP set is 17 so pad the skb
         * in order to meet this minimum size requirement.
         */
        if (unlikely(skb->len < 17)) {
@@ -4692,8 +4732,8 @@ static netdev_tx_t igb_xmit_frame(struct sk_buff *skb,
 }
 
 /**
- * igb_tx_timeout - Respond to a Tx Hang
- * @netdev: network interface device structure
+ *  igb_tx_timeout - Respond to a Tx Hang
+ *  @netdev: network interface device structure
  **/
 static void igb_tx_timeout(struct net_device *netdev)
 {
@@ -4722,13 +4762,12 @@ static void igb_reset_task(struct work_struct *work)
 }
 
 /**
- * igb_get_stats64 - Get System Network Statistics
- * @netdev: network interface device structure
- * @stats: rtnl_link_stats64 pointer
- *
+ *  igb_get_stats64 - Get System Network Statistics
+ *  @netdev: network interface device structure
+ *  @stats: rtnl_link_stats64 pointer
  **/
 static struct rtnl_link_stats64 *igb_get_stats64(struct net_device *netdev,
-                                                struct rtnl_link_stats64 *stats)
+                                               struct rtnl_link_stats64 *stats)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
 
@@ -4741,11 +4780,11 @@ static struct rtnl_link_stats64 *igb_get_stats64(struct net_device *netdev,
 }
 
 /**
- * igb_change_mtu - Change the Maximum Transfer Unit
- * @netdev: network interface device structure
- * @new_mtu: new value for maximum frame size
+ *  igb_change_mtu - Change the Maximum Transfer Unit
+ *  @netdev: network interface device structure
+ *  @new_mtu: new value for maximum frame size
  *
- * Returns 0 on success, negative on failure
+ *  Returns 0 on success, negative on failure
  **/
 static int igb_change_mtu(struct net_device *netdev, int new_mtu)
 {
@@ -4788,10 +4827,9 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
 }
 
 /**
- * igb_update_stats - Update the board statistics counters
- * @adapter: board private structure
+ *  igb_update_stats - Update the board statistics counters
+ *  @adapter: board private structure
  **/
-
 void igb_update_stats(struct igb_adapter *adapter,
                      struct rtnl_link_stats64 *net_stats)
 {
@@ -4806,8 +4844,7 @@ void igb_update_stats(struct igb_adapter *adapter,
 
 #define PHY_IDLE_ERROR_COUNT_MASK 0x00FF
 
-       /*
-        * Prevent stats update while adapter is being reset, or if the pci
+       /* Prevent stats update while adapter is being reset, or if the pci
         * connection is down.
         */
        if (adapter->link_speed == 0)
@@ -4941,7 +4978,8 @@ void igb_update_stats(struct igb_adapter *adapter,
        /* Rx Errors */
 
        /* RLEC on some newer hardware can be incorrect so build
-        * our own version based on RUC and ROC */
+        * our own version based on RUC and ROC
+        */
        net_stats->rx_errors = adapter->stats.rxerrc +
                adapter->stats.crcerrs + adapter->stats.algnerrc +
                adapter->stats.ruc + adapter->stats.roc +
@@ -5000,7 +5038,8 @@ static irqreturn_t igb_msix_other(int irq, void *data)
                adapter->stats.doosync++;
                /* The DMA Out of Sync is also indication of a spoof event
                 * in IOV mode. Check the Wrong VM Behavior register to
-                * see if it is really a spoof event. */
+                * see if it is really a spoof event.
+                */
                igb_check_wvbr(adapter);
        }
 
@@ -5074,8 +5113,7 @@ static void igb_update_tx_dca(struct igb_adapter *adapter,
        if (hw->mac.type != e1000_82575)
                txctrl <<= E1000_DCA_TXCTRL_CPUID_SHIFT;
 
-       /*
-        * We can enable relaxed ordering for reads, but not writes when
+       /* We can enable relaxed ordering for reads, but not writes when
         * DCA is enabled.  This is due to a known issue in some chipsets
         * which will cause the DCA tag to be cleared.
         */
@@ -5096,8 +5134,7 @@ static void igb_update_rx_dca(struct igb_adapter *adapter,
        if (hw->mac.type != e1000_82575)
                rxctrl <<= E1000_DCA_RXCTRL_CPUID_SHIFT;
 
-       /*
-        * We can enable relaxed ordering for reads, but not writes when
+       /* We can enable relaxed ordering for reads, but not writes when
         * DCA is enabled.  This is due to a known issue in some chipsets
         * which will cause the DCA tag to be cleared.
         */
@@ -5166,7 +5203,8 @@ static int __igb_notify_dca(struct device *dev, void *data)
        case DCA_PROVIDER_REMOVE:
                if (adapter->flags & IGB_FLAG_DCA_ENABLED) {
                        /* without this a class_device is left
-                        * hanging around in the sysfs model */
+                        * hanging around in the sysfs model
+                        */
                        dca_remove_requester(dev);
                        dev_info(&pdev->dev, "DCA disabled\n");
                        adapter->flags &= ~IGB_FLAG_DCA_ENABLED;
@@ -5179,12 +5217,12 @@ static int __igb_notify_dca(struct device *dev, void *data)
 }
 
 static int igb_notify_dca(struct notifier_block *nb, unsigned long event,
-                          void *p)
+                         void *p)
 {
        int ret_val;
 
        ret_val = driver_for_each_device(&igb_driver.driver, NULL, &event,
-                                        __igb_notify_dca);
+                                        __igb_notify_dca);
 
        return ret_val ? NOTIFY_BAD : NOTIFY_DONE;
 }
@@ -5198,6 +5236,9 @@ static int igb_vf_configure(struct igb_adapter *adapter, int vf)
        eth_zero_addr(mac_addr);
        igb_set_vf_mac(adapter, vf, mac_addr);
 
+       /* By default spoof check is enabled for all VFs */
+       adapter->vf_data[vf].spoofchk_enabled = true;
+
        return 0;
 }
 
@@ -5256,7 +5297,7 @@ static int igb_set_vf_promisc(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
        struct vf_data_storage *vf_data = &adapter->vf_data[vf];
 
        vf_data->flags &= ~(IGB_VF_FLAG_UNI_PROMISC |
-                           IGB_VF_FLAG_MULTI_PROMISC);
+                           IGB_VF_FLAG_MULTI_PROMISC);
        vmolr &= ~(E1000_VMOLR_ROPE | E1000_VMOLR_ROMPE | E1000_VMOLR_MPME);
 
        if (*msgbuf & E1000_VF_SET_PROMISC_MULTICAST) {
@@ -5264,8 +5305,7 @@ static int igb_set_vf_promisc(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
                vf_data->flags |= IGB_VF_FLAG_MULTI_PROMISC;
                *msgbuf &= ~E1000_VF_SET_PROMISC_MULTICAST;
        } else {
-               /*
-                * if we have hashes and we are clearing a multicast promisc
+               /* if we have hashes and we are clearing a multicast promisc
                 * flag we need to write the hashes to the MTA as this step
                 * was previously skipped
                 */
@@ -5286,7 +5326,6 @@ static int igb_set_vf_promisc(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
                return -EINVAL;
 
        return 0;
-
 }
 
 static int igb_set_vf_multicasts(struct igb_adapter *adapter,
@@ -5493,22 +5532,20 @@ static int igb_ndo_set_vf_vlan(struct net_device *netdev,
                         "Setting VLAN %d, QOS 0x%x on VF %d\n", vlan, qos, vf);
                if (test_bit(__IGB_DOWN, &adapter->state)) {
                        dev_warn(&adapter->pdev->dev,
-                                "The VF VLAN has been set,"
-                                " but the PF device is not up.\n");
+                                "The VF VLAN has been set, but the PF device is not up.\n");
                        dev_warn(&adapter->pdev->dev,
-                                "Bring the PF device up before"
-                                " attempting to use the VF device.\n");
+                                "Bring the PF device up before attempting to use the VF device.\n");
                }
        } else {
                igb_vlvf_set(adapter, adapter->vf_data[vf].pf_vlan,
-                                  false, vf);
+                            false, vf);
                igb_set_vmvir(adapter, vlan, vf);
                igb_set_vmolr(adapter, vf, true);
                adapter->vf_data[vf].pf_vlan = 0;
                adapter->vf_data[vf].pf_qos = 0;
-       }
+       }
 out:
-       return err;
+       return err;
 }
 
 static int igb_set_vf_vlan(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
@@ -5586,8 +5623,7 @@ static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf)
 
 static int igb_set_vf_mac_addr(struct igb_adapter *adapter, u32 *msg, int vf)
 {
-       /*
-        * The VF MAC Address is stored in a packed array of bytes
+       /* The VF MAC Address is stored in a packed array of bytes
         * starting at the second 32 bit word of the msg array
         */
        unsigned char *addr = (char *)&msg[1];
@@ -5636,11 +5672,9 @@ static void igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
        if (msgbuf[0] & (E1000_VT_MSGTYPE_ACK | E1000_VT_MSGTYPE_NACK))
                return;
 
-       /*
-        * until the vf completes a reset it should not be
+       /* until the vf completes a reset it should not be
         * allowed to start any configuration.
         */
-
        if (msgbuf[0] == E1000_VF_RESET) {
                igb_vf_reset_msg(adapter, vf);
                return;
@@ -5660,9 +5694,8 @@ static void igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
                        retval = igb_set_vf_mac_addr(adapter, msgbuf, vf);
                else
                        dev_warn(&pdev->dev,
-                                "VF %d attempted to override administratively "
-                                "set MAC address\nReload the VF driver to "
-                                "resume operations\n", vf);
+                                "VF %d attempted to override administratively set MAC address\nReload the VF driver to resume operations\n",
+                                vf);
                break;
        case E1000_VF_SET_PROMISC:
                retval = igb_set_vf_promisc(adapter, msgbuf, vf);
@@ -5677,9 +5710,8 @@ static void igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
                retval = -1;
                if (vf_data->pf_vlan)
                        dev_warn(&pdev->dev,
-                                "VF %d attempted to override administratively "
-                                "set VLAN tag\nReload the VF driver to "
-                                "resume operations\n", vf);
+                                "VF %d attempted to override administratively set VLAN tag\nReload the VF driver to resume operations\n",
+                                vf);
                else
                        retval = igb_set_vf_vlan(adapter, msgbuf, vf);
                break;
@@ -5748,9 +5780,9 @@ static void igb_set_uta(struct igb_adapter *adapter)
 }
 
 /**
- * igb_intr_msi - Interrupt Handler
- * @irq: interrupt number
- * @data: pointer to a network interface device structure
+ *  igb_intr_msi - Interrupt Handler
+ *  @irq: interrupt number
+ *  @data: pointer to a network interface device structure
  **/
 static irqreturn_t igb_intr_msi(int irq, void *data)
 {
@@ -5793,9 +5825,9 @@ static irqreturn_t igb_intr_msi(int irq, void *data)
 }
 
 /**
- * igb_intr - Legacy Interrupt Handler
- * @irq: interrupt number
- * @data: pointer to a network interface device structure
+ *  igb_intr - Legacy Interrupt Handler
+ *  @irq: interrupt number
+ *  @data: pointer to a network interface device structure
  **/
 static irqreturn_t igb_intr(int irq, void *data)
 {
@@ -5803,11 +5835,13 @@ static irqreturn_t igb_intr(int irq, void *data)
        struct igb_q_vector *q_vector = adapter->q_vector[0];
        struct e1000_hw *hw = &adapter->hw;
        /* Interrupt Auto-Mask...upon reading ICR, interrupts are masked.  No
-        * need for the IMC write */
+        * need for the IMC write
+        */
        u32 icr = rd32(E1000_ICR);
 
        /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is
-        * not set, then the adapter didn't send an interrupt */
+        * not set, then the adapter didn't send an interrupt
+        */
        if (!(icr & E1000_ICR_INT_ASSERTED))
                return IRQ_NONE;
 
@@ -5866,15 +5900,15 @@ static void igb_ring_irq_enable(struct igb_q_vector *q_vector)
 }
 
 /**
- * igb_poll - NAPI Rx polling callback
- * @napi: napi polling structure
- * @budget: count of how many packets we should handle
+ *  igb_poll - NAPI Rx polling callback
+ *  @napi: napi polling structure
+ *  @budget: count of how many packets we should handle
  **/
 static int igb_poll(struct napi_struct *napi, int budget)
 {
        struct igb_q_vector *q_vector = container_of(napi,
-                                                    struct igb_q_vector,
-                                                    napi);
+                                                    struct igb_q_vector,
+                                                    napi);
        bool clean_complete = true;
 
 #ifdef CONFIG_IGB_DCA
@@ -5899,10 +5933,10 @@ static int igb_poll(struct napi_struct *napi, int budget)
 }
 
 /**
- * igb_clean_tx_irq - Reclaim resources after transmit completes
- * @q_vector: pointer to q_vector containing needed info
+ *  igb_clean_tx_irq - Reclaim resources after transmit completes
+ *  @q_vector: pointer to q_vector containing needed info
  *
- * returns true if ring is completely cleaned
+ *  returns true if ring is completely cleaned
  **/
 static bool igb_clean_tx_irq(struct igb_q_vector *q_vector)
 {
@@ -6008,7 +6042,8 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector)
                struct e1000_hw *hw = &adapter->hw;
 
                /* Detect a transmit hang in hardware, this serializes the
-                * check with the clearing of time_stamp and movement of i */
+                * check with the clearing of time_stamp and movement of i
+                */
                clear_bit(IGB_RING_FLAG_TX_DETECT_HANG, &tx_ring->flags);
                if (tx_buffer->next_to_watch &&
                    time_after(jiffies, tx_buffer->time_stamp +
@@ -6047,8 +6082,8 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector)
 
 #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
        if (unlikely(total_packets &&
-                    netif_carrier_ok(tx_ring->netdev) &&
-                    igb_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD)) {
+           netif_carrier_ok(tx_ring->netdev) &&
+           igb_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD)) {
                /* Make sure that anybody stopping the queue after this
                 * sees the new next_to_clean.
                 */
@@ -6069,11 +6104,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector)
 }
 
 /**
- * igb_reuse_rx_page - page flip buffer and store it back on the ring
- * @rx_ring: rx descriptor ring to store buffers on
- * @old_buff: donor buffer to have page reused
+ *  igb_reuse_rx_page - page flip buffer and store it back on the ring
+ *  @rx_ring: rx descriptor ring to store buffers on
+ *  @old_buff: donor buffer to have page reused
  *
- * Synchronizes page for reuse by the adapter
+ *  Synchronizes page for reuse by the adapter
  **/
 static void igb_reuse_rx_page(struct igb_ring *rx_ring,
                              struct igb_rx_buffer *old_buff)
@@ -6133,19 +6168,19 @@ static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer,
 }
 
 /**
- * igb_add_rx_frag - Add contents of Rx buffer to sk_buff
- * @rx_ring: rx descriptor ring to transact packets on
- * @rx_buffer: buffer containing page to add
- * @rx_desc: descriptor containing length of buffer written by hardware
- * @skb: sk_buff to place the data into
+ *  igb_add_rx_frag - Add contents of Rx buffer to sk_buff
+ *  @rx_ring: rx descriptor ring to transact packets on
+ *  @rx_buffer: buffer containing page to add
+ *  @rx_desc: descriptor containing length of buffer written by hardware
+ *  @skb: sk_buff to place the data into
  *
- * This function will add the data contained in rx_buffer->page to the skb.
- * This is done either through a direct copy if the data in the buffer is
- * less than the skb header size, otherwise it will just attach the page as
- * a frag to the skb.
+ *  This function will add the data contained in rx_buffer->page to the skb.
+ *  This is done either through a direct copy if the data in the buffer is
+ *  less than the skb header size, otherwise it will just attach the page as
+ *  a frag to the skb.
  *
- * The function will then update the page offset if necessary and return
- * true if the buffer can be reused by the adapter.
+ *  The function will then update the page offset if necessary and return
+ *  true if the buffer can be reused by the adapter.
  **/
 static bool igb_add_rx_frag(struct igb_ring *rx_ring,
                            struct igb_rx_buffer *rx_buffer,
@@ -6216,8 +6251,7 @@ static struct sk_buff *igb_fetch_rx_buffer(struct igb_ring *rx_ring,
                        return NULL;
                }
 
-               /*
-                * we will be copying header into skb->data in
+               /* we will be copying header into skb->data in
                 * pskb_may_pull so it is in our interest to prefetch
                 * it now to avoid a possible cache miss
                 */
@@ -6265,8 +6299,7 @@ static inline void igb_rx_checksum(struct igb_ring *ring,
        if (igb_test_staterr(rx_desc,
                             E1000_RXDEXT_STATERR_TCPE |
                             E1000_RXDEXT_STATERR_IPE)) {
-               /*
-                * work around errata with sctp packets where the TCPE aka
+               /* work around errata with sctp packets where the TCPE aka
                 * L4E bit is set incorrectly on 64 byte (60 byte w/o crc)
                 * packets, (aka let the stack check the crc32c)
                 */
@@ -6297,15 +6330,15 @@ static inline void igb_rx_hash(struct igb_ring *ring,
 }
 
 /**
- * igb_is_non_eop - process handling of non-EOP buffers
- * @rx_ring: Rx ring being processed
- * @rx_desc: Rx descriptor for current buffer
- * @skb: current socket buffer containing buffer in progress
+ *  igb_is_non_eop - process handling of non-EOP buffers
+ *  @rx_ring: Rx ring being processed
+ *  @rx_desc: Rx descriptor for current buffer
+ *  @skb: current socket buffer containing buffer in progress
  *
- * This function updates next to clean.  If the buffer is an EOP buffer
- * this function exits returning false, otherwise it will place the
- * sk_buff in the next buffer to be chained and return true indicating
- * that this is in fact a non-EOP buffer.
+ *  This function updates next to clean.  If the buffer is an EOP buffer
+ *  this function exits returning false, otherwise it will place the
+ *  sk_buff in the next buffer to be chained and return true indicating
+ *  that this is in fact a non-EOP buffer.
  **/
 static bool igb_is_non_eop(struct igb_ring *rx_ring,
                           union e1000_adv_rx_desc *rx_desc)
@@ -6325,15 +6358,15 @@ static bool igb_is_non_eop(struct igb_ring *rx_ring,
 }
 
 /**
- * igb_get_headlen - determine size of header for LRO/GRO
- * @data: pointer to the start of the headers
- * @max_len: total length of section to find headers in
+ *  igb_get_headlen - determine size of header for LRO/GRO
+ *  @data: pointer to the start of the headers
+ *  @max_len: total length of section to find headers in
  *
- * This function is meant to determine the length of headers that will
- * be recognized by hardware for LRO, and GRO offloads.  The main
- * motivation of doing this is to only perform one pull for IPv4 TCP
- * packets so that we can do basic things like calculating the gso_size
- * based on the average data per packet.
+ *  This function is meant to determine the length of headers that will
+ *  be recognized by hardware for LRO, and GRO offloads.  The main
+ *  motivation of doing this is to only perform one pull for IPv4 TCP
+ *  packets so that we can do basic things like calculating the gso_size
+ *  based on the average data per packet.
  **/
 static unsigned int igb_get_headlen(unsigned char *data,
                                    unsigned int max_len)
@@ -6384,7 +6417,7 @@ static unsigned int igb_get_headlen(unsigned char *data,
                        return hdr.network - data;
 
                /* record next protocol if header is present */
-               if (!hdr.ipv4->frag_off)
+               if (!(hdr.ipv4->frag_off & htons(IP_OFFSET)))
                        nexthdr = hdr.ipv4->protocol;
        } else if (protocol == __constant_htons(ETH_P_IPV6)) {
                if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr)))
@@ -6420,8 +6453,7 @@ static unsigned int igb_get_headlen(unsigned char *data,
                hdr.network += sizeof(struct udphdr);
        }
 
-       /*
-        * If everything has gone correctly hdr.network should be the
+       /* If everything has gone correctly hdr.network should be the
         * data section of the packet and will be the end of the header.
         * If not then it probably represents the end of the last recognized
         * header.
@@ -6433,17 +6465,17 @@ static unsigned int igb_get_headlen(unsigned char *data,
 }
 
 /**
- * igb_pull_tail - igb specific version of skb_pull_tail
- * @rx_ring: rx descriptor ring packet is being transacted on
- * @rx_desc: pointer to the EOP Rx descriptor
- * @skb: pointer to current skb being adjusted
+ *  igb_pull_tail - igb specific version of skb_pull_tail
+ *  @rx_ring: rx descriptor ring packet is being transacted on
+ *  @rx_desc: pointer to the EOP Rx descriptor
+ *  @skb: pointer to current skb being adjusted
  *
- * This function is an igb specific version of __pskb_pull_tail.  The
- * main difference between this version and the original function is that
- * this function can make several assumptions about the state of things
- * that allow for significant optimizations versus the standard function.
- * As a result we can do things like drop a frag and maintain an accurate
- * truesize for the skb.
+ *  This function is an igb specific version of __pskb_pull_tail.  The
+ *  main difference between this version and the original function is that
+ *  this function can make several assumptions about the state of things
+ *  that allow for significant optimizations versus the standard function.
+ *  As a result we can do things like drop a frag and maintain an accurate
+ *  truesize for the skb.
  */
 static void igb_pull_tail(struct igb_ring *rx_ring,
                          union e1000_adv_rx_desc *rx_desc,
@@ -6453,8 +6485,7 @@ static void igb_pull_tail(struct igb_ring *rx_ring,
        unsigned char *va;
        unsigned int pull_len;
 
-       /*
-        * it is valid to use page_address instead of kmap since we are
+       /* it is valid to use page_address instead of kmap since we are
         * working with pages allocated out of the lomem pool per
         * alloc_page(GFP_ATOMIC)
         */
@@ -6474,8 +6505,7 @@ static void igb_pull_tail(struct igb_ring *rx_ring,
                va += IGB_TS_HDR_LEN;
        }
 
-       /*
-        * we need the header to contain the greater of either ETH_HLEN or
+       /* we need the header to contain the greater of either ETH_HLEN or
         * 60 bytes if the skb->len is less than 60 for skb_pad.
         */
        pull_len = igb_get_headlen(va, IGB_RX_HDR_LEN);
@@ -6491,24 +6521,23 @@ static void igb_pull_tail(struct igb_ring *rx_ring,
 }
 
 /**
- * igb_cleanup_headers - Correct corrupted or empty headers
- * @rx_ring: rx descriptor ring packet is being transacted on
- * @rx_desc: pointer to the EOP Rx descriptor
- * @skb: pointer to current skb being fixed
+ *  igb_cleanup_headers - Correct corrupted or empty headers
+ *  @rx_ring: rx descriptor ring packet is being transacted on
+ *  @rx_desc: pointer to the EOP Rx descriptor
+ *  @skb: pointer to current skb being fixed
  *
- * Address the case where we are pulling data in on pages only
- * and as such no data is present in the skb header.
+ *  Address the case where we are pulling data in on pages only
+ *  and as such no data is present in the skb header.
  *
- * In addition if skb is not at least 60 bytes we need to pad it so that
- * it is large enough to qualify as a valid Ethernet frame.
+ *  In addition if skb is not at least 60 bytes we need to pad it so that
+ *  it is large enough to qualify as a valid Ethernet frame.
  *
- * Returns true if an error was encountered and skb was freed.
+ *  Returns true if an error was encountered and skb was freed.
  **/
 static bool igb_cleanup_headers(struct igb_ring *rx_ring,
                                union e1000_adv_rx_desc *rx_desc,
                                struct sk_buff *skb)
 {
-
        if (unlikely((igb_test_staterr(rx_desc,
                                       E1000_RXDEXT_ERR_FRAME_ERR_MASK)))) {
                struct net_device *netdev = rx_ring->netdev;
@@ -6535,14 +6564,14 @@ static bool igb_cleanup_headers(struct igb_ring *rx_ring,
 }
 
 /**
- * igb_process_skb_fields - Populate skb header fields from Rx descriptor
- * @rx_ring: rx descriptor ring packet is being transacted on
- * @rx_desc: pointer to the EOP Rx descriptor
- * @skb: pointer to current skb being populated
+ *  igb_process_skb_fields - Populate skb header fields from Rx descriptor
+ *  @rx_ring: rx descriptor ring packet is being transacted on
+ *  @rx_desc: pointer to the EOP Rx descriptor
+ *  @skb: pointer to current skb being populated
  *
- * This function checks the ring, descriptor, and packet information in
- * order to populate the hash, checksum, VLAN, timestamp, protocol, and
- * other fields within the skb.
+ *  This function checks the ring, descriptor, and packet information in
+ *  order to populate the hash, checksum, VLAN, timestamp, protocol, and
+ *  other fields within the skb.
  **/
 static void igb_process_skb_fields(struct igb_ring *rx_ring,
                                   union e1000_adv_rx_desc *rx_desc,
@@ -6556,7 +6585,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring,
 
        igb_ptp_rx_hwtstamp(rx_ring->q_vector, rx_desc, skb);
 
-       if ((dev->features & NETIF_F_HW_VLAN_RX) &&
+       if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
            igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) {
                u16 vid;
                if (igb_test_staterr(rx_desc, E1000_RXDEXT_STATERR_LB) &&
@@ -6565,7 +6594,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring,
                else
                        vid = le16_to_cpu(rx_desc->wb.upper.vlan);
 
-               __vlan_hwaccel_put_tag(skb, vid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
        }
 
        skb_record_rx_queue(skb, rx_ring->queue_index);
@@ -6670,8 +6699,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
        /* map page for use */
        dma = dma_map_page(rx_ring->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
 
-       /*
-        * if mapping failed free memory back to system since
+       /* if mapping failed free memory back to system since
         * there isn't much point in holding memory we can't use
         */
        if (dma_mapping_error(rx_ring->dev, dma)) {
@@ -6689,8 +6717,8 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
 }
 
 /**
- * igb_alloc_rx_buffers - Replace used receive buffers; packet split
- * @adapter: address of board private structure
+ *  igb_alloc_rx_buffers - Replace used receive buffers; packet split
+ *  @adapter: address of board private structure
  **/
 void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count)
 {
@@ -6710,8 +6738,7 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count)
                if (!igb_alloc_mapped_page(rx_ring, bi))
                        break;
 
-               /*
-                * Refresh the desc even if buffer_addrs didn't change
+               /* Refresh the desc even if buffer_addrs didn't change
                 * because each write-back erases this info.
                 */
                rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset);
@@ -6740,8 +6767,7 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count)
                /* update next to alloc since we have filled the ring */
                rx_ring->next_to_alloc = i;
 
-               /*
-                * Force memory writes to complete before letting h/w
+               /* 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).
@@ -6826,7 +6852,7 @@ static void igb_vlan_mode(struct net_device *netdev, netdev_features_t features)
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
        u32 ctrl, rctl;
-       bool enable = !!(features & NETIF_F_HW_VLAN_RX);
+       bool enable = !!(features & NETIF_F_HW_VLAN_CTAG_RX);
 
        if (enable) {
                /* enable VLAN tag insert/strip */
@@ -6848,7 +6874,8 @@ static void igb_vlan_mode(struct net_device *netdev, netdev_features_t features)
        igb_rlpml_set(adapter);
 }
 
-static int igb_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+static int igb_vlan_rx_add_vid(struct net_device *netdev,
+                              __be16 proto, u16 vid)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -6865,7 +6892,8 @@ static int igb_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
        return 0;
 }
 
-static int igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+static int igb_vlan_rx_kill_vid(struct net_device *netdev,
+                               __be16 proto, u16 vid)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -6891,7 +6919,7 @@ static void igb_restore_vlan(struct igb_adapter *adapter)
        igb_vlan_mode(adapter->netdev, adapter->netdev->features);
 
        for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
-               igb_vlan_rx_add_vid(adapter->netdev, vid);
+               igb_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid);
 }
 
 int igb_set_spd_dplx(struct igb_adapter *adapter, u32 spd, u8 dplx)
@@ -6902,15 +6930,24 @@ int igb_set_spd_dplx(struct igb_adapter *adapter, u32 spd, u8 dplx)
        mac->autoneg = 0;
 
        /* Make sure dplx is at most 1 bit and lsb of speed is not set
-        * for the switch() below to work */
+        * for the switch() below to work
+        */
        if ((spd & 1) || (dplx & ~1))
                goto err_inval;
 
-       /* Fiber NIC's only allow 1000 Gbps Full duplex */
-       if ((adapter->hw.phy.media_type == e1000_media_type_internal_serdes) &&
-           spd != SPEED_1000 &&
-           dplx != DUPLEX_FULL)
-               goto err_inval;
+       /* Fiber NIC's only allow 1000 gbps Full duplex
+        * and 100Mbps Full duplex for 100baseFx sfp
+        */
+       if (adapter->hw.phy.media_type == e1000_media_type_internal_serdes) {
+               switch (spd + dplx) {
+               case SPEED_10 + DUPLEX_HALF:
+               case SPEED_10 + DUPLEX_FULL:
+               case SPEED_100 + DUPLEX_HALF:
+                       goto err_inval;
+               default:
+                       break;
+               }
+       }
 
        switch (spd + dplx) {
        case SPEED_10 + DUPLEX_HALF:
@@ -7009,7 +7046,8 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake,
                igb_power_up_link(adapter);
 
        /* Release control of h/w to f/w.  If f/w is AMT enabled, this
-        * would have already happened in close and is redundant. */
+        * would have already happened in close and is redundant.
+        */
        igb_release_hw_control(adapter);
 
        pci_disable_device(pdev);
@@ -7071,7 +7109,8 @@ static int igb_resume(struct device *dev)
        igb_reset(adapter);
 
        /* let the f/w know that the h/w is now under the control of the
-        * driver. */
+        * driver.
+        */
        igb_get_hw_control(adapter);
 
        wr32(E1000_WUS, ~0);
@@ -7207,8 +7246,7 @@ static int igb_pci_sriov_configure(struct pci_dev *dev, int num_vfs)
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling 'interrupt' - used by things like netconsole to send skbs
+/* Polling 'interrupt' - used by things like netconsole to send skbs
  * without having to re-enable interrupts. It's not called while
  * the interrupt routine is executing.
  */
@@ -7231,13 +7269,13 @@ static void igb_netpoll(struct net_device *netdev)
 #endif /* CONFIG_NET_POLL_CONTROLLER */
 
 /**
- * igb_io_error_detected - called when PCI error is detected
- * @pdev: Pointer to PCI device
- * @state: The current pci connection state
+ *  igb_io_error_detected - called when PCI error is detected
+ *  @pdev: Pointer to PCI device
+ *  @state: The current pci connection state
  *
- * This function is called after a PCI bus error affecting
- * this device has been detected.
- */
+ *  This function is called after a PCI bus error affecting
+ *  this device has been detected.
+ **/
 static pci_ers_result_t igb_io_error_detected(struct pci_dev *pdev,
                                              pci_channel_state_t state)
 {
@@ -7258,12 +7296,12 @@ static pci_ers_result_t igb_io_error_detected(struct pci_dev *pdev,
 }
 
 /**
- * igb_io_slot_reset - called after the pci bus has been reset.
- * @pdev: Pointer to PCI device
+ *  igb_io_slot_reset - called after the pci bus has been reset.
+ *  @pdev: Pointer to PCI device
  *
- * Restart the card from scratch, as if from a cold-boot. Implementation
- * resembles the first-half of the igb_resume routine.
- */
+ *  Restart the card from scratch, as if from a cold-boot. Implementation
+ *  resembles the first-half of the igb_resume routine.
+ **/
 static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
@@ -7291,8 +7329,9 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
 
        err = pci_cleanup_aer_uncorrect_error_status(pdev);
        if (err) {
-               dev_err(&pdev->dev, "pci_cleanup_aer_uncorrect_error_status "
-                       "failed 0x%0x\n", err);
+               dev_err(&pdev->dev,
+                       "pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
+                       err);
                /* non-fatal, continue */
        }
 
@@ -7300,12 +7339,12 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
 }
 
 /**
- * igb_io_resume - called when traffic can start flowing again.
- * @pdev: Pointer to PCI device
+ *  igb_io_resume - called when traffic can start flowing again.
+ *  @pdev: Pointer to PCI device
  *
- * This callback is called when the error recovery driver tells us that
- * its OK to resume normal operation. Implementation resembles the
- * second-half of the igb_resume routine.
+ *  This callback is called when the error recovery driver tells us that
+ *  its OK to resume normal operation. Implementation resembles the
+ *  second-half of the igb_resume routine.
  */
 static void igb_io_resume(struct pci_dev *pdev)
 {
@@ -7322,12 +7361,13 @@ static void igb_io_resume(struct pci_dev *pdev)
        netif_device_attach(netdev);
 
        /* let the f/w know that the h/w is now under the control of the
-        * driver. */
+        * driver.
+        */
        igb_get_hw_control(adapter);
 }
 
 static void igb_rar_set_qsel(struct igb_adapter *adapter, u8 *addr, u32 index,
-                             u8 qsel)
+                            u8 qsel)
 {
        u32 rar_low, rar_high;
        struct e1000_hw *hw = &adapter->hw;
@@ -7336,7 +7376,7 @@ static void igb_rar_set_qsel(struct igb_adapter *adapter, u8 *addr, u32 index,
         * 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));
+                  ((u32) addr[2] << 16) | ((u32) addr[3] << 24));
        rar_high = ((u32) addr[4] | ((u32) addr[5] << 8));
 
        /* Indicate to hardware the Address is Valid. */
@@ -7354,11 +7394,12 @@ static void igb_rar_set_qsel(struct igb_adapter *adapter, u8 *addr, u32 index,
 }
 
 static int igb_set_vf_mac(struct igb_adapter *adapter,
-                          int vf, unsigned char *mac_addr)
+                         int vf, unsigned char *mac_addr)
 {
        struct e1000_hw *hw = &adapter->hw;
        /* VF MAC addresses start at end of receive addresses and moves
-        * torwards the first, as a result a collision should not be possible */
+        * towards the first, as a result a collision should not be possible
+        */
        int rar_entry = hw->mac.rar_entry_count - (vf + 1);
 
        memcpy(adapter->vf_data[vf].vf_mac_addresses, mac_addr, ETH_ALEN);
@@ -7375,13 +7416,13 @@ static int igb_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
                return -EINVAL;
        adapter->vf_data[vf].flags |= IGB_VF_FLAG_PF_SET_MAC;
        dev_info(&adapter->pdev->dev, "setting MAC %pM on VF %d\n", mac, vf);
-       dev_info(&adapter->pdev->dev, "Reload the VF driver to make this"
-                                     " change effective.");
+       dev_info(&adapter->pdev->dev,
+                "Reload the VF driver to make this change effective.");
        if (test_bit(__IGB_DOWN, &adapter->state)) {
-               dev_warn(&adapter->pdev->dev, "The VF MAC address has been set,"
-                        " but the PF device is not up.\n");
-               dev_warn(&adapter->pdev->dev, "Bring the PF device up before"
-                        " attempting to use the VF device.\n");
+               dev_warn(&adapter->pdev->dev,
+                        "The VF MAC address has been set, but the PF device is not up.\n");
+               dev_warn(&adapter->pdev->dev,
+                        "Bring the PF device up before attempting to use the VF device.\n");
        }
        return igb_set_vf_mac(adapter, vf, mac);
 }
@@ -7408,19 +7449,19 @@ static void igb_set_vf_rate_limit(struct e1000_hw *hw, int vf, int tx_rate,
                /* Calculate the rate factor values to set */
                rf_int = link_speed / tx_rate;
                rf_dec = (link_speed - (rf_int * tx_rate));
-               rf_dec = (rf_dec * (1<<E1000_RTTBCNRC_RF_INT_SHIFT)) / tx_rate;
+               rf_dec = (rf_dec * (1 << E1000_RTTBCNRC_RF_INT_SHIFT)) /
+                        tx_rate;
 
                bcnrc_val = E1000_RTTBCNRC_RS_ENA;
-               bcnrc_val |= ((rf_int<<E1000_RTTBCNRC_RF_INT_SHIFT) &
-                              E1000_RTTBCNRC_RF_INT_MASK);
+               bcnrc_val |= ((rf_int << E1000_RTTBCNRC_RF_INT_SHIFT) &
+                             E1000_RTTBCNRC_RF_INT_MASK);
                bcnrc_val |= (rf_dec & E1000_RTTBCNRC_RF_DEC_MASK);
        } else {
                bcnrc_val = 0;
        }
 
        wr32(E1000_RTTDQSEL, vf); /* vf X uses queue X */
-       /*
-        * Set global transmit compensation time to the MMW_SIZE in RTTBCNRM
+       /* Set global transmit compensation time to the MMW_SIZE in RTTBCNRM
         * register. MMW_SIZE=0x014 if 9728-byte jumbo is supported.
         */
        wr32(E1000_RTTBCNRM, 0x14);
@@ -7442,8 +7483,7 @@ static void igb_check_vf_rate_limit(struct igb_adapter *adapter)
                reset_rate = true;
                adapter->vf_rate_link_speed = 0;
                dev_info(&adapter->pdev->dev,
-                        "Link speed has been changed. VF Transmit "
-                        "rate is disabled\n");
+                        "Link speed has been changed. VF Transmit rate is disabled\n");
        }
 
        for (i = 0; i < adapter->vfs_allocated_count; i++) {
@@ -7451,8 +7491,8 @@ static void igb_check_vf_rate_limit(struct igb_adapter *adapter)
                        adapter->vf_data[i].tx_rate = 0;
 
                igb_set_vf_rate_limit(&adapter->hw, i,
-                                     adapter->vf_data[i].tx_rate,
-                                     actual_link_speed);
+                                     adapter->vf_data[i].tx_rate,
+                                     actual_link_speed);
        }
 }
 
@@ -7478,6 +7518,33 @@ static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
        return 0;
 }
 
+static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
+                                  bool setting)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       struct e1000_hw *hw = &adapter->hw;
+       u32 reg_val, reg_offset;
+
+       if (!adapter->vfs_allocated_count)
+               return -EOPNOTSUPP;
+
+       if (vf >= adapter->vfs_allocated_count)
+               return -EINVAL;
+
+       reg_offset = (hw->mac.type == e1000_82576) ? E1000_DTXSWC : E1000_TXSWC;
+       reg_val = rd32(reg_offset);
+       if (setting)
+               reg_val |= ((1 << vf) |
+                           (1 << (vf + E1000_DTXSWC_VLAN_SPOOF_SHIFT)));
+       else
+               reg_val &= ~((1 << vf) |
+                            (1 << (vf + E1000_DTXSWC_VLAN_SPOOF_SHIFT)));
+       wr32(reg_offset, reg_val);
+
+       adapter->vf_data[vf].spoofchk_enabled = setting;
+       return E1000_SUCCESS;
+}
+
 static int igb_ndo_get_vf_config(struct net_device *netdev,
                                 int vf, struct ifla_vf_info *ivi)
 {
@@ -7489,6 +7556,7 @@ static int igb_ndo_get_vf_config(struct net_device *netdev,
        ivi->tx_rate = adapter->vf_data[vf].tx_rate;
        ivi->vlan = adapter->vf_data[vf].pf_vlan;
        ivi->qos = adapter->vf_data[vf].pf_qos;
+       ivi->spoofchk = adapter->vf_data[vf].spoofchk_enabled;
        return 0;
 }
 
@@ -7501,6 +7569,7 @@ static void igb_vmm_control(struct igb_adapter *adapter)
        case e1000_82575:
        case e1000_i210:
        case e1000_i211:
+       case e1000_i354:
        default:
                /* replication is not supported for 82575 */
                return;
@@ -7523,7 +7592,7 @@ static void igb_vmm_control(struct igb_adapter *adapter)
                igb_vmdq_set_loopback_pf(hw, true);
                igb_vmdq_set_replication_pf(hw, true);
                igb_vmdq_set_anti_spoofing_pf(hw, true,
-                                               adapter->vfs_allocated_count);
+                                             adapter->vfs_allocated_count);
        } else {
                igb_vmdq_set_loopback_pf(hw, false);
                igb_vmdq_set_replication_pf(hw, false);
@@ -7543,8 +7612,7 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
                        /* force threshold to 0. */
                        wr32(E1000_DMCTXTH, 0);
 
-                       /*
-                        * DMA Coalescing high water mark needs to be greater
+                       /* DMA Coalescing high water mark needs to be greater
                         * than the Rx threshold. Set hwm to PBA - max frame
                         * size in 16B units, capping it at PBA - 6KB.
                         */
@@ -7557,8 +7625,7 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
                                & E1000_FCRTC_RTH_COAL_MASK);
                        wr32(E1000_FCRTC, reg);
 
-                       /*
-                        * Set the DMA Coalescing Rx threshold to PBA - 2 * max
+                       /* Set the DMA Coalescing Rx threshold to PBA - 2 * max
                         * frame size, capping it at PBA - 10KB.
                         */
                        dmac_thr = pba - adapter->max_frame_size / 512;
@@ -7576,11 +7643,12 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
                        reg |= (1000 >> 5);
 
                        /* Disable BMC-to-OS Watchdog Enable */
-                       reg &= ~E1000_DMACR_DC_BMC2OSW_EN;
+                       if (hw->mac.type != e1000_i354)
+                               reg &= ~E1000_DMACR_DC_BMC2OSW_EN;
+
                        wr32(E1000_DMACR, reg);
 
-                       /*
-                        * no lower threshold to disable
+                       /* no lower threshold to disable
                         * coalescing(smart fifb)-UTRESH=0
                         */
                        wr32(E1000_DMCRTRH, 0);
@@ -7589,15 +7657,13 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
 
                        wr32(E1000_DMCTLX, reg);
 
-                       /*
-                        * free space in tx packet buffer to wake from
+                       /* free space in tx packet buffer to wake from
                         * DMA coal
                         */
                        wr32(E1000_DMCTXTH, (IGB_MIN_TXPBSIZE -
                             (IGB_TX_BUF_4096 + adapter->max_frame_size)) >> 6);
 
-                       /*
-                        * make low power state decision controlled
+                       /* make low power state decision controlled
                         * by DMA coal
                         */
                        reg = rd32(E1000_PCIEMISC);
@@ -7611,7 +7677,8 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
        }
 }
 
-/*  igb_read_i2c_byte - Reads 8 bit word over I2C
+/**
+ *  igb_read_i2c_byte - Reads 8 bit word over I2C
  *  @hw: pointer to hardware structure
  *  @byte_offset: byte offset to read
  *  @dev_addr: device address
@@ -7619,9 +7686,9 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
  *
  *  Performs byte read operation over I2C interface at
  *  a specified device address.
- */
+ **/
 s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
-                               u8 dev_addr, u8 *data)
+                     u8 dev_addr, u8 *data)
 {
        struct igb_adapter *adapter = container_of(hw, struct igb_adapter, hw);
        struct i2c_client *this_client = adapter->i2c_client;
@@ -7648,7 +7715,8 @@ s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
        }
 }
 
-/*  igb_write_i2c_byte - Writes 8 bit word over I2C
+/**
+ *  igb_write_i2c_byte - Writes 8 bit word over I2C
  *  @hw: pointer to hardware structure
  *  @byte_offset: byte offset to write
  *  @dev_addr: device address
@@ -7656,9 +7724,9 @@ s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
  *
  *  Performs byte write operation over I2C interface at
  *  a specified device address.
- */
+ **/
 s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
-                                u8 dev_addr, u8 data)
+                      u8 dev_addr, u8 data)
 {
        struct igb_adapter *adapter = container_of(hw, struct igb_adapter, hw);
        struct i2c_client *this_client = adapter->i2c_client;
index 0a237507ee85008e9e11c703b07f58730202e072..7e8c477b0ab9fb6e2758a763411dbde42ae57769 100644 (file)
@@ -1,5 +1,4 @@
-/*
- * PTP Hardware Clock (PHC) driver for the Intel 82576 and 82580
+/* PTP Hardware Clock (PHC) driver for the Intel 82576 and 82580
  *
  * Copyright (C) 2011 Richard Cochran <richardcochran@gmail.com>
  *
@@ -27,8 +26,7 @@
 #define INCVALUE_MASK          0x7fffffff
 #define ISGN                   0x80000000
 
-/*
- * The 82580 timesync updates the system timer every 8ns by 8ns,
+/* The 82580 timesync updates the system timer every 8ns by 8ns,
  * and this update value cannot be reprogrammed.
  *
  * Neither the 82576 nor the 82580 offer registers wide enough to hold
 #define INCVALUE_82576                 (16 << IGB_82576_TSYNC_SHIFT)
 #define IGB_NBITS_82580                        40
 
-/*
- * SYSTIM read access for the 82576
- */
-
+/* SYSTIM read access for the 82576 */
 static cycle_t igb_ptp_read_82576(const struct cyclecounter *cc)
 {
        struct igb_adapter *igb = container_of(cc, struct igb_adapter, cc);
@@ -97,10 +92,7 @@ static cycle_t igb_ptp_read_82576(const struct cyclecounter *cc)
        return val;
 }
 
-/*
- * SYSTIM read access for the 82580
- */
-
+/* SYSTIM read access for the 82580 */
 static cycle_t igb_ptp_read_82580(const struct cyclecounter *cc)
 {
        struct igb_adapter *igb = container_of(cc, struct igb_adapter, cc);
@@ -108,8 +100,7 @@ static cycle_t igb_ptp_read_82580(const struct cyclecounter *cc)
        u64 val;
        u32 lo, hi, jk;
 
-       /*
-        * The timestamp latches on lowest register read. For the 82580
+       /* The timestamp latches on lowest register read. For the 82580
         * the lowest register is SYSTIMR instead of SYSTIML.  However we only
         * need to provide nanosecond resolution, so we just ignore it.
         */
@@ -123,17 +114,13 @@ static cycle_t igb_ptp_read_82580(const struct cyclecounter *cc)
        return val;
 }
 
-/*
- * SYSTIM read access for I210/I211
- */
-
+/* SYSTIM read access for I210/I211 */
 static void igb_ptp_read_i210(struct igb_adapter *adapter, struct timespec *ts)
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 sec, nsec, jk;
 
-       /*
-        * The timestamp latches on lowest register read. For I210/I211, the
+       /* The timestamp latches on lowest register read. For I210/I211, the
         * lowest register is SYSTIMR. Since we only need to provide nanosecond
         * resolution, we can ignore it.
         */
@@ -150,8 +137,7 @@ static void igb_ptp_write_i210(struct igb_adapter *adapter,
 {
        struct e1000_hw *hw = &adapter->hw;
 
-       /*
-        * Writing the SYSTIMR register is not necessary as it only provides
+       /* Writing the SYSTIMR register is not necessary as it only provides
         * sub-nanosecond resolution.
         */
        wr32(E1000_SYSTIML, ts->tv_nsec);
@@ -185,6 +171,7 @@ static void igb_ptp_systim_to_hwtstamp(struct igb_adapter *adapter,
        switch (adapter->hw.mac.type) {
        case e1000_82576:
        case e1000_82580:
+       case e1000_i354:
        case e1000_i350:
                spin_lock_irqsave(&adapter->tmreg_lock, flags);
 
@@ -207,10 +194,7 @@ static void igb_ptp_systim_to_hwtstamp(struct igb_adapter *adapter,
        }
 }
 
-/*
- * PTP clock operations
- */
-
+/* PTP clock operations */
 static int igb_ptp_adjfreq_82576(struct ptp_clock_info *ptp, s32 ppb)
 {
        struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
@@ -387,7 +371,7 @@ static int igb_ptp_enable(struct ptp_clock_info *ptp,
  *
  * This work function polls the TSYNCTXCTL valid bit to determine when a
  * timestamp has been taken for the current stored skb.
- */
+ **/
 void igb_ptp_tx_work(struct work_struct *work)
 {
        struct igb_adapter *adapter = container_of(work, struct igb_adapter,
@@ -437,7 +421,7 @@ static void igb_ptp_overflow_check(struct work_struct *work)
  * dropped an Rx packet that was timestamped when the ring is full. The
  * particular error is rare but leaves the device in a state unable to timestamp
  * any future packets.
- */
+ **/
 void igb_ptp_rx_hang(struct igb_adapter *adapter)
 {
        struct e1000_hw *hw = &adapter->hw;
@@ -481,7 +465,7 @@ void igb_ptp_rx_hang(struct igb_adapter *adapter)
  * If we were asked to do hardware stamping and such a time stamp is
  * available, then it must have been for this skb here because we only
  * allow only one such packet into the queue.
- */
+ **/
 void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
 {
        struct e1000_hw *hw = &adapter->hw;
@@ -506,15 +490,14 @@ void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
  * This function is meant to retrieve a timestamp from the first buffer of an
  * incoming frame.  The value is stored in little endian format starting on
  * byte 8.
- */
+ **/
 void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector,
                         unsigned char *va,
                         struct sk_buff *skb)
 {
        __le64 *regval = (__le64 *)va;
 
-       /*
-        * The timestamp is recorded in little endian format.
+       /* The timestamp is recorded in little endian format.
         * DWORD: 0        1        2        3
         * Field: Reserved Reserved SYSTIML  SYSTIMH
         */
@@ -529,7 +512,7 @@ void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector,
  *
  * This function is meant to retrieve a timestamp from the internal registers
  * of the adapter and store it in the skb.
- */
+ **/
 void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector,
                         struct sk_buff *skb)
 {
@@ -537,8 +520,7 @@ void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector,
        struct e1000_hw *hw = &adapter->hw;
        u64 regval;
 
-       /*
-        * If this bit is set, then the RX registers contain the time stamp. No
+       /* If this bit is set, then the RX registers contain the time stamp. No
         * other packet will be time stamped until we read these registers, so
         * read the registers to make them available again. Because only one
         * packet can be time stamped at a time, we know that the register
@@ -574,7 +556,6 @@ void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector,
  * type has to be specified. Matching the kind of event packet is
  * not supported, with the exception of "all V2 events regardless of
  * level 2 or 4".
- *
  **/
 int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
                           struct ifreq *ifr, int cmd)
@@ -655,10 +636,9 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
                return 0;
        }
 
-       /*
-        * Per-packet timestamping only works if all packets are
+       /* Per-packet timestamping only works if all packets are
         * timestamped, so enable timestamping in all packets as
-        * long as one rx filter was configured.
+        * long as one Rx filter was configured.
         */
        if ((hw->mac.type >= e1000_82580) && tsync_rx_ctl) {
                tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
@@ -756,6 +736,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                wr32(E1000_TIMINCA, INCPERIOD_82576 | INCVALUE_82576);
                break;
        case e1000_82580:
+       case e1000_i354:
        case e1000_i350:
                snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
                adapter->ptp_caps.owner = THIS_MODULE;
@@ -844,6 +825,7 @@ void igb_ptp_stop(struct igb_adapter *adapter)
        switch (adapter->hw.mac.type) {
        case e1000_82576:
        case e1000_82580:
+       case e1000_i354:
        case e1000_i350:
                cancel_delayed_work_sync(&adapter->ptp_overflow_work);
                break;
@@ -888,6 +870,7 @@ void igb_ptp_reset(struct igb_adapter *adapter)
                wr32(E1000_TIMINCA, INCPERIOD_82576 | INCVALUE_82576);
                break;
        case e1000_82580:
+       case e1000_i354:
        case e1000_i350:
        case e1000_i210:
        case e1000_i211:
index d60cd43934153df96f45a164c05043e1b36a0dd9..93eb7ee06d3e0aa2159fcccc61e797eb44fd79bd 100644 (file)
@@ -116,7 +116,7 @@ static void igbvf_receive_skb(struct igbvf_adapter *adapter,
                else
                        vid = le16_to_cpu(vlan) & E1000_RXD_SPC_VLAN_MASK;
                if (test_bit(vid, adapter->active_vlans))
-                       __vlan_hwaccel_put_tag(skb, vid);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
        }
 
        napi_gro_receive(&adapter->rx_ring->napi, skb);
@@ -447,7 +447,6 @@ int igbvf_setup_tx_resources(struct igbvf_adapter *adapter,
 
        tx_ring->desc = dma_alloc_coherent(&pdev->dev, tx_ring->size,
                                           &tx_ring->dma, GFP_KERNEL);
-
        if (!tx_ring->desc)
                goto err;
 
@@ -488,7 +487,6 @@ int igbvf_setup_rx_resources(struct igbvf_adapter *adapter,
 
        rx_ring->desc = dma_alloc_coherent(&pdev->dev, rx_ring->size,
                                           &rx_ring->dma, GFP_KERNEL);
-
        if (!rx_ring->desc)
                goto err;
 
@@ -1232,7 +1230,8 @@ static void igbvf_set_rlpml(struct igbvf_adapter *adapter)
        e1000_rlpml_set_vf(hw, max_frame_size);
 }
 
-static int igbvf_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+static int igbvf_vlan_rx_add_vid(struct net_device *netdev,
+                                __be16 proto, u16 vid)
 {
        struct igbvf_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -1245,7 +1244,8 @@ static int igbvf_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
        return 0;
 }
 
-static int igbvf_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+static int igbvf_vlan_rx_kill_vid(struct net_device *netdev,
+                                 __be16 proto, u16 vid)
 {
        struct igbvf_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -1264,7 +1264,7 @@ static void igbvf_restore_vlan(struct igbvf_adapter *adapter)
        u16 vid;
 
        for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
-               igbvf_vlan_rx_add_vid(adapter->netdev, vid);
+               igbvf_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid);
 }
 
 /**
@@ -2724,9 +2724,9 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                           NETIF_F_RXCSUM;
 
        netdev->features = netdev->hw_features |
-                          NETIF_F_HW_VLAN_TX |
-                          NETIF_F_HW_VLAN_RX |
-                          NETIF_F_HW_VLAN_FILTER;
+                          NETIF_F_HW_VLAN_CTAG_TX |
+                          NETIF_F_HW_VLAN_CTAG_RX |
+                          NETIF_F_HW_VLAN_CTAG_FILTER;
 
        if (pci_using_dac)
                netdev->features |= NETIF_F_HIGHDMA;
index b5f94abe3cffc9c406f8e2aaa27ed9cd77ad6542..fce3e92f9d11cf249b9574d3687947f3242dd81e 100644 (file)
@@ -101,8 +101,10 @@ static void ixgb_tx_timeout_task(struct work_struct *work);
 
 static void ixgb_vlan_strip_enable(struct ixgb_adapter *adapter);
 static void ixgb_vlan_strip_disable(struct ixgb_adapter *adapter);
-static int ixgb_vlan_rx_add_vid(struct net_device *netdev, u16 vid);
-static int ixgb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid);
+static int ixgb_vlan_rx_add_vid(struct net_device *netdev,
+                               __be16 proto, u16 vid);
+static int ixgb_vlan_rx_kill_vid(struct net_device *netdev,
+                                __be16 proto, u16 vid);
 static void ixgb_restore_vlan(struct ixgb_adapter *adapter);
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -332,8 +334,8 @@ ixgb_fix_features(struct net_device *netdev, netdev_features_t features)
         * Tx VLAN insertion does not work per HW design when Rx stripping is
         * disabled.
         */
-       if (!(features & NETIF_F_HW_VLAN_RX))
-               features &= ~NETIF_F_HW_VLAN_TX;
+       if (!(features & NETIF_F_HW_VLAN_CTAG_RX))
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -344,7 +346,7 @@ ixgb_set_features(struct net_device *netdev, netdev_features_t features)
        struct ixgb_adapter *adapter = netdev_priv(netdev);
        netdev_features_t changed = features ^ netdev->features;
 
-       if (!(changed & (NETIF_F_RXCSUM|NETIF_F_HW_VLAN_RX)))
+       if (!(changed & (NETIF_F_RXCSUM|NETIF_F_HW_VLAN_CTAG_RX)))
                return 0;
 
        adapter->rx_csum = !!(features & NETIF_F_RXCSUM);
@@ -479,10 +481,10 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        netdev->hw_features = NETIF_F_SG |
                           NETIF_F_TSO |
                           NETIF_F_HW_CSUM |
-                          NETIF_F_HW_VLAN_TX |
-                          NETIF_F_HW_VLAN_RX;
+                          NETIF_F_HW_VLAN_CTAG_TX |
+                          NETIF_F_HW_VLAN_CTAG_RX;
        netdev->features = netdev->hw_features |
-                          NETIF_F_HW_VLAN_FILTER;
+                          NETIF_F_HW_VLAN_CTAG_FILTER;
        netdev->hw_features |= NETIF_F_RXCSUM;
 
        if (pci_using_dac) {
@@ -717,14 +719,11 @@ ixgb_setup_tx_resources(struct ixgb_adapter *adapter)
        txdr->size = ALIGN(txdr->size, 4096);
 
        txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size, &txdr->dma,
-                                       GFP_KERNEL);
+                                       GFP_KERNEL | __GFP_ZERO);
        if (!txdr->desc) {
                vfree(txdr->buffer_info);
-               netif_err(adapter, probe, adapter->netdev,
-                         "Unable to allocate transmit descriptor memory\n");
                return -ENOMEM;
        }
-       memset(txdr->desc, 0, txdr->size);
 
        txdr->next_to_use = 0;
        txdr->next_to_clean = 0;
@@ -807,8 +806,6 @@ ixgb_setup_rx_resources(struct ixgb_adapter *adapter)
 
        if (!rxdr->desc) {
                vfree(rxdr->buffer_info);
-               netif_err(adapter, probe, adapter->netdev,
-                         "Unable to allocate receive descriptors\n");
                return -ENOMEM;
        }
        memset(rxdr->desc, 0, rxdr->size);
@@ -1145,7 +1142,7 @@ ixgb_set_multi(struct net_device *netdev)
        }
 
 alloc_failed:
-       if (netdev->features & NETIF_F_HW_VLAN_RX)
+       if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
                ixgb_vlan_strip_enable(adapter);
        else
                ixgb_vlan_strip_disable(adapter);
@@ -2085,8 +2082,8 @@ ixgb_clean_rx_irq(struct ixgb_adapter *adapter, int *work_done, int work_to_do)
 
                skb->protocol = eth_type_trans(skb, netdev);
                if (status & IXGB_RX_DESC_STATUS_VP)
-                       __vlan_hwaccel_put_tag(skb,
-                                              le16_to_cpu(rx_desc->special));
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+                                      le16_to_cpu(rx_desc->special));
 
                netif_receive_skb(skb);
 
@@ -2214,7 +2211,7 @@ ixgb_vlan_strip_disable(struct ixgb_adapter *adapter)
 }
 
 static int
-ixgb_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+ixgb_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct ixgb_adapter *adapter = netdev_priv(netdev);
        u32 vfta, index;
@@ -2231,7 +2228,7 @@ ixgb_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
 }
 
 static int
-ixgb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+ixgb_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct ixgb_adapter *adapter = netdev_priv(netdev);
        u32 vfta, index;
@@ -2253,7 +2250,7 @@ ixgb_restore_vlan(struct ixgb_adapter *adapter)
        u16 vid;
 
        for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
-               ixgb_vlan_rx_add_vid(adapter->netdev, vid);
+               ixgb_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid);
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
index a8e10cff7a89fff2fb02fdf60e2338f35beb8d4b..ca932387a80faaad7775ae782b2aa82c1926c25e 100644 (file)
@@ -740,6 +740,11 @@ extern void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter);
 extern void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter);
 extern void ixgbe_dbg_init(void);
 extern void ixgbe_dbg_exit(void);
+#else
+static inline void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter) {}
+static inline void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter) {}
+static inline void ixgbe_dbg_init(void) {}
+static inline void ixgbe_dbg_exit(void) {}
 #endif /* CONFIG_DEBUG_FS */
 static inline struct netdev_queue *txring_txq(const struct ixgbe_ring *ring)
 {
index d0113fc97b6f3f3ced90fc674fdf9a04a212bc53..4a5bfb6b3af05b09e5d5895e0a2b82bf04e10d0b 100644 (file)
@@ -1305,6 +1305,7 @@ static struct ixgbe_mac_operations mac_ops_82598 = {
        .release_swfw_sync      = &ixgbe_release_swfw_sync,
        .get_thermal_sensor_data = NULL,
        .init_thermal_sensor_thresh = NULL,
+       .mng_fw_enabled         = NULL,
 };
 
 static struct ixgbe_eeprom_operations eeprom_ops_82598 = {
index 203a00c2433027c19f824bd9ddb84ed6fa0cb5bd..7946da94b2286a1baba9daf19c84ffe70f07e756 100644 (file)
@@ -59,12 +59,34 @@ static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw,
                                          bool autoneg_wait_to_complete);
 static s32 ixgbe_verify_fw_version_82599(struct ixgbe_hw *hw);
 
+static bool ixgbe_mng_enabled(struct ixgbe_hw *hw)
+{
+       u32 fwsm, manc, factps;
+
+       fwsm = IXGBE_READ_REG(hw, IXGBE_FWSM);
+       if ((fwsm & IXGBE_FWSM_MODE_MASK) != IXGBE_FWSM_FW_MODE_PT)
+               return false;
+
+       manc = IXGBE_READ_REG(hw, IXGBE_MANC);
+       if (!(manc & IXGBE_MANC_RCV_TCO_EN))
+               return false;
+
+       factps = IXGBE_READ_REG(hw, IXGBE_FACTPS);
+       if (factps & IXGBE_FACTPS_MNGCG)
+               return false;
+
+       return true;
+}
+
 static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw)
 {
        struct ixgbe_mac_info *mac = &hw->mac;
 
-       /* enable the laser control functions for SFP+ fiber */
-       if (mac->ops.get_media_type(hw) == ixgbe_media_type_fiber) {
+       /* enable the laser control functions for SFP+ fiber
+        * and MNG not enabled
+        */
+       if ((mac->ops.get_media_type(hw) == ixgbe_media_type_fiber) &&
+           !hw->mng_fw_enabled) {
                mac->ops.disable_tx_laser =
                                       &ixgbe_disable_tx_laser_multispeed_fiber;
                mac->ops.enable_tx_laser =
@@ -563,7 +585,8 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                        return status;
 
                /* Flap the tx laser if it has not already been done */
-               hw->mac.ops.flap_tx_laser(hw);
+               if (hw->mac.ops.flap_tx_laser)
+                       hw->mac.ops.flap_tx_laser(hw);
 
                /*
                 * Wait for the controller to acquire link.  Per IEEE 802.3ap,
@@ -615,7 +638,8 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                        return status;
 
                /* Flap the tx laser if it has not already been done */
-               hw->mac.ops.flap_tx_laser(hw);
+               if (hw->mac.ops.flap_tx_laser)
+                       hw->mac.ops.flap_tx_laser(hw);
 
                /* Wait for the link partner to also set speed */
                msleep(100);
@@ -933,6 +957,7 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
        ixgbe_link_speed link_speed;
        s32 status;
        u32 ctrl, i, autoc, autoc2;
+       u32 curr_lms;
        bool link_up = false;
 
        /* Call adapter stop to disable tx/rx and clear interrupts */
@@ -964,6 +989,9 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
        if (hw->phy.reset_disable == false && hw->phy.ops.reset != NULL)
                hw->phy.ops.reset(hw);
 
+       /* remember AUTOC LMS from before we reset */
+       curr_lms = IXGBE_READ_REG(hw, IXGBE_AUTOC) & IXGBE_AUTOC_LMS_MASK;
+
 mac_reset_top:
        /*
         * Issue global reset to the MAC. Needs to be SW reset if link is up.
@@ -1019,6 +1047,19 @@ mac_reset_top:
                hw->mac.orig_autoc2 = autoc2;
                hw->mac.orig_link_settings_stored = true;
        } else {
+
+               /* If MNG FW is running on a multi-speed device that
+                * doesn't autoneg with out driver support we need to
+                * leave LMS in the state it was before we MAC reset.
+                * Likewise if we support WoL we don't want change the
+                * LMS state either.
+                */
+               if ((hw->phy.multispeed_fiber && hw->mng_fw_enabled) ||
+                   hw->wol_supported)
+                       hw->mac.orig_autoc =
+                               (hw->mac.orig_autoc & ~IXGBE_AUTOC_LMS_MASK) |
+                               curr_lms;
+
                if (autoc != hw->mac.orig_autoc) {
                        /* Need SW/FW semaphore around AUTOC writes if LESM is
                         * on, likewise reset_pipeline requires us to hold
@@ -2216,7 +2257,7 @@ static struct ixgbe_mac_operations mac_ops_82599 = {
        .release_swfw_sync      = &ixgbe_release_swfw_sync,
        .get_thermal_sensor_data = &ixgbe_get_thermal_sensor_data_generic,
        .init_thermal_sensor_thresh = &ixgbe_init_thermal_sensor_thresh_generic,
-
+       .mng_fw_enabled         = &ixgbe_mng_enabled,
 };
 
 static struct ixgbe_eeprom_operations eeprom_ops_82599 = {
index 99e472ebaa750c2a1a90bdd089ba6559d62e4cb3..9bcdeb89af5a0a532689cb7b91b2b3b64fca380a 100644 (file)
@@ -592,6 +592,36 @@ s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr)
        return 0;
 }
 
+enum ixgbe_bus_width ixgbe_convert_bus_width(u16 link_status)
+{
+       switch (link_status & IXGBE_PCI_LINK_WIDTH) {
+       case IXGBE_PCI_LINK_WIDTH_1:
+               return ixgbe_bus_width_pcie_x1;
+       case IXGBE_PCI_LINK_WIDTH_2:
+               return ixgbe_bus_width_pcie_x2;
+       case IXGBE_PCI_LINK_WIDTH_4:
+               return ixgbe_bus_width_pcie_x4;
+       case IXGBE_PCI_LINK_WIDTH_8:
+               return ixgbe_bus_width_pcie_x8;
+       default:
+               return ixgbe_bus_width_unknown;
+       }
+}
+
+enum ixgbe_bus_speed ixgbe_convert_bus_speed(u16 link_status)
+{
+       switch (link_status & IXGBE_PCI_LINK_SPEED) {
+       case IXGBE_PCI_LINK_SPEED_2500:
+               return ixgbe_bus_speed_2500;
+       case IXGBE_PCI_LINK_SPEED_5000:
+               return ixgbe_bus_speed_5000;
+       case IXGBE_PCI_LINK_SPEED_8000:
+               return ixgbe_bus_speed_8000;
+       default:
+               return ixgbe_bus_speed_unknown;
+       }
+}
+
 /**
  *  ixgbe_get_bus_info_generic - Generic set PCI bus info
  *  @hw: pointer to hardware structure
@@ -610,35 +640,8 @@ s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw)
        pci_read_config_word(adapter->pdev, IXGBE_PCI_LINK_STATUS,
                             &link_status);
 
-       switch (link_status & IXGBE_PCI_LINK_WIDTH) {
-       case IXGBE_PCI_LINK_WIDTH_1:
-               hw->bus.width = ixgbe_bus_width_pcie_x1;
-               break;
-       case IXGBE_PCI_LINK_WIDTH_2:
-               hw->bus.width = ixgbe_bus_width_pcie_x2;
-               break;
-       case IXGBE_PCI_LINK_WIDTH_4:
-               hw->bus.width = ixgbe_bus_width_pcie_x4;
-               break;
-       case IXGBE_PCI_LINK_WIDTH_8:
-               hw->bus.width = ixgbe_bus_width_pcie_x8;
-               break;
-       default:
-               hw->bus.width = ixgbe_bus_width_unknown;
-               break;
-       }
-
-       switch (link_status & IXGBE_PCI_LINK_SPEED) {
-       case IXGBE_PCI_LINK_SPEED_2500:
-               hw->bus.speed = ixgbe_bus_speed_2500;
-               break;
-       case IXGBE_PCI_LINK_SPEED_5000:
-               hw->bus.speed = ixgbe_bus_speed_5000;
-               break;
-       default:
-               hw->bus.speed = ixgbe_bus_speed_unknown;
-               break;
-       }
+       hw->bus.width = ixgbe_convert_bus_width(link_status);
+       hw->bus.speed = ixgbe_convert_bus_speed(link_status);
 
        mac->ops.set_lan_id(hw);
 
@@ -1125,7 +1128,7 @@ s32 ixgbe_read_eerd_buffer_generic(struct ixgbe_hw *hw, u16 offset,
        }
 
        for (i = 0; i < words; i++) {
-               eerd = ((offset + i) << IXGBE_EEPROM_RW_ADDR_SHIFT) +
+               eerd = ((offset + i) << IXGBE_EEPROM_RW_ADDR_SHIFT) |
                       IXGBE_EEPROM_RW_REG_START;
 
                IXGBE_WRITE_REG(hw, IXGBE_EERD, eerd);
index bc3948ead6e08323759c24b38884766ede33d7f8..22eee38868f1aa2801137e3b26eb2131efe9ec56 100644 (file)
@@ -40,6 +40,8 @@ s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw);
 s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num,
                                   u32 pba_num_size);
 s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr);
+enum ixgbe_bus_width ixgbe_convert_bus_width(u16 link_status);
+enum ixgbe_bus_speed ixgbe_convert_bus_speed(u16 link_status);
 s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw);
 void ixgbe_set_lan_id_multi_port_pcie(struct ixgbe_hw *hw);
 s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw);
index 79f4a26ea6cc580c1b23cd72fee362c3c495880a..6225f880a3f455a342a1abe0dc9a76bf15b8a737 100644 (file)
@@ -63,7 +63,7 @@ char ixgbe_default_device_descr[] =
 static char ixgbe_default_device_descr[] =
                              "Intel(R) 10 Gigabit Network Connection";
 #endif
-#define DRV_VERSION "3.11.33-k"
+#define DRV_VERSION "3.13.10-k"
 const char ixgbe_driver_version[] = DRV_VERSION;
 static const char ixgbe_copyright[] =
                                "Copyright (c) 1999-2013 Intel Corporation.";
@@ -149,6 +149,52 @@ MODULE_DESCRIPTION("Intel(R) 10 Gigabit PCI Express Network Driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 
+static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,
+                                         u32 reg, u16 *value)
+{
+       int pos = 0;
+       struct pci_dev *parent_dev;
+       struct pci_bus *parent_bus;
+
+       parent_bus = adapter->pdev->bus->parent;
+       if (!parent_bus)
+               return -1;
+
+       parent_dev = parent_bus->self;
+       if (!parent_dev)
+               return -1;
+
+       pos = pci_find_capability(parent_dev, PCI_CAP_ID_EXP);
+       if (!pos)
+               return -1;
+
+       pci_read_config_word(parent_dev, pos + reg, value);
+       return 0;
+}
+
+static s32 ixgbe_get_parent_bus_info(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       u16 link_status = 0;
+       int err;
+
+       hw->bus.type = ixgbe_bus_type_pci_express;
+
+       /* Get the negotiated link width and speed from PCI config space of the
+        * parent, as this device is behind a switch
+        */
+       err = ixgbe_read_pci_cfg_word_parent(adapter, 18, &link_status);
+
+       /* assume caller will handle error case */
+       if (err)
+               return err;
+
+       hw->bus.width = ixgbe_convert_bus_width(link_status);
+       hw->bus.speed = ixgbe_convert_bus_speed(link_status);
+
+       return 0;
+}
+
 static void ixgbe_service_event_schedule(struct ixgbe_adapter *adapter)
 {
        if (!test_bit(__IXGBE_DOWN, &adapter->state) &&
@@ -1337,7 +1383,7 @@ static unsigned int ixgbe_get_headlen(unsigned char *data,
                        return hdr.network - data;
 
                /* record next protocol if header is present */
-               if (!hdr.ipv4->frag_off)
+               if (!(hdr.ipv4->frag_off & htons(IP_OFFSET)))
                        nexthdr = hdr.ipv4->protocol;
        } else if (protocol == __constant_htons(ETH_P_IPV6)) {
                if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr)))
@@ -1442,10 +1488,10 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring,
 
        ixgbe_ptp_rx_hwtstamp(rx_ring, rx_desc, skb);
 
-       if ((dev->features & NETIF_F_HW_VLAN_RX) &&
+       if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
            ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_VP)) {
                u16 vid = le16_to_cpu(rx_desc->wb.upper.vlan);
-               __vlan_hwaccel_put_tag(skb, vid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
        }
 
        skb_record_rx_queue(skb, rx_ring->queue_index);
@@ -3421,7 +3467,8 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
        hw->mac.ops.enable_rx_dma(hw, rxctrl);
 }
 
-static int ixgbe_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+static int ixgbe_vlan_rx_add_vid(struct net_device *netdev,
+                                __be16 proto, u16 vid)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -3433,7 +3480,8 @@ static int ixgbe_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
        return 0;
 }
 
-static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev,
+                                 __be16 proto, u16 vid)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -3538,10 +3586,10 @@ static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
 {
        u16 vid;
 
-       ixgbe_vlan_rx_add_vid(adapter->netdev, 0);
+       ixgbe_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), 0);
 
        for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
-               ixgbe_vlan_rx_add_vid(adapter->netdev, vid);
+               ixgbe_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid);
 }
 
 /**
@@ -3676,7 +3724,7 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
 
        IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl);
 
-       if (netdev->features & NETIF_F_HW_VLAN_RX)
+       if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
                ixgbe_vlan_strip_enable(adapter);
        else
                ixgbe_vlan_strip_disable(adapter);
@@ -5077,14 +5125,14 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake)
 
        netif_device_detach(netdev);
 
+       rtnl_lock();
        if (netif_running(netdev)) {
-               rtnl_lock();
                ixgbe_down(adapter);
                ixgbe_free_irq(adapter);
                ixgbe_free_all_tx_resources(adapter);
                ixgbe_free_all_rx_resources(adapter);
-               rtnl_unlock();
        }
+       rtnl_unlock();
 
        ixgbe_clear_interrupt_scheme(adapter);
 
@@ -6425,9 +6473,7 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
        struct ixgbe_tx_buffer *first;
        int tso;
        u32 tx_flags = 0;
-#if PAGE_SIZE > IXGBE_MAX_DATA_PER_TXD
        unsigned short f;
-#endif
        u16 count = TXD_USE_COUNT(skb_headlen(skb));
        __be16 protocol = skb->protocol;
        u8 hdr_len = 0;
@@ -6439,12 +6485,9 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
         *       + 1 desc for context descriptor,
         * otherwise try next time
         */
-#if PAGE_SIZE > IXGBE_MAX_DATA_PER_TXD
        for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
                count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
-#else
-       count += skb_shinfo(skb)->nr_frags;
-#endif
+
        if (ixgbe_maybe_stop_tx(tx_ring, count + 3)) {
                tx_ring->tx_stats.tx_busy++;
                return NETDEV_TX_BUSY;
@@ -6983,7 +7026,7 @@ static int ixgbe_set_features(struct net_device *netdev,
                break;
        }
 
-       if (features & NETIF_F_HW_VLAN_RX)
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
                ixgbe_vlan_strip_enable(adapter);
        else
                ixgbe_vlan_strip_disable(adapter);
@@ -7007,7 +7050,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        int err;
 
        if (!(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED))
-               return -EOPNOTSUPP;
+               return ndo_dflt_fdb_add(ndm, tb, dev, addr, flags);
 
        /* Hardware does not support aging addresses so if a
         * ndm_state is given only allow permanent addresses
@@ -7038,44 +7081,6 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        return err;
 }
 
-static int ixgbe_ndo_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
-                            struct net_device *dev,
-                            const unsigned char *addr)
-{
-       struct ixgbe_adapter *adapter = netdev_priv(dev);
-       int err = -EOPNOTSUPP;
-
-       if (ndm->ndm_state & NUD_PERMANENT) {
-               pr_info("%s: FDB only supports static addresses\n",
-                       ixgbe_driver_name);
-               return -EINVAL;
-       }
-
-       if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) {
-               if (is_unicast_ether_addr(addr))
-                       err = dev_uc_del(dev, addr);
-               else if (is_multicast_ether_addr(addr))
-                       err = dev_mc_del(dev, addr);
-               else
-                       err = -EINVAL;
-       }
-
-       return err;
-}
-
-static int ixgbe_ndo_fdb_dump(struct sk_buff *skb,
-                             struct netlink_callback *cb,
-                             struct net_device *dev,
-                             int idx)
-{
-       struct ixgbe_adapter *adapter = netdev_priv(dev);
-
-       if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
-               idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
-
-       return idx;
-}
-
 static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
                                    struct nlmsghdr *nlh)
 {
@@ -7171,8 +7176,6 @@ static const struct net_device_ops ixgbe_netdev_ops = {
        .ndo_set_features = ixgbe_set_features,
        .ndo_fix_features = ixgbe_fix_features,
        .ndo_fdb_add            = ixgbe_ndo_fdb_add,
-       .ndo_fdb_del            = ixgbe_ndo_fdb_del,
-       .ndo_fdb_dump           = ixgbe_ndo_fdb_dump,
        .ndo_bridge_setlink     = ixgbe_ndo_bridge_setlink,
        .ndo_bridge_getlink     = ixgbe_ndo_bridge_getlink,
 };
@@ -7205,6 +7208,7 @@ int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id,
                case IXGBE_SUBDEV_ID_82599_SFP:
                case IXGBE_SUBDEV_ID_82599_RNDC:
                case IXGBE_SUBDEV_ID_82599_ECNA_DP:
+               case IXGBE_SUBDEV_ID_82599_LOM_SFP:
                        is_wol_supported = 1;
                        break;
                }
@@ -7369,6 +7373,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto err_sw_init;
 
+       /* Cache if MNG FW is up so we don't have to read the REG later */
+       if (hw->mac.ops.mng_fw_enabled)
+               hw->mng_fw_enabled = hw->mac.ops.mng_fw_enabled(hw);
+
        /* Make it possible the adapter to be woken up via WOL */
        switch (adapter->hw.mac.type) {
        case ixgbe_mac_82599EB:
@@ -7425,9 +7433,9 @@ skip_sriov:
        netdev->features = NETIF_F_SG |
                           NETIF_F_IP_CSUM |
                           NETIF_F_IPV6_CSUM |
-                          NETIF_F_HW_VLAN_TX |
-                          NETIF_F_HW_VLAN_RX |
-                          NETIF_F_HW_VLAN_FILTER |
+                          NETIF_F_HW_VLAN_CTAG_TX |
+                          NETIF_F_HW_VLAN_CTAG_RX |
+                          NETIF_F_HW_VLAN_CTAG_FILTER |
                           NETIF_F_TSO |
                           NETIF_F_TSO6 |
                           NETIF_F_RXHASH |
@@ -7521,7 +7529,9 @@ skip_sriov:
        /* WOL not supported for all devices */
        adapter->wol = 0;
        hw->eeprom.ops.read(hw, 0x2c, &adapter->eeprom_cap);
-       if (ixgbe_wol_supported(adapter, pdev->device, pdev->subsystem_device))
+       hw->wol_supported = ixgbe_wol_supported(adapter, pdev->device,
+                                               pdev->subsystem_device);
+       if (hw->wol_supported)
                adapter->wol = IXGBE_WUFC_MAG;
 
        device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
@@ -7532,10 +7542,13 @@ skip_sriov:
 
        /* pick up the PCI bus settings for reporting later */
        hw->mac.ops.get_bus_info(hw);
+       if (hw->device_id == IXGBE_DEV_ID_82599_SFP_SF_QP)
+               ixgbe_get_parent_bus_info(adapter);
 
        /* print bus type/speed/width info */
        e_dev_info("(PCI Express:%s:%s) %pM\n",
-                  (hw->bus.speed == ixgbe_bus_speed_5000 ? "5.0GT/s" :
+                  (hw->bus.speed == ixgbe_bus_speed_8000 ? "8.0GT/s" :
+                   hw->bus.speed == ixgbe_bus_speed_5000 ? "5.0GT/s" :
                    hw->bus.speed == ixgbe_bus_speed_2500 ? "2.5GT/s" :
                    "Unknown"),
                   (hw->bus.width == ixgbe_bus_width_pcie_x8 ? "Width x8" :
@@ -7615,9 +7628,13 @@ skip_sriov:
                e_err(probe, "failed to allocate sysfs resources\n");
 #endif /* CONFIG_IXGBE_HWMON */
 
-#ifdef CONFIG_DEBUG_FS
        ixgbe_dbg_adapter_init(adapter);
-#endif /* CONFIG_DEBUG_FS */
+
+       /* Need link setup for MNG FW, else wait for IXGBE_UP */
+       if (hw->mng_fw_enabled && hw->mac.ops.setup_link)
+               hw->mac.ops.setup_link(hw,
+                       IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL,
+                       true);
 
        return 0;
 
@@ -7653,9 +7670,7 @@ static void ixgbe_remove(struct pci_dev *pdev)
        struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
        struct net_device *netdev = adapter->netdev;
 
-#ifdef CONFIG_DEBUG_FS
        ixgbe_dbg_adapter_exit(adapter);
-#endif /*CONFIG_DEBUG_FS */
 
        set_bit(__IXGBE_DOWN, &adapter->state);
        cancel_work_sync(&adapter->service_task);
@@ -7918,15 +7933,11 @@ 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);
 
-#ifdef CONFIG_DEBUG_FS
        ixgbe_dbg_init();
-#endif /* CONFIG_DEBUG_FS */
 
        ret = pci_register_driver(&ixgbe_driver);
        if (ret) {
-#ifdef CONFIG_DEBUG_FS
                ixgbe_dbg_exit();
-#endif /* CONFIG_DEBUG_FS */
                return ret;
        }
 
@@ -7952,9 +7963,7 @@ static void __exit ixgbe_exit_module(void)
 #endif
        pci_unregister_driver(&ixgbe_driver);
 
-#ifdef CONFIG_DEBUG_FS
        ixgbe_dbg_exit();
-#endif /* CONFIG_DEBUG_FS */
 
        rcu_barrier(); /* Wait for completion of call_rcu()'s */
 }
index 97e33669c0b9b4c6e4bc105ab5f125dffabd9ee0..1e7d587c4e572f9efdd3f876b98e906cb3963561 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/ipv6.h>
-#ifdef NETIF_F_HW_VLAN_TX
+#ifdef NETIF_F_HW_VLAN_CTAG_TX
 #include <linux/if_vlan.h>
 #endif
 
@@ -661,13 +661,7 @@ int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask)
        bool enable = ((event_mask & 0x10000000U) != 0);
 
        if (enable) {
-               eth_random_addr(vf_mac_addr);
-               e_info(probe, "IOV: VF %d is enabled MAC %pM\n",
-                      vfn, vf_mac_addr);
-               /*
-                * Store away the VF "permananet" MAC address, it will ask
-                * for it later.
-                */
+               eth_zero_addr(vf_mac_addr);
                memcpy(adapter->vfinfo[vfn].vf_mac_addresses, vf_mac_addr, 6);
        }
 
@@ -688,7 +682,8 @@ static int ixgbe_vf_reset_msg(struct ixgbe_adapter *adapter, u32 vf)
        ixgbe_vf_reset_event(adapter, vf);
 
        /* set vf mac address */
-       ixgbe_set_vf_mac(adapter, vf, vf_mac);
+       if (!is_zero_ether_addr(vf_mac))
+               ixgbe_set_vf_mac(adapter, vf, vf_mac);
 
        vf_shift = vf % 32;
        reg_offset = vf / 32;
@@ -729,8 +724,16 @@ static int ixgbe_vf_reset_msg(struct ixgbe_adapter *adapter, u32 vf)
        IXGBE_WRITE_REG(hw, IXGBE_VMECM(reg_offset), reg);
 
        /* reply to reset with ack and vf mac address */
-       msgbuf[0] = IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_ACK;
-       memcpy(addr, vf_mac, ETH_ALEN);
+       msgbuf[0] = IXGBE_VF_RESET;
+       if (!is_zero_ether_addr(vf_mac)) {
+               msgbuf[0] |= IXGBE_VT_MSGTYPE_ACK;
+               memcpy(addr, vf_mac, ETH_ALEN);
+       } else {
+               msgbuf[0] |= IXGBE_VT_MSGTYPE_NACK;
+               dev_warn(&adapter->pdev->dev,
+                        "VF %d has no MAC address assigned, you may have to assign one manually\n",
+                        vf);
+       }
 
        /*
         * Piggyback the multicast filter type so VF can compute the
index 6652e96c352d3743ff72ebff0a305dbc977b51cb..402f1a2ada3ee4d15b02c1716a1ef581b4a75f69 100644 (file)
@@ -57,6 +57,7 @@
 #define IXGBE_SUBDEV_ID_82599_RNDC       0x1F72
 #define IXGBE_SUBDEV_ID_82599_560FLR     0x17D0
 #define IXGBE_SUBDEV_ID_82599_ECNA_DP    0x0470
+#define IXGBE_SUBDEV_ID_82599_LOM_SFP    0x8976
 #define IXGBE_DEV_ID_82599_SFP_EM        0x1507
 #define IXGBE_DEV_ID_82599_SFP_SF2       0x154D
 #define IXGBE_DEV_ID_82599EN_SFP         0x1557
@@ -729,6 +730,13 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_MDEF_EXT(_i) (0x05160 + ((_i) * 4)) /* 8 of these (0-7) */
 #define IXGBE_LSWFW     0x15014
 
+/* Management Bit Fields and Masks */
+#define IXGBE_MANC_RCV_TCO_EN  0x00020000 /* Rcv TCO packet enable */
+
+/* Firmware Semaphore Register */
+#define IXGBE_FWSM_MODE_MASK   0xE
+#define IXGBE_FWSM_FW_MODE_PT  0x4
+
 /* ARC Subsystem registers */
 #define IXGBE_HICR      0x15F00
 #define IXGBE_FWSTS     0x15F0C
@@ -1019,6 +1027,7 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_CTRL_RST_MASK     (IXGBE_CTRL_LNK_RST | IXGBE_CTRL_RST)
 
 /* FACTPS */
+#define IXGBE_FACTPS_MNGCG      0x20000000 /* Manageblility Clock Gated */
 #define IXGBE_FACTPS_LFS        0x40000000 /* LAN Function Select */
 
 /* MHADD Bit Masks */
@@ -1827,6 +1836,7 @@ enum {
 #define IXGBE_PCI_LINK_SPEED      0xF
 #define IXGBE_PCI_LINK_SPEED_2500 0x1
 #define IXGBE_PCI_LINK_SPEED_5000 0x2
+#define IXGBE_PCI_LINK_SPEED_8000 0x3
 #define IXGBE_PCI_HEADER_TYPE_REGISTER  0x0E
 #define IXGBE_PCI_HEADER_TYPE_MULTIFUNC 0x80
 #define IXGBE_PCI_DEVICE_CONTROL2_16ms  0x0005
@@ -2650,6 +2660,7 @@ enum ixgbe_bus_speed {
        ixgbe_bus_speed_133     = 133,
        ixgbe_bus_speed_2500    = 2500,
        ixgbe_bus_speed_5000    = 5000,
+       ixgbe_bus_speed_8000    = 8000,
        ixgbe_bus_speed_reserved
 };
 
@@ -2859,6 +2870,7 @@ struct ixgbe_mac_operations {
        s32 (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8);
        s32 (*get_thermal_sensor_data)(struct ixgbe_hw *);
        s32 (*init_thermal_sensor_thresh)(struct ixgbe_hw *hw);
+       bool (*mng_fw_enabled)(struct ixgbe_hw *hw);
 };
 
 struct ixgbe_phy_operations {
@@ -2986,6 +2998,8 @@ struct ixgbe_hw {
        bool                            adapter_stopped;
        bool                            force_full_reset;
        bool                            allow_unsupported_sfp;
+       bool                            mng_fw_enabled;
+       bool                            wol_supported;
 };
 
 struct ixgbe_info {
index 66c5e946284e0b68489ccd924e9ec21727023079..389324f5929a5ef9fd16bac01b9a6d799943747a 100644 (file)
@@ -854,6 +854,7 @@ static struct ixgbe_mac_operations mac_ops_X540 = {
        .enable_rx_buff         = &ixgbe_enable_rx_buff_generic,
        .get_thermal_sensor_data = NULL,
        .init_thermal_sensor_thresh = NULL,
+       .mng_fw_enabled         = NULL,
 };
 
 static struct ixgbe_eeprom_operations eeprom_ops_X540 = {
index fc0af9a3bb35b354e26568af3b68d54c9a3c5018..fff0d98675295182fd15b7bbba36820274286ea2 100644 (file)
@@ -44,8 +44,8 @@ struct ixgbevf_tx_buffer {
        struct sk_buff *skb;
        dma_addr_t dma;
        unsigned long time_stamp;
+       union ixgbe_adv_tx_desc *next_to_watch;
        u16 length;
-       u16 next_to_watch;
        u16 mapped_as_page;
 };
 
index 2b6cb5ca48eefe8f0a075b1166f24dff7aaaaa83..1f5166ad6bb514ade1ee4fd906c1e47385bf5b6a 100644 (file)
@@ -76,12 +76,9 @@ static const struct ixgbevf_info *ixgbevf_info_tbl[] = {
  * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
  *   Class, Class Mask, private data (not used) }
  */
-static struct pci_device_id ixgbevf_pci_tbl[] = {
-       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF),
-       board_82599_vf},
-       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF),
-       board_X540_vf},
-
+static DEFINE_PCI_DEVICE_TABLE(ixgbevf_pci_tbl) = {
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF), board_82599_vf },
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF), board_X540_vf },
        /* required last entry */
        {0, }
 };
@@ -190,28 +187,37 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
        struct ixgbevf_adapter *adapter = q_vector->adapter;
        union ixgbe_adv_tx_desc *tx_desc, *eop_desc;
        struct ixgbevf_tx_buffer *tx_buffer_info;
-       unsigned int i, eop, count = 0;
+       unsigned int i, count = 0;
        unsigned int total_bytes = 0, total_packets = 0;
 
        if (test_bit(__IXGBEVF_DOWN, &adapter->state))
                return true;
 
        i = tx_ring->next_to_clean;
-       eop = tx_ring->tx_buffer_info[i].next_to_watch;
-       eop_desc = IXGBEVF_TX_DESC(tx_ring, eop);
+       tx_buffer_info = &tx_ring->tx_buffer_info[i];
+       eop_desc = tx_buffer_info->next_to_watch;
 
-       while ((eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD)) &&
-              (count < tx_ring->count)) {
+       do {
                bool cleaned = false;
-               rmb(); /* read buffer_info after eop_desc */
-               /* eop could change between read and DD-check */
-               if (unlikely(eop != tx_ring->tx_buffer_info[i].next_to_watch))
-                       goto cont_loop;
+
+               /* if next_to_watch is not set then there is no work pending */
+               if (!eop_desc)
+                       break;
+
+               /* prevent any other reads prior to eop_desc */
+               read_barrier_depends();
+
+               /* if DD is not set pending work has not been completed */
+               if (!(eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD)))
+                       break;
+
+               /* clear next_to_watch to prevent false hangs */
+               tx_buffer_info->next_to_watch = NULL;
+
                for ( ; !cleaned; count++) {
                        struct sk_buff *skb;
                        tx_desc = IXGBEVF_TX_DESC(tx_ring, i);
-                       tx_buffer_info = &tx_ring->tx_buffer_info[i];
-                       cleaned = (i == eop);
+                       cleaned = (tx_desc == eop_desc);
                        skb = tx_buffer_info->skb;
 
                        if (cleaned && skb) {
@@ -234,12 +240,12 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
                        i++;
                        if (i == tx_ring->count)
                                i = 0;
+
+                       tx_buffer_info = &tx_ring->tx_buffer_info[i];
                }
 
-cont_loop:
-               eop = tx_ring->tx_buffer_info[i].next_to_watch;
-               eop_desc = IXGBEVF_TX_DESC(tx_ring, eop);
-       }
+               eop_desc = tx_buffer_info->next_to_watch;
+       } while (count < tx_ring->count);
 
        tx_ring->next_to_clean = i;
 
@@ -285,7 +291,7 @@ static void ixgbevf_receive_skb(struct ixgbevf_q_vector *q_vector,
        u16 tag = le16_to_cpu(rx_desc->wb.upper.vlan);
 
        if (is_vlan && test_bit(tag & VLAN_VID_MASK, adapter->active_vlans))
-               __vlan_hwaccel_put_tag(skb, tag);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tag);
 
        if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL))
                napi_gro_receive(&q_vector->napi, skb);
@@ -1173,7 +1179,8 @@ static void ixgbevf_configure_rx(struct ixgbevf_adapter *adapter)
        }
 }
 
-static int ixgbevf_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+static int ixgbevf_vlan_rx_add_vid(struct net_device *netdev,
+                                  __be16 proto, u16 vid)
 {
        struct ixgbevf_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -1198,7 +1205,8 @@ static int ixgbevf_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
        return err;
 }
 
-static int ixgbevf_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+static int ixgbevf_vlan_rx_kill_vid(struct net_device *netdev,
+                                   __be16 proto, u16 vid)
 {
        struct ixgbevf_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -1221,7 +1229,8 @@ static void ixgbevf_restore_vlan(struct ixgbevf_adapter *adapter)
        u16 vid;
 
        for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID)
-               ixgbevf_vlan_rx_add_vid(adapter->netdev, vid);
+               ixgbevf_vlan_rx_add_vid(adapter->netdev,
+                                       htons(ETH_P_8021Q), vid);
 }
 
 static int ixgbevf_write_uc_addr_list(struct net_device *netdev)
@@ -2046,6 +2055,7 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        struct pci_dev *pdev = adapter->pdev;
+       struct net_device *netdev = adapter->netdev;
        int err;
 
        /* PCI config space info */
@@ -2065,18 +2075,26 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter)
        err = hw->mac.ops.reset_hw(hw);
        if (err) {
                dev_info(&pdev->dev,
-                        "PF still in reset state, assigning new address\n");
-               eth_hw_addr_random(adapter->netdev);
-               memcpy(adapter->hw.mac.addr, adapter->netdev->dev_addr,
-                       adapter->netdev->addr_len);
+                        "PF still in reset state.  Is the PF interface up?\n");
        } else {
                err = hw->mac.ops.init_hw(hw);
                if (err) {
                        pr_err("init_shared_code failed: %d\n", err);
                        goto out;
                }
-               memcpy(adapter->netdev->dev_addr, adapter->hw.mac.addr,
-                      adapter->netdev->addr_len);
+               err = hw->mac.ops.get_mac_addr(hw, hw->mac.addr);
+               if (err)
+                       dev_info(&pdev->dev, "Error reading MAC address\n");
+               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);
+       }
+
+       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);
        }
 
        /* lock to protect mailbox accesses */
@@ -2425,9 +2443,6 @@ int ixgbevf_setup_rx_resources(struct ixgbevf_adapter *adapter,
                                           &rx_ring->dma, GFP_KERNEL);
 
        if (!rx_ring->desc) {
-               hw_dbg(&adapter->hw,
-                      "Unable to allocate memory for "
-                      "the receive descriptor ring\n");
                vfree(rx_ring->rx_buffer_info);
                rx_ring->rx_buffer_info = NULL;
                goto alloc_failed;
@@ -2822,8 +2837,7 @@ static bool ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
 }
 
 static int ixgbevf_tx_map(struct ixgbevf_ring *tx_ring,
-                         struct sk_buff *skb, u32 tx_flags,
-                         unsigned int first)
+                         struct sk_buff *skb, u32 tx_flags)
 {
        struct ixgbevf_tx_buffer *tx_buffer_info;
        unsigned int len;
@@ -2848,7 +2862,6 @@ static int ixgbevf_tx_map(struct ixgbevf_ring *tx_ring,
                                                     size, DMA_TO_DEVICE);
                if (dma_mapping_error(tx_ring->dev, tx_buffer_info->dma))
                        goto dma_error;
-               tx_buffer_info->next_to_watch = i;
 
                len -= size;
                total -= size;
@@ -2878,7 +2891,6 @@ static int ixgbevf_tx_map(struct ixgbevf_ring *tx_ring,
                                              tx_buffer_info->dma))
                                goto dma_error;
                        tx_buffer_info->mapped_as_page = true;
-                       tx_buffer_info->next_to_watch = i;
 
                        len -= size;
                        total -= size;
@@ -2897,8 +2909,6 @@ static int ixgbevf_tx_map(struct ixgbevf_ring *tx_ring,
        else
                i = i - 1;
        tx_ring->tx_buffer_info[i].skb = skb;
-       tx_ring->tx_buffer_info[first].next_to_watch = i;
-       tx_ring->tx_buffer_info[first].time_stamp = jiffies;
 
        return count;
 
@@ -2907,7 +2917,6 @@ dma_error:
 
        /* clear timestamp and dma mappings for failed tx_buffer_info map */
        tx_buffer_info->dma = 0;
-       tx_buffer_info->next_to_watch = 0;
        count--;
 
        /* clear timestamp and dma mappings for remaining portion of packet */
@@ -2924,7 +2933,8 @@ dma_error:
 }
 
 static void ixgbevf_tx_queue(struct ixgbevf_ring *tx_ring, int tx_flags,
-                            int count, u32 paylen, u8 hdr_len)
+                            int count, unsigned int first, u32 paylen,
+                            u8 hdr_len)
 {
        union ixgbe_adv_tx_desc *tx_desc = NULL;
        struct ixgbevf_tx_buffer *tx_buffer_info;
@@ -2975,6 +2985,16 @@ static void ixgbevf_tx_queue(struct ixgbevf_ring *tx_ring, int tx_flags,
 
        tx_desc->read.cmd_type_len |= cpu_to_le32(txd_cmd);
 
+       tx_ring->tx_buffer_info[first].time_stamp = jiffies;
+
+       /* 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();
+
+       tx_ring->tx_buffer_info[first].next_to_watch = tx_desc;
        tx_ring->next_to_use = i;
 }
 
@@ -3066,15 +3086,8 @@ static int ixgbevf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
                tx_flags |= IXGBE_TX_FLAGS_CSUM;
 
        ixgbevf_tx_queue(tx_ring, tx_flags,
-                        ixgbevf_tx_map(tx_ring, skb, tx_flags, first),
-                        skb->len, hdr_len);
-       /*
-        * 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();
+                        ixgbevf_tx_map(tx_ring, skb, tx_flags),
+                        first, skb->len, hdr_len);
 
        writel(tx_ring->next_to_use, adapter->hw.hw_addr + tx_ring->tail);
 
@@ -3400,9 +3413,9 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                           NETIF_F_RXCSUM;
 
        netdev->features = netdev->hw_features |
-                          NETIF_F_HW_VLAN_TX |
-                          NETIF_F_HW_VLAN_RX |
-                          NETIF_F_HW_VLAN_FILTER;
+                          NETIF_F_HW_VLAN_CTAG_TX |
+                          NETIF_F_HW_VLAN_CTAG_RX |
+                          NETIF_F_HW_VLAN_CTAG_FILTER;
 
        netdev->vlan_features |= NETIF_F_TSO;
        netdev->vlan_features |= NETIF_F_TSO6;
index 0c94557b53df9b656dafd2e1a96f56c3803c8288..387b52635bc051259a5e5b99d9740c69bcca687a 100644 (file)
@@ -109,7 +109,12 @@ static s32 ixgbevf_reset_hw_vf(struct ixgbe_hw *hw)
        if (ret_val)
                return ret_val;
 
-       if (msgbuf[0] != (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_ACK))
+       /* New versions of the PF may NACK the reset return message
+        * to indicate that no MAC address has yet been assigned for
+        * the VF.
+        */
+       if (msgbuf[0] != (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_ACK) &&
+           msgbuf[0] != (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_NACK))
                return IXGBE_ERR_INVALID_MAC_ADDR;
 
        memcpy(hw->mac.perm_addr, addr, ETH_ALEN);
index 0519afa413d22714b88e355f1cea862f11650cba..070a6f1a057745a803d3a6ccd6d8201644e95703 100644 (file)
@@ -1059,7 +1059,7 @@ jme_alloc_and_feed_skb(struct jme_adapter *jme, int idx)
                if (rxdesc->descwb.flags & cpu_to_le16(RXWBFLAG_TAGON)) {
                        u16 vid = le16_to_cpu(rxdesc->descwb.vlan);
 
-                       __vlan_hwaccel_put_tag(skb, vid);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
                        NET_STAT(jme).rx_bytes += 4;
                }
                jme->jme_rx(skb);
@@ -3030,8 +3030,8 @@ jme_init_one(struct pci_dev *pdev,
                                                NETIF_F_SG |
                                                NETIF_F_TSO |
                                                NETIF_F_TSO6 |
-                                               NETIF_F_HW_VLAN_TX |
-                                               NETIF_F_HW_VLAN_RX;
+                                               NETIF_F_HW_VLAN_CTAG_TX |
+                                               NETIF_F_HW_VLAN_CTAG_RX;
        if (using_dac)
                netdev->features        |=      NETIF_F_HIGHDMA;
 
index 434e33c527df102adf5052854bc92d816588b4ba..a49e81bdf8e8fab4ca445d92d438717d25083035 100644 (file)
@@ -21,8 +21,8 @@ if NET_VENDOR_MARVELL
 config MV643XX_ETH
        tristate "Marvell Discovery (643XX) and Orion ethernet support"
        depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
-       select INET_LRO
        select PHYLIB
+       select MVMDIO
        ---help---
          This driver supports the gigabit ethernet MACs in the
          Marvell Discovery PPC/MIPS chipset family (MV643XX) and
@@ -39,9 +39,7 @@ config MVMDIO
          interface units of the Marvell EBU SoCs (Kirkwood, Orion5x,
          Dove, Armada 370 and Armada XP).
 
-         For now, this driver is only needed for the MVNETA driver
-         (used on Armada 370 and XP), but it could be used in the
-         future by the MV643XX_ETH driver.
+         This driver is used by the MV643XX_ETH and MVNETA drivers.
 
 config MVNETA
        tristate "Marvell Armada 370/XP network interface support"
index 7f63b4aac434c61c3a80d60e954b0c57fcb9c9e0..5c4a7765ff0efbf7094fade03febe101b9abdcc2 100644 (file)
@@ -2,8 +2,8 @@
 # Makefile for the Marvell device drivers.
 #
 
-obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
 obj-$(CONFIG_MVMDIO) += mvmdio.o
+obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
 obj-$(CONFIG_MVNETA) += mvneta.o
 obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
 obj-$(CONFIG_SKGE) += skge.o
index 6562c736a1d83df96a8feb249a289268b014a854..d0afeea181fb0fd35918941c6e19a0008ded1141 100644 (file)
@@ -20,6 +20,8 @@
  * Copyright (C) 2007-2008 Marvell Semiconductor
  *                        Lennert Buytenhek <buytenh@marvell.com>
  *
+ * Copyright (C) 2013 Michael Stapelberg <michael@stapelberg.de>
+ *
  * 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
@@ -54,8 +56,8 @@
 #include <linux/phy.h>
 #include <linux/mv643xx_eth.h>
 #include <linux/io.h>
+#include <linux/interrupt.h>
 #include <linux/types.h>
-#include <linux/inet_lro.h>
 #include <linux/slab.h>
 #include <linux/clk.h>
 
@@ -67,14 +69,6 @@ static char mv643xx_eth_driver_version[] = "1.4";
  * Registers shared between all ports.
  */
 #define PHY_ADDR                       0x0000
-#define SMI_REG                                0x0004
-#define  SMI_BUSY                      0x10000000
-#define  SMI_READ_VALID                        0x08000000
-#define  SMI_OPCODE_READ               0x04000000
-#define  SMI_OPCODE_WRITE              0x00000000
-#define ERR_INT_CAUSE                  0x0080
-#define  ERR_INT_SMI_DONE              0x00000010
-#define ERR_INT_MASK                   0x0084
 #define WINDOW_BASE(w)                 (0x0200 + ((w) << 3))
 #define WINDOW_SIZE(w)                 (0x0204 + ((w) << 3))
 #define WINDOW_REMAP_HIGH(w)           (0x0280 + ((w) << 2))
@@ -263,25 +257,6 @@ struct mv643xx_eth_shared_private {
         */
        void __iomem *base;
 
-       /*
-        * Points at the right SMI instance to use.
-        */
-       struct mv643xx_eth_shared_private *smi;
-
-       /*
-        * Provides access to local SMI interface.
-        */
-       struct mii_bus *smi_bus;
-
-       /*
-        * If we have access to the error interrupt pin (which is
-        * somewhat misnamed as it not only reflects internal errors
-        * but also reflects SMI completion), use that to wait for
-        * SMI access completion instead of polling the SMI busy bit.
-        */
-       int err_interrupt;
-       wait_queue_head_t smi_busy_wait;
-
        /*
         * Per-port MBUS window access register value.
         */
@@ -293,7 +268,7 @@ struct mv643xx_eth_shared_private {
        int extended_rx_coal_limit;
        int tx_bw_control;
        int tx_csum_limit;
-
+       struct clk *clk;
 };
 
 #define TX_BW_CONTROL_ABSENT           0
@@ -341,12 +316,6 @@ struct mib_counters {
        u32 rx_overrun;
 };
 
-struct lro_counters {
-       u32 lro_aggregated;
-       u32 lro_flushed;
-       u32 lro_no_desc;
-};
-
 struct rx_queue {
        int index;
 
@@ -360,9 +329,6 @@ struct rx_queue {
        dma_addr_t rx_desc_dma;
        int rx_desc_area_size;
        struct sk_buff **rx_skb;
-
-       struct net_lro_mgr lro_mgr;
-       struct net_lro_desc lro_arr[8];
 };
 
 struct tx_queue {
@@ -398,8 +364,6 @@ struct mv643xx_eth_private {
        spinlock_t mib_counters_lock;
        struct mib_counters mib_counters;
 
-       struct lro_counters lro_counters;
-
        struct work_struct tx_timeout_task;
 
        struct napi_struct napi;
@@ -435,9 +399,7 @@ struct mv643xx_eth_private {
        /*
         * Hardware-specific parameters.
         */
-#if defined(CONFIG_HAVE_CLK)
        struct clk *clk;
-#endif
        unsigned int t_clk;
 };
 
@@ -530,42 +492,12 @@ static void txq_maybe_wake(struct tx_queue *txq)
        }
 }
 
-
-/* rx napi ******************************************************************/
-static int
-mv643xx_get_skb_header(struct sk_buff *skb, void **iphdr, void **tcph,
-                      u64 *hdr_flags, void *priv)
-{
-       unsigned long cmd_sts = (unsigned long)priv;
-
-       /*
-        * Make sure that this packet is Ethernet II, is not VLAN
-        * tagged, is IPv4, has a valid IP header, and is TCP.
-        */
-       if ((cmd_sts & (RX_IP_HDR_OK | RX_PKT_IS_IPV4 |
-                      RX_PKT_IS_ETHERNETV2 | RX_PKT_LAYER4_TYPE_MASK |
-                      RX_PKT_IS_VLAN_TAGGED)) !=
-           (RX_IP_HDR_OK | RX_PKT_IS_IPV4 |
-            RX_PKT_IS_ETHERNETV2 | RX_PKT_LAYER4_TYPE_TCP_IPV4))
-               return -1;
-
-       skb_reset_network_header(skb);
-       skb_set_transport_header(skb, ip_hdrlen(skb));
-       *iphdr = ip_hdr(skb);
-       *tcph = tcp_hdr(skb);
-       *hdr_flags = LRO_IPV4 | LRO_TCP;
-
-       return 0;
-}
-
 static int rxq_process(struct rx_queue *rxq, int budget)
 {
        struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
        struct net_device_stats *stats = &mp->dev->stats;
-       int lro_flush_needed;
        int rx;
 
-       lro_flush_needed = 0;
        rx = 0;
        while (rx < budget && rxq->rx_desc_count) {
                struct rx_desc *rx_desc;
@@ -626,12 +558,7 @@ static int rxq_process(struct rx_queue *rxq, int budget)
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                skb->protocol = eth_type_trans(skb, mp->dev);
 
-               if (skb->dev->features & NETIF_F_LRO &&
-                   skb->ip_summed == CHECKSUM_UNNECESSARY) {
-                       lro_receive_skb(&rxq->lro_mgr, skb, (void *)cmd_sts);
-                       lro_flush_needed = 1;
-               } else
-                       netif_receive_skb(skb);
+               napi_gro_receive(&mp->napi, skb);
 
                continue;
 
@@ -651,9 +578,6 @@ err:
                dev_kfree_skb(skb);
        }
 
-       if (lro_flush_needed)
-               lro_flush_all(&rxq->lro_mgr);
-
        if (rx < budget)
                mp->work_rx &= ~(1 << rxq->index);
 
@@ -1120,97 +1044,6 @@ out_write:
        wrlp(mp, PORT_SERIAL_CONTROL, pscr);
 }
 
-static irqreturn_t mv643xx_eth_err_irq(int irq, void *dev_id)
-{
-       struct mv643xx_eth_shared_private *msp = dev_id;
-
-       if (readl(msp->base + ERR_INT_CAUSE) & ERR_INT_SMI_DONE) {
-               writel(~ERR_INT_SMI_DONE, msp->base + ERR_INT_CAUSE);
-               wake_up(&msp->smi_busy_wait);
-               return IRQ_HANDLED;
-       }
-
-       return IRQ_NONE;
-}
-
-static int smi_is_done(struct mv643xx_eth_shared_private *msp)
-{
-       return !(readl(msp->base + SMI_REG) & SMI_BUSY);
-}
-
-static int smi_wait_ready(struct mv643xx_eth_shared_private *msp)
-{
-       if (msp->err_interrupt == NO_IRQ) {
-               int i;
-
-               for (i = 0; !smi_is_done(msp); i++) {
-                       if (i == 10)
-                               return -ETIMEDOUT;
-                       msleep(10);
-               }
-
-               return 0;
-       }
-
-       if (!smi_is_done(msp)) {
-               wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp),
-                                  msecs_to_jiffies(100));
-               if (!smi_is_done(msp))
-                       return -ETIMEDOUT;
-       }
-
-       return 0;
-}
-
-static int smi_bus_read(struct mii_bus *bus, int addr, int reg)
-{
-       struct mv643xx_eth_shared_private *msp = bus->priv;
-       void __iomem *smi_reg = msp->base + SMI_REG;
-       int ret;
-
-       if (smi_wait_ready(msp)) {
-               pr_warn("SMI bus busy timeout\n");
-               return -ETIMEDOUT;
-       }
-
-       writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg);
-
-       if (smi_wait_ready(msp)) {
-               pr_warn("SMI bus busy timeout\n");
-               return -ETIMEDOUT;
-       }
-
-       ret = readl(smi_reg);
-       if (!(ret & SMI_READ_VALID)) {
-               pr_warn("SMI bus read not valid\n");
-               return -ENODEV;
-       }
-
-       return ret & 0xffff;
-}
-
-static int smi_bus_write(struct mii_bus *bus, int addr, int reg, u16 val)
-{
-       struct mv643xx_eth_shared_private *msp = bus->priv;
-       void __iomem *smi_reg = msp->base + SMI_REG;
-
-       if (smi_wait_ready(msp)) {
-               pr_warn("SMI bus busy timeout\n");
-               return -ETIMEDOUT;
-       }
-
-       writel(SMI_OPCODE_WRITE | (reg << 21) |
-               (addr << 16) | (val & 0xffff), smi_reg);
-
-       if (smi_wait_ready(msp)) {
-               pr_warn("SMI bus busy timeout\n");
-               return -ETIMEDOUT;
-       }
-
-       return 0;
-}
-
-
 /* statistics ***************************************************************/
 static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev)
 {
@@ -1236,26 +1069,6 @@ static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev)
        return stats;
 }
 
-static void mv643xx_eth_grab_lro_stats(struct mv643xx_eth_private *mp)
-{
-       u32 lro_aggregated = 0;
-       u32 lro_flushed = 0;
-       u32 lro_no_desc = 0;
-       int i;
-
-       for (i = 0; i < mp->rxq_count; i++) {
-               struct rx_queue *rxq = mp->rxq + i;
-
-               lro_aggregated += rxq->lro_mgr.stats.aggregated;
-               lro_flushed += rxq->lro_mgr.stats.flushed;
-               lro_no_desc += rxq->lro_mgr.stats.no_desc;
-       }
-
-       mp->lro_counters.lro_aggregated = lro_aggregated;
-       mp->lro_counters.lro_flushed = lro_flushed;
-       mp->lro_counters.lro_no_desc = lro_no_desc;
-}
-
 static inline u32 mib_read(struct mv643xx_eth_private *mp, int offset)
 {
        return rdl(mp, MIB_COUNTERS(mp->port_num) + offset);
@@ -1419,10 +1232,6 @@ struct mv643xx_eth_stats {
        { #m, FIELD_SIZEOF(struct mib_counters, m),             \
          -1, offsetof(struct mv643xx_eth_private, mib_counters.m) }
 
-#define LROSTAT(m)                                             \
-       { #m, FIELD_SIZEOF(struct lro_counters, m),             \
-         -1, offsetof(struct mv643xx_eth_private, lro_counters.m) }
-
 static const struct mv643xx_eth_stats mv643xx_eth_stats[] = {
        SSTAT(rx_packets),
        SSTAT(tx_packets),
@@ -1464,9 +1273,6 @@ static const struct mv643xx_eth_stats mv643xx_eth_stats[] = {
        MIBSTAT(late_collision),
        MIBSTAT(rx_discard),
        MIBSTAT(rx_overrun),
-       LROSTAT(lro_aggregated),
-       LROSTAT(lro_flushed),
-       LROSTAT(lro_no_desc),
 };
 
 static int
@@ -1523,6 +1329,34 @@ mv643xx_eth_get_settings_phyless(struct mv643xx_eth_private *mp,
        return 0;
 }
 
+static void
+mv643xx_eth_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       struct mv643xx_eth_private *mp = netdev_priv(dev);
+       wol->supported = 0;
+       wol->wolopts = 0;
+       if (mp->phy)
+               phy_ethtool_get_wol(mp->phy, wol);
+}
+
+static int
+mv643xx_eth_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       struct mv643xx_eth_private *mp = netdev_priv(dev);
+       int err;
+
+       if (mp->phy == NULL)
+               return -EOPNOTSUPP;
+
+       err = phy_ethtool_set_wol(mp->phy, wol);
+       /* Given that mv643xx_eth works without the marvell-specific PHY driver,
+        * this debugging hint is useful to have.
+        */
+       if (err == -EOPNOTSUPP)
+               netdev_info(dev, "The PHY does not support set_wol, was CONFIG_MARVELL_PHY enabled?\n");
+       return err;
+}
+
 static int
 mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
@@ -1668,7 +1502,6 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *dev,
 
        mv643xx_eth_get_stats(dev);
        mib_counters_update(mp);
-       mv643xx_eth_grab_lro_stats(mp);
 
        for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) {
                const struct mv643xx_eth_stats *stat;
@@ -1708,6 +1541,8 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops = {
        .get_ethtool_stats      = mv643xx_eth_get_ethtool_stats,
        .get_sset_count         = mv643xx_eth_get_sset_count,
        .get_ts_info            = ethtool_op_get_ts_info,
+       .get_wol                = mv643xx_eth_get_wol,
+       .set_wol                = mv643xx_eth_set_wol,
 };
 
 
@@ -1939,19 +1774,6 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index)
                                        nexti * sizeof(struct rx_desc);
        }
 
-       rxq->lro_mgr.dev = mp->dev;
-       memset(&rxq->lro_mgr.stats, 0, sizeof(rxq->lro_mgr.stats));
-       rxq->lro_mgr.features = LRO_F_NAPI;
-       rxq->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY;
-       rxq->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
-       rxq->lro_mgr.max_desc = ARRAY_SIZE(rxq->lro_arr);
-       rxq->lro_mgr.max_aggr = 32;
-       rxq->lro_mgr.frag_align_pad = 0;
-       rxq->lro_mgr.lro_arr = rxq->lro_arr;
-       rxq->lro_mgr.get_skb_header = mv643xx_get_skb_header;
-
-       memset(&rxq->lro_arr, 0, sizeof(rxq->lro_arr));
-
        return 0;
 
 
@@ -2635,66 +2457,26 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
        struct mv643xx_eth_shared_private *msp;
        const struct mbus_dram_target_info *dram;
        struct resource *res;
-       int ret;
 
        if (!mv643xx_eth_version_printed++)
                pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n",
                          mv643xx_eth_driver_version);
 
-       ret = -EINVAL;
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL)
-               goto out;
+               return -EINVAL;
 
-       ret = -ENOMEM;
-       msp = kzalloc(sizeof(*msp), GFP_KERNEL);
+       msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL);
        if (msp == NULL)
-               goto out;
+               return -ENOMEM;
 
        msp->base = ioremap(res->start, resource_size(res));
        if (msp->base == NULL)
-               goto out_free;
-
-       /*
-        * Set up and register SMI bus.
-        */
-       if (pd == NULL || pd->shared_smi == NULL) {
-               msp->smi_bus = mdiobus_alloc();
-               if (msp->smi_bus == NULL)
-                       goto out_unmap;
-
-               msp->smi_bus->priv = msp;
-               msp->smi_bus->name = "mv643xx_eth smi";
-               msp->smi_bus->read = smi_bus_read;
-               msp->smi_bus->write = smi_bus_write,
-               snprintf(msp->smi_bus->id, MII_BUS_ID_SIZE, "%s-%d",
-                       pdev->name, pdev->id);
-               msp->smi_bus->parent = &pdev->dev;
-               msp->smi_bus->phy_mask = 0xffffffff;
-               if (mdiobus_register(msp->smi_bus) < 0)
-                       goto out_free_mii_bus;
-               msp->smi = msp;
-       } else {
-               msp->smi = platform_get_drvdata(pd->shared_smi);
-       }
-
-       msp->err_interrupt = NO_IRQ;
-       init_waitqueue_head(&msp->smi_busy_wait);
+               return -ENOMEM;
 
-       /*
-        * Check whether the error interrupt is hooked up.
-        */
-       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (res != NULL) {
-               int err;
-
-               err = request_irq(res->start, mv643xx_eth_err_irq,
-                                 IRQF_SHARED, "mv643xx_eth", msp);
-               if (!err) {
-                       writel(ERR_INT_SMI_DONE, msp->base + ERR_INT_MASK);
-                       msp->err_interrupt = res->start;
-               }
-       }
+       msp->clk = devm_clk_get(&pdev->dev, NULL);
+       if (!IS_ERR(msp->clk))
+               clk_prepare_enable(msp->clk);
 
        /*
         * (Re-)program MBUS remapping windows if we are asked to.
@@ -2710,30 +2492,15 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, msp);
 
        return 0;
-
-out_free_mii_bus:
-       mdiobus_free(msp->smi_bus);
-out_unmap:
-       iounmap(msp->base);
-out_free:
-       kfree(msp);
-out:
-       return ret;
 }
 
 static int mv643xx_eth_shared_remove(struct platform_device *pdev)
 {
        struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
-       struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
 
-       if (pd == NULL || pd->shared_smi == NULL) {
-               mdiobus_unregister(msp->smi_bus);
-               mdiobus_free(msp->smi_bus);
-       }
-       if (msp->err_interrupt != NO_IRQ)
-               free_irq(msp->err_interrupt, msp);
        iounmap(msp->base);
-       kfree(msp);
+       if (!IS_ERR(msp->clk))
+               clk_disable_unprepare(msp->clk);
 
        return 0;
 }
@@ -2794,14 +2561,21 @@ static void set_params(struct mv643xx_eth_private *mp,
        mp->txq_count = pd->tx_queue_count ? : 1;
 }
 
+static void mv643xx_eth_adjust_link(struct net_device *dev)
+{
+       struct mv643xx_eth_private *mp = netdev_priv(dev);
+
+       mv643xx_adjust_pscr(mp);
+}
+
 static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
                                   int phy_addr)
 {
-       struct mii_bus *bus = mp->shared->smi->smi_bus;
        struct phy_device *phydev;
        int start;
        int num;
        int i;
+       char phy_id[MII_BUS_ID_SIZE + 3];
 
        if (phy_addr == MV643XX_ETH_PHY_ADDR_DEFAULT) {
                start = phy_addr_get(mp) & 0x1f;
@@ -2811,17 +2585,19 @@ static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
                num = 1;
        }
 
-       phydev = NULL;
+       /* Attempt to connect to the PHY using orion-mdio */
+       phydev = ERR_PTR(-ENODEV);
        for (i = 0; i < num; i++) {
                int addr = (start + i) & 0x1f;
 
-               if (bus->phy_map[addr] == NULL)
-                       mdiobus_scan(bus, addr);
+               snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT,
+                               "orion-mdio-mii", addr);
 
-               if (phydev == NULL) {
-                       phydev = bus->phy_map[addr];
-                       if (phydev != NULL)
-                               phy_addr_set(mp, addr);
+               phydev = phy_connect(mp->dev, phy_id, mv643xx_eth_adjust_link,
+                               PHY_INTERFACE_MODE_GMII);
+               if (!IS_ERR(phydev)) {
+                       phy_addr_set(mp, addr);
+                       break;
                }
        }
 
@@ -2834,8 +2610,6 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex)
 
        phy_reset(mp);
 
-       phy_attach(mp->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_GMII);
-
        if (speed == 0) {
                phy->autoneg = AUTONEG_ENABLE;
                phy->speed = 0;
@@ -2932,22 +2706,27 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
         * it to override the default.
         */
        mp->t_clk = 133000000;
-#if defined(CONFIG_HAVE_CLK)
-       mp->clk = clk_get(&pdev->dev, (pdev->id ? "1" : "0"));
+       mp->clk = devm_clk_get(&pdev->dev, NULL);
        if (!IS_ERR(mp->clk)) {
                clk_prepare_enable(mp->clk);
                mp->t_clk = clk_get_rate(mp->clk);
        }
-#endif
+
        set_params(mp, pd);
        netif_set_real_num_tx_queues(dev, mp->txq_count);
        netif_set_real_num_rx_queues(dev, mp->rxq_count);
 
-       if (pd->phy_addr != MV643XX_ETH_PHY_NONE)
+       if (pd->phy_addr != MV643XX_ETH_PHY_NONE) {
                mp->phy = phy_scan(mp, pd->phy_addr);
 
-       if (mp->phy != NULL)
+               if (IS_ERR(mp->phy)) {
+                       err = PTR_ERR(mp->phy);
+                       if (err == -ENODEV)
+                               err = -EPROBE_DEFER;
+                       goto out;
+               }
                phy_init(mp, pd->speed, pd->duplex);
+       }
 
        SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops);
 
@@ -2982,8 +2761,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        dev->watchdog_timeo = 2 * HZ;
        dev->base_addr = 0;
 
-       dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
-               NETIF_F_RXCSUM | NETIF_F_LRO;
+       dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
        dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
        dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM;
 
@@ -3014,12 +2792,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        return 0;
 
 out:
-#if defined(CONFIG_HAVE_CLK)
-       if (!IS_ERR(mp->clk)) {
+       if (!IS_ERR(mp->clk))
                clk_disable_unprepare(mp->clk);
-               clk_put(mp->clk);
-       }
-#endif
        free_netdev(dev);
 
        return err;
@@ -3034,12 +2808,8 @@ static int mv643xx_eth_remove(struct platform_device *pdev)
                phy_detach(mp->phy);
        cancel_work_sync(&mp->tx_timeout_task);
 
-#if defined(CONFIG_HAVE_CLK)
-       if (!IS_ERR(mp->clk)) {
+       if (!IS_ERR(mp->clk))
                clk_disable_unprepare(mp->clk);
-               clk_put(mp->clk);
-       }
-#endif
 
        free_netdev(mp->dev);
 
index 77b7c80262f4501867aea6b2be8520fc60f34a0d..e2f6626603138e7e7d4c7cc01e4853e83a0920a9 100644 (file)
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/phy.h>
-#include <linux/of_address.h>
-#include <linux/of_mdio.h>
+#include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/of_mdio.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
 
 #define MVMDIO_SMI_DATA_SHIFT              0
 #define MVMDIO_SMI_PHY_ADDR_SHIFT          16
 #define MVMDIO_SMI_WRITE_OPERATION         0
 #define MVMDIO_SMI_READ_VALID              BIT(27)
 #define MVMDIO_SMI_BUSY                    BIT(28)
+#define MVMDIO_ERR_INT_CAUSE              0x007C
+#define  MVMDIO_ERR_INT_SMI_DONE          0x00000010
+#define MVMDIO_ERR_INT_MASK               0x0080
 
 struct orion_mdio_dev {
        struct mutex lock;
-       void __iomem *smireg;
+       void __iomem *regs;
+       struct clk *clk;
+       /*
+        * If we have access to the error interrupt pin (which is
+        * somewhat misnamed as it not only reflects internal errors
+        * but also reflects SMI completion), use that to wait for
+        * SMI access completion instead of polling the SMI busy bit.
+        */
+       int err_interrupt;
+       wait_queue_head_t smi_busy_wait;
 };
 
+static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev)
+{
+       return !(readl(dev->regs) & MVMDIO_SMI_BUSY);
+}
+
 /* Wait for the SMI unit to be ready for another operation
  */
 static int orion_mdio_wait_ready(struct mii_bus *bus)
 {
        struct orion_mdio_dev *dev = bus->priv;
        int count;
-       u32 val;
 
-       count = 0;
-       while (1) {
-               val = readl(dev->smireg);
-               if (!(val & MVMDIO_SMI_BUSY))
-                       break;
+       if (dev->err_interrupt <= 0) {
+               count = 0;
+               while (1) {
+                       if (orion_mdio_smi_is_done(dev))
+                               break;
 
-               if (count > 100) {
-                       dev_err(bus->parent, "Timeout: SMI busy for too long\n");
-                       return -ETIMEDOUT;
-               }
+                       if (count > 100) {
+                               dev_err(bus->parent,
+                                       "Timeout: SMI busy for too long\n");
+                               return -ETIMEDOUT;
+                       }
 
-               udelay(10);
-               count++;
+                       udelay(10);
+                       count++;
+               }
+       } else {
+               if (!orion_mdio_smi_is_done(dev)) {
+                       wait_event_timeout(dev->smi_busy_wait,
+                               orion_mdio_smi_is_done(dev),
+                               msecs_to_jiffies(100));
+                       if (!orion_mdio_smi_is_done(dev))
+                               return -ETIMEDOUT;
+               }
        }
 
        return 0;
@@ -87,12 +117,12 @@ static int orion_mdio_read(struct mii_bus *bus, int mii_id,
        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
                MVMDIO_SMI_READ_OPERATION),
-              dev->smireg);
+              dev->regs);
 
        /* Wait for the value to become available */
        count = 0;
        while (1) {
-               val = readl(dev->smireg);
+               val = readl(dev->regs);
                if (val & MVMDIO_SMI_READ_VALID)
                        break;
 
@@ -129,7 +159,7 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id,
                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
                MVMDIO_SMI_WRITE_OPERATION            |
                (value << MVMDIO_SMI_DATA_SHIFT)),
-              dev->smireg);
+              dev->regs);
 
        mutex_unlock(&dev->lock);
 
@@ -141,13 +171,34 @@ static int orion_mdio_reset(struct mii_bus *bus)
        return 0;
 }
 
+static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id)
+{
+       struct orion_mdio_dev *dev = dev_id;
+
+       if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) &
+                       MVMDIO_ERR_INT_SMI_DONE) {
+               writel(~MVMDIO_ERR_INT_SMI_DONE,
+                               dev->regs + MVMDIO_ERR_INT_CAUSE);
+               wake_up(&dev->smi_busy_wait);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
 static int orion_mdio_probe(struct platform_device *pdev)
 {
-       struct device_node *np = pdev->dev.of_node;
+       struct resource *r;
        struct mii_bus *bus;
        struct orion_mdio_dev *dev;
        int i, ret;
 
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "No SMI register address given\n");
+               return -ENODEV;
+       }
+
        bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
        if (!bus) {
                dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
@@ -172,36 +223,66 @@ static int orion_mdio_probe(struct platform_device *pdev)
                bus->irq[i] = PHY_POLL;
 
        dev = bus->priv;
-       dev->smireg = of_iomap(pdev->dev.of_node, 0);
-       if (!dev->smireg) {
-               dev_err(&pdev->dev, "No SMI register address given in DT\n");
-               kfree(bus->irq);
-               mdiobus_free(bus);
-               return -ENODEV;
+       dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+       if (!dev->regs) {
+               dev_err(&pdev->dev, "Unable to remap SMI register\n");
+               ret = -ENODEV;
+               goto out_mdio;
+       }
+
+       init_waitqueue_head(&dev->smi_busy_wait);
+
+       dev->clk = devm_clk_get(&pdev->dev, NULL);
+       if (!IS_ERR(dev->clk))
+               clk_prepare_enable(dev->clk);
+
+       dev->err_interrupt = platform_get_irq(pdev, 0);
+       if (dev->err_interrupt != -ENXIO) {
+               ret = devm_request_irq(&pdev->dev, dev->err_interrupt,
+                                       orion_mdio_err_irq,
+                                       IRQF_SHARED, pdev->name, dev);
+               if (ret)
+                       goto out_mdio;
+
+               writel(MVMDIO_ERR_INT_SMI_DONE,
+                       dev->regs + MVMDIO_ERR_INT_MASK);
        }
 
        mutex_init(&dev->lock);
 
-       ret = of_mdiobus_register(bus, np);
+       if (pdev->dev.of_node)
+               ret = of_mdiobus_register(bus, pdev->dev.of_node);
+       else
+               ret = mdiobus_register(bus);
        if (ret < 0) {
                dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
-               iounmap(dev->smireg);
-               kfree(bus->irq);
-               mdiobus_free(bus);
-               return ret;
+               goto out_mdio;
        }
 
        platform_set_drvdata(pdev, bus);
 
        return 0;
+
+out_mdio:
+       if (!IS_ERR(dev->clk))
+               clk_disable_unprepare(dev->clk);
+       kfree(bus->irq);
+       mdiobus_free(bus);
+       return ret;
 }
 
 static int orion_mdio_remove(struct platform_device *pdev)
 {
        struct mii_bus *bus = platform_get_drvdata(pdev);
+       struct orion_mdio_dev *dev = bus->priv;
+
+       writel(0, dev->regs + MVMDIO_ERR_INT_MASK);
        mdiobus_unregister(bus);
        kfree(bus->irq);
        mdiobus_free(bus);
+       if (!IS_ERR(dev->clk))
+               clk_disable_unprepare(dev->clk);
+
        return 0;
 }
 
@@ -225,3 +306,4 @@ module_platform_driver(orion_mdio_driver);
 MODULE_DESCRIPTION("Marvell MDIO interface driver");
 MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:orion-mdio");
index a47a097c21e13b1373fb6fa896374d8d6ffcd04c..c96678555233c4afc1336c012c17c3b0da943060 100644 (file)
@@ -1969,13 +1969,8 @@ static int mvneta_rxq_init(struct mvneta_port *pp,
        rxq->descs = dma_alloc_coherent(pp->dev->dev.parent,
                                        rxq->size * MVNETA_DESC_ALIGNED_SIZE,
                                        &rxq->descs_phys, GFP_KERNEL);
-       if (rxq->descs == NULL) {
-               netdev_err(pp->dev,
-                          "rxq=%d: Can't allocate %d bytes for %d RX descr\n",
-                          rxq->id, rxq->size * MVNETA_DESC_ALIGNED_SIZE,
-                          rxq->size);
+       if (rxq->descs == NULL)
                return -ENOMEM;
-       }
 
        BUG_ON(rxq->descs !=
               PTR_ALIGN(rxq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
@@ -2029,13 +2024,8 @@ static int mvneta_txq_init(struct mvneta_port *pp,
        txq->descs = dma_alloc_coherent(pp->dev->dev.parent,
                                        txq->size * MVNETA_DESC_ALIGNED_SIZE,
                                        &txq->descs_phys, GFP_KERNEL);
-       if (txq->descs == NULL) {
-               netdev_err(pp->dev,
-                          "txQ=%d: Can't allocate %d bytes for %d TX descr\n",
-                          txq->id, txq->size * MVNETA_DESC_ALIGNED_SIZE,
-                          txq->size);
+       if (txq->descs == NULL)
                return -ENOMEM;
-       }
 
        /* Make sure descriptor address is cache line size aligned  */
        BUG_ON(txq->descs !=
index 037ed866c22ffa7cfdd57b54fb45216a0a3dd729..339bb323cb0c99c91bf2741cbfea918e7ae4499d 100644 (file)
@@ -584,12 +584,14 @@ static int init_hash_table(struct pxa168_eth_private *pep)
         */
        if (pep->htpr == NULL) {
                pep->htpr = dma_alloc_coherent(pep->dev->dev.parent,
-                                             HASH_ADDR_TABLE_SIZE,
-                                             &pep->htpr_dma, GFP_KERNEL);
+                                              HASH_ADDR_TABLE_SIZE,
+                                              &pep->htpr_dma,
+                                              GFP_KERNEL | __GFP_ZERO);
                if (pep->htpr == NULL)
                        return -ENOMEM;
+       } else {
+               memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
        }
-       memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
        wrl(pep, HTPR, pep->htpr_dma);
        return 0;
 }
@@ -1023,13 +1025,11 @@ static int rxq_init(struct net_device *dev)
        size = pep->rx_ring_size * sizeof(struct rx_desc);
        pep->rx_desc_area_size = size;
        pep->p_rx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size,
-                                               &pep->rx_desc_dma, GFP_KERNEL);
-       if (!pep->p_rx_desc_area) {
-               printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
-                      dev->name, size);
+                                                &pep->rx_desc_dma,
+                                                GFP_KERNEL | __GFP_ZERO);
+       if (!pep->p_rx_desc_area)
                goto out;
-       }
-       memset((void *)pep->p_rx_desc_area, 0, size);
+
        /* initialize the next_desc_ptr links in the Rx descriptors ring */
        p_rx_desc = pep->p_rx_desc_area;
        for (i = 0; i < rx_desc_num; i++) {
@@ -1086,13 +1086,10 @@ static int txq_init(struct net_device *dev)
        size = pep->tx_ring_size * sizeof(struct tx_desc);
        pep->tx_desc_area_size = size;
        pep->p_tx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size,
-                                               &pep->tx_desc_dma, GFP_KERNEL);
-       if (!pep->p_tx_desc_area) {
-               printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n",
-                      dev->name, size);
+                                                &pep->tx_desc_dma,
+                                                GFP_KERNEL | __GFP_ZERO);
+       if (!pep->p_tx_desc_area)
                goto out;
-       }
-       memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
        /* Initialize the next_desc_ptr links in the Tx descriptors ring */
        p_tx_desc = pep->p_tx_desc_area;
        for (i = 0; i < tx_desc_num; i++) {
index 6a0e671fcecd6e399952056646130578191ba57d..256ae789c1431c669835879b84587926d45a2d35 100644 (file)
@@ -1421,14 +1421,14 @@ static void sky2_vlan_mode(struct net_device *dev, netdev_features_t features)
        struct sky2_hw *hw = sky2->hw;
        u16 port = sky2->port;
 
-       if (features & NETIF_F_HW_VLAN_RX)
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
                sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T),
                             RX_VLAN_STRIP_ON);
        else
                sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T),
                             RX_VLAN_STRIP_OFF);
 
-       if (features & NETIF_F_HW_VLAN_TX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_TX) {
                sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
                             TX_VLAN_TAG_ON);
 
@@ -2713,7 +2713,7 @@ static void sky2_rx_tag(struct sky2_port *sky2, u16 length)
        struct sk_buff *skb;
 
        skb = sky2->rx_ring[sky2->rx_next].skb;
-       __vlan_hwaccel_put_tag(skb, be16_to_cpu(length));
+       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), be16_to_cpu(length));
 }
 
 static void sky2_rx_hash(struct sky2_port *sky2, u32 status)
@@ -4406,7 +4406,7 @@ static int sky2_set_features(struct net_device *dev, netdev_features_t features)
        if (changed & NETIF_F_RXHASH)
                rx_set_rss(dev, features);
 
-       if (changed & (NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX))
+       if (changed & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX))
                sky2_vlan_mode(dev, features);
 
        return 0;
@@ -4793,7 +4793,8 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port,
                dev->hw_features |= NETIF_F_RXHASH;
 
        if (!(hw->flags & SKY2_HW_VLAN_BROKEN)) {
-               dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+               dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
+                                   NETIF_F_HW_VLAN_CTAG_RX;
                dev->vlan_features |= SKY2_VLAN_OFFLOADS;
        }
 
index fdc5f23d8e9f26ae9a0fe30638a4359cb22eafa3..05267d716e8687883eaedff07b875bec57181e33 100644 (file)
@@ -1837,10 +1837,8 @@ int mlx4_cmd_init(struct mlx4_dev *dev)
                priv->mfunc.vhcr = dma_alloc_coherent(&(dev->pdev->dev), PAGE_SIZE,
                                                      &priv->mfunc.vhcr_dma,
                                                      GFP_KERNEL);
-               if (!priv->mfunc.vhcr) {
-                       mlx4_err(dev, "Couldn't allocate VHCR.\n");
+               if (!priv->mfunc.vhcr)
                        goto err_hcr;
-               }
        }
 
        priv->cmd.pool = pci_pool_create("mlx4_cmd", dev->pdev,
index b799ab12a2918bb7f0c5b8bdc0d5db70ecaf4392..0f91222ea3d77dd1a2ff29c90521da77d560c9f3 100644 (file)
@@ -186,7 +186,7 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev,
 
 static u8 mlx4_en_dcbnl_getdcbx(struct net_device *dev)
 {
-       return DCB_CAP_DCBX_VER_IEEE;
+       return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
 }
 
 static u8 mlx4_en_dcbnl_setdcbx(struct net_device *dev, u8 mode)
@@ -253,3 +253,11 @@ const struct dcbnl_rtnl_ops mlx4_en_dcbnl_ops = {
        .getdcbx        = mlx4_en_dcbnl_getdcbx,
        .setdcbx        = mlx4_en_dcbnl_setdcbx,
 };
+
+const struct dcbnl_rtnl_ops mlx4_en_dcbnl_pfc_ops = {
+       .ieee_getpfc    = mlx4_en_dcbnl_ieee_getpfc,
+       .ieee_setpfc    = mlx4_en_dcbnl_ieee_setpfc,
+
+       .getdcbx        = mlx4_en_dcbnl_getdcbx,
+       .setdcbx        = mlx4_en_dcbnl_setdcbx,
+};
index 30d78f806dc3735dd3d8a4aa0c9a21a867976cd2..e7e27842d8d4ab0706be9f18ef81e7969b8faf51 100644 (file)
@@ -356,7 +356,8 @@ static void mlx4_en_filter_rfs_expire(struct mlx4_en_priv *priv)
 }
 #endif
 
-static int mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+static int mlx4_en_vlan_rx_add_vid(struct net_device *dev,
+                                  __be16 proto, u16 vid)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
@@ -381,7 +382,8 @@ static int mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
        return 0;
 }
 
-static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev,
+                                   __be16 proto, u16 vid)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
@@ -1931,79 +1933,6 @@ static int mlx4_en_set_features(struct net_device *netdev,
 
 }
 
-static int mlx4_en_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
-                          struct net_device *dev,
-                          const unsigned char *addr, u16 flags)
-{
-       struct mlx4_en_priv *priv = netdev_priv(dev);
-       struct mlx4_dev *mdev = priv->mdev->dev;
-       int err;
-
-       if (!mlx4_is_mfunc(mdev))
-               return -EOPNOTSUPP;
-
-       /* Hardware does not support aging addresses, allow only
-        * permanent addresses if ndm_state is given
-        */
-       if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) {
-               en_info(priv, "Add FDB only supports static addresses\n");
-               return -EINVAL;
-       }
-
-       if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
-               err = dev_uc_add_excl(dev, addr);
-       else if (is_multicast_ether_addr(addr))
-               err = dev_mc_add_excl(dev, addr);
-       else
-               err = -EINVAL;
-
-       /* Only return duplicate errors if NLM_F_EXCL is set */
-       if (err == -EEXIST && !(flags & NLM_F_EXCL))
-               err = 0;
-
-       return err;
-}
-
-static int mlx4_en_fdb_del(struct ndmsg *ndm,
-                          struct nlattr *tb[],
-                          struct net_device *dev,
-                          const unsigned char *addr)
-{
-       struct mlx4_en_priv *priv = netdev_priv(dev);
-       struct mlx4_dev *mdev = priv->mdev->dev;
-       int err;
-
-       if (!mlx4_is_mfunc(mdev))
-               return -EOPNOTSUPP;
-
-       if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) {
-               en_info(priv, "Del FDB only supports static addresses\n");
-               return -EINVAL;
-       }
-
-       if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
-               err = dev_uc_del(dev, addr);
-       else if (is_multicast_ether_addr(addr))
-               err = dev_mc_del(dev, addr);
-       else
-               err = -EINVAL;
-
-       return err;
-}
-
-static int mlx4_en_fdb_dump(struct sk_buff *skb,
-                           struct netlink_callback *cb,
-                           struct net_device *dev, int idx)
-{
-       struct mlx4_en_priv *priv = netdev_priv(dev);
-       struct mlx4_dev *mdev = priv->mdev->dev;
-
-       if (mlx4_is_mfunc(mdev))
-               idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
-
-       return idx;
-}
-
 static const struct net_device_ops mlx4_netdev_ops = {
        .ndo_open               = mlx4_en_open,
        .ndo_stop               = mlx4_en_close,
@@ -2025,9 +1954,6 @@ static const struct net_device_ops mlx4_netdev_ops = {
 #ifdef CONFIG_RFS_ACCEL
        .ndo_rx_flow_steer      = mlx4_en_filter_rfs,
 #endif
-       .ndo_fdb_add            = mlx4_en_fdb_add,
-       .ndo_fdb_del            = mlx4_en_fdb_del,
-       .ndo_fdb_dump           = mlx4_en_fdb_dump,
 };
 
 int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
@@ -2089,8 +2015,14 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate);
        INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats);
 #ifdef CONFIG_MLX4_EN_DCB
-       if (!mlx4_is_slave(priv->mdev->dev))
-               dev->dcbnl_ops = &mlx4_en_dcbnl_ops;
+       if (!mlx4_is_slave(priv->mdev->dev)) {
+               if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_SET_ETH_SCHED) {
+                       dev->dcbnl_ops = &mlx4_en_dcbnl_ops;
+               } else {
+                       en_info(priv, "enabling only PFC DCB ops\n");
+                       dev->dcbnl_ops = &mlx4_en_dcbnl_pfc_ops;
+               }
+       }
 #endif
 
        for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i)
@@ -2152,8 +2084,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
 
        dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_RXHASH;
        dev->features = dev->hw_features | NETIF_F_HIGHDMA |
-                       NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
-                       NETIF_F_HW_VLAN_FILTER;
+                       NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+                       NETIF_F_HW_VLAN_CTAG_FILTER;
        dev->hw_features |= NETIF_F_LOOPBACK;
 
        if (mdev->dev->caps.steering_mode ==
index c7f856308e1ac42249848bfea0780ed3a590b3b9..4006f8857cb567ee426f73ba7ac44aa763258f6d 100644 (file)
@@ -673,7 +673,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                                            cpu_to_be32(MLX4_CQE_VLAN_PRESENT_MASK)) {
                                                u16 vid = be16_to_cpu(cqe->sl_vid);
 
-                                               __vlan_hwaccel_put_tag(gro_skb, vid);
+                                               __vlan_hwaccel_put_tag(gro_skb, htons(ETH_P_8021Q), vid);
                                        }
 
                                        if (dev->features & NETIF_F_RXHASH)
@@ -716,7 +716,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
 
                if (be32_to_cpu(cqe->vlan_my_qpn) &
                    MLX4_CQE_VLAN_PRESENT_MASK)
-                       __vlan_hwaccel_put_tag(skb, be16_to_cpu(cqe->sl_vid));
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), be16_to_cpu(cqe->sl_vid));
 
                /* Push it up the stack */
                netif_receive_skb(skb);
index 3488c6d9e6b53cb496f43d84d224342802c2e45f..2448f0d669e6cadd19ca06b5d820097ca6cf80fe 100644 (file)
@@ -58,10 +58,9 @@ static int mlx4_en_test_loopback_xmit(struct mlx4_en_priv *priv)
 
        /* build the pkt before xmit */
        skb = netdev_alloc_skb(priv->dev, MLX4_LOOPBACK_TEST_PAYLOAD + ETH_HLEN + NET_IP_ALIGN);
-       if (!skb) {
-               en_err(priv, "-LOOPBACK_TEST_XMIT- failed to create skb for xmit\n");
+       if (!skb)
                return -ENOMEM;
-       }
+
        skb_reserve(skb, NET_IP_ALIGN);
 
        ethh = (struct ethhdr *)skb_put(skb, sizeof(struct ethhdr));
index f6245579962d5f0c1a81e31f757a52d16c7cd3de..ab470d991ade57cfdccc2fa593f88b927ec4054a 100644 (file)
@@ -91,7 +91,7 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags)
                [ 8] = "P_Key violation counter",
                [ 9] = "Q_Key violation counter",
                [10] = "VMM",
-               [12] = "DPDP",
+               [12] = "Dual Port Different Protocol (DPDP) support",
                [15] = "Big LSO headers",
                [16] = "MW support",
                [17] = "APM support",
@@ -109,6 +109,8 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags)
                [41] = "Unicast VEP steering support",
                [42] = "Multicast VEP steering support",
                [48] = "Counters support",
+               [53] = "Port ETS Scheduler support",
+               [55] = "Port link type sensing support",
                [59] = "Port management change event support",
                [61] = "64 byte EQE support",
                [62] = "64 byte CQE support",
index 52685524708d3012f09b0ffa586cc82a1d689055..ffc78d2cb0cf648341e26733fac5e6508388fd3a 100644 (file)
@@ -1125,28 +1125,11 @@ static int mlx4_QP_ATTACH(struct mlx4_dev *dev, struct mlx4_qp *qp,
        return err;
 }
 
-int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
-                         u8 port, int block_mcast_loopback,
-                         enum mlx4_protocol prot, u64 *reg_id)
+int mlx4_trans_to_dmfs_attach(struct mlx4_dev *dev, struct mlx4_qp *qp,
+                             u8 gid[16], u8 port,
+                             int block_mcast_loopback,
+                             enum mlx4_protocol prot, u64 *reg_id)
 {
-
-       switch (dev->caps.steering_mode) {
-       case MLX4_STEERING_MODE_A0:
-               if (prot == MLX4_PROT_ETH)
-                       return 0;
-
-       case MLX4_STEERING_MODE_B0:
-               if (prot == MLX4_PROT_ETH)
-                       gid[7] |= (MLX4_MC_STEER << 1);
-
-               if (mlx4_is_mfunc(dev))
-                       return mlx4_QP_ATTACH(dev, qp, gid, 1,
-                                             block_mcast_loopback, prot);
-               return mlx4_qp_attach_common(dev, qp, gid,
-                                            block_mcast_loopback, prot,
-                                            MLX4_MC_STEER);
-
-       case MLX4_STEERING_MODE_DEVICE_MANAGED: {
                struct mlx4_spec_list spec = { {NULL} };
                __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16);
 
@@ -1180,8 +1163,32 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
                list_add_tail(&spec.list, &rule.list);
 
                return mlx4_flow_attach(dev, &rule, reg_id);
-       }
+}
 
+int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
+                         u8 port, int block_mcast_loopback,
+                         enum mlx4_protocol prot, u64 *reg_id)
+{
+       switch (dev->caps.steering_mode) {
+       case MLX4_STEERING_MODE_A0:
+               if (prot == MLX4_PROT_ETH)
+                       return 0;
+
+       case MLX4_STEERING_MODE_B0:
+               if (prot == MLX4_PROT_ETH)
+                       gid[7] |= (MLX4_MC_STEER << 1);
+
+               if (mlx4_is_mfunc(dev))
+                       return mlx4_QP_ATTACH(dev, qp, gid, 1,
+                                             block_mcast_loopback, prot);
+               return mlx4_qp_attach_common(dev, qp, gid,
+                                            block_mcast_loopback, prot,
+                                            MLX4_MC_STEER);
+
+       case MLX4_STEERING_MODE_DEVICE_MANAGED:
+               return mlx4_trans_to_dmfs_attach(dev, qp, gid, port,
+                                                block_mcast_loopback,
+                                                prot, reg_id);
        default:
                return -EINVAL;
        }
index d738454116a088db7a098c88f2ef5fc9b7eb1698..252f4ba7f32c34999188bec1e3ed4eadc6dc594f 100644 (file)
@@ -1190,6 +1190,10 @@ int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
 int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
                          int block_mcast_loopback, enum mlx4_protocol prot,
                          enum mlx4_steer_type steer);
+int mlx4_trans_to_dmfs_attach(struct mlx4_dev *dev, struct mlx4_qp *qp,
+                             u8 gid[16], u8 port,
+                             int block_mcast_loopback,
+                             enum mlx4_protocol prot, u64 *reg_id);
 int mlx4_SET_MCAST_FLTR_wrapper(struct mlx4_dev *dev, int slave,
                                struct mlx4_vhcr *vhcr,
                                struct mlx4_cmd_mailbox *inbox,
index f710b7ce0dcbbf9ef740d0661add1bb744921b9f..d4cb5d3b28a2a82d19ec08f3ab8cba80b1782dc5 100644 (file)
@@ -624,6 +624,7 @@ int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port);
 
 #ifdef CONFIG_MLX4_EN_DCB
 extern const struct dcbnl_rtnl_ops mlx4_en_dcbnl_ops;
+extern const struct dcbnl_rtnl_ops mlx4_en_dcbnl_pfc_ops;
 #endif
 
 int mlx4_en_setup_tc(struct net_device *dev, u8 up);
index 10c57c86388baed3626cc803fc8acadf605ca855..4b6aad39e72cd97d09d8deff9f35390684a700bf 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <linux/errno.h>
 #include <linux/if_ether.h>
+#include <linux/if_vlan.h>
 #include <linux/export.h>
 
 #include <linux/mlx4/cmd.h>
@@ -517,7 +518,8 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
                        /* Mtu is configured as the max MTU among all the
                         * the functions on the port. */
                        mtu = be16_to_cpu(gen_context->mtu);
-                       mtu = min_t(int, mtu, dev->caps.eth_mtu_cap[port]);
+                       mtu = min_t(int, mtu, dev->caps.eth_mtu_cap[port] +
+                                   ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN);
                        prev_mtu = slave_st->mtu[port];
                        slave_st->mtu[port] = mtu;
                        if (mtu > master->max_mtu[port])
index 1391b52f443aa5489ec31ee7e155b087f0372b0c..f2d64435d8ef8f8de55b58fd1e2d2b97bdab16c1 100644 (file)
@@ -75,6 +75,7 @@ struct res_gid {
        u8                      gid[16];
        enum mlx4_protocol      prot;
        enum mlx4_steer_type    steer;
+       u64                     reg_id;
 };
 
 enum res_qp_states {
@@ -2934,7 +2935,7 @@ static struct res_gid *find_gid(struct mlx4_dev *dev, int slave,
 
 static int add_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp,
                       u8 *gid, enum mlx4_protocol prot,
-                      enum mlx4_steer_type steer)
+                      enum mlx4_steer_type steer, u64 reg_id)
 {
        struct res_gid *res;
        int err;
@@ -2951,6 +2952,7 @@ static int add_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp,
                memcpy(res->gid, gid, 16);
                res->prot = prot;
                res->steer = steer;
+               res->reg_id = reg_id;
                list_add_tail(&res->list, &rqp->mcg_list);
                err = 0;
        }
@@ -2961,7 +2963,7 @@ static int add_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp,
 
 static int rem_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp,
                       u8 *gid, enum mlx4_protocol prot,
-                      enum mlx4_steer_type steer)
+                      enum mlx4_steer_type steer, u64 *reg_id)
 {
        struct res_gid *res;
        int err;
@@ -2971,6 +2973,7 @@ static int rem_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp,
        if (!res || res->prot != prot || res->steer != steer)
                err = -EINVAL;
        else {
+               *reg_id = res->reg_id;
                list_del(&res->list);
                kfree(res);
                err = 0;
@@ -2980,6 +2983,37 @@ static int rem_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp,
        return err;
 }
 
+static int qp_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
+                    int block_loopback, enum mlx4_protocol prot,
+                    enum mlx4_steer_type type, u64 *reg_id)
+{
+       switch (dev->caps.steering_mode) {
+       case MLX4_STEERING_MODE_DEVICE_MANAGED:
+               return mlx4_trans_to_dmfs_attach(dev, qp, gid, gid[5],
+                                               block_loopback, prot,
+                                               reg_id);
+       case MLX4_STEERING_MODE_B0:
+               return mlx4_qp_attach_common(dev, qp, gid,
+                                           block_loopback, prot, type);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int qp_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
+                    enum mlx4_protocol prot, enum mlx4_steer_type type,
+                    u64 reg_id)
+{
+       switch (dev->caps.steering_mode) {
+       case MLX4_STEERING_MODE_DEVICE_MANAGED:
+               return mlx4_flow_detach(dev, reg_id);
+       case MLX4_STEERING_MODE_B0:
+               return mlx4_qp_detach_common(dev, qp, gid, prot, type);
+       default:
+               return -EINVAL;
+       }
+}
+
 int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
                               struct mlx4_vhcr *vhcr,
                               struct mlx4_cmd_mailbox *inbox,
@@ -2992,14 +3026,12 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
        int err;
        int qpn;
        struct res_qp *rqp;
+       u64 reg_id = 0;
        int attach = vhcr->op_modifier;
        int block_loopback = vhcr->in_modifier >> 31;
        u8 steer_type_mask = 2;
        enum mlx4_steer_type type = (gid[7] & steer_type_mask) >> 1;
 
-       if (dev->caps.steering_mode != MLX4_STEERING_MODE_B0)
-               return -EINVAL;
-
        qpn = vhcr->in_modifier & 0xffffff;
        err = get_res(dev, slave, qpn, RES_QP, &rqp);
        if (err)
@@ -3007,30 +3039,32 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
 
        qp.qpn = qpn;
        if (attach) {
-               err = add_mcg_res(dev, slave, rqp, gid, prot, type);
-               if (err)
+               err = qp_attach(dev, &qp, gid, block_loopback, prot,
+                               type, &reg_id);
+               if (err) {
+                       pr_err("Fail to attach rule to qp 0x%x\n", qpn);
                        goto ex_put;
-
-               err = mlx4_qp_attach_common(dev, &qp, gid,
-                                           block_loopback, prot, type);
+               }
+               err = add_mcg_res(dev, slave, rqp, gid, prot, type, reg_id);
                if (err)
-                       goto ex_rem;
+                       goto ex_detach;
        } else {
-               err = rem_mcg_res(dev, slave, rqp, gid, prot, type);
+               err = rem_mcg_res(dev, slave, rqp, gid, prot, type, &reg_id);
                if (err)
                        goto ex_put;
-               err = mlx4_qp_detach_common(dev, &qp, gid, prot, type);
-       }
 
+               err = qp_detach(dev, &qp, gid, prot, type, reg_id);
+               if (err)
+                       pr_err("Fail to detach rule from qp 0x%x reg_id = 0x%llx\n",
+                              qpn, reg_id);
+       }
        put_res(dev, slave, qpn, RES_QP);
-       return 0;
+       return err;
 
-ex_rem:
-       /* ignore error return below, already in error */
-       (void) rem_mcg_res(dev, slave, rqp, gid, prot, type);
+ex_detach:
+       qp_detach(dev, &qp, gid, prot, type, reg_id);
 ex_put:
        put_res(dev, slave, qpn, RES_QP);
-
        return err;
 }
 
@@ -3266,9 +3300,16 @@ static void detach_qp(struct mlx4_dev *dev, int slave, struct res_qp *rqp)
        struct mlx4_qp qp; /* dummy for calling attach/detach */
 
        list_for_each_entry_safe(rgid, tmp, &rqp->mcg_list, list) {
-               qp.qpn = rqp->local_qpn;
-               (void) mlx4_qp_detach_common(dev, &qp, rgid->gid, rgid->prot,
-                                            rgid->steer);
+               switch (dev->caps.steering_mode) {
+               case MLX4_STEERING_MODE_DEVICE_MANAGED:
+                       mlx4_flow_detach(dev, rgid->reg_id);
+                       break;
+               case MLX4_STEERING_MODE_B0:
+                       qp.qpn = rqp->local_qpn;
+                       (void) mlx4_qp_detach_common(dev, &qp, rgid->gid,
+                                                    rgid->prot, rgid->steer);
+                       break;
+               }
                list_del(&rgid->list);
                kfree(rgid);
        }
index 07a6ebc47c929bdf6aabdfedf23fd20901226921..b6c60fdef4ff6d1d5498e1bfb688b4edfa7ad8bd 100644 (file)
@@ -1622,25 +1622,7 @@ static struct platform_driver ks8695_driver = {
        .resume         = ks8695_drv_resume,
 };
 
-/* Module interface */
-
-static int __init
-ks8695_init(void)
-{
-       printk(KERN_INFO "%s Ethernet driver, V%s\n",
-              MODULENAME, MODULEVERSION);
-
-       return platform_driver_register(&ks8695_driver);
-}
-
-static void __exit
-ks8695_cleanup(void)
-{
-       platform_driver_unregister(&ks8695_driver);
-}
-
-module_init(ks8695_init);
-module_exit(ks8695_cleanup);
+module_platform_driver(ks8695_driver);
 
 MODULE_AUTHOR("Simtec Electronics");
 MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
index 8fb481252e2cb653f45f7543f7e41c70cef9401e..727b546a9eb844c909dd8cd145aacfab014c9f99 100644 (file)
@@ -1364,37 +1364,37 @@ static int ks8851_read_selftest(struct ks8851_net *ks)
 
 /* driver bus management functions */
 
-#ifdef CONFIG_PM
-static int ks8851_suspend(struct spi_device *spi, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+
+static int ks8851_suspend(struct device *dev)
 {
-       struct ks8851_net *ks = dev_get_drvdata(&spi->dev);
-       struct net_device *dev = ks->netdev;
+       struct ks8851_net *ks = dev_get_drvdata(dev);
+       struct net_device *netdev = ks->netdev;
 
-       if (netif_running(dev)) {
-               netif_device_detach(dev);
-               ks8851_net_stop(dev);
+       if (netif_running(netdev)) {
+               netif_device_detach(netdev);
+               ks8851_net_stop(netdev);
        }
 
        return 0;
 }
 
-static int ks8851_resume(struct spi_device *spi)
+static int ks8851_resume(struct device *dev)
 {
-       struct ks8851_net *ks = dev_get_drvdata(&spi->dev);
-       struct net_device *dev = ks->netdev;
+       struct ks8851_net *ks = dev_get_drvdata(dev);
+       struct net_device *netdev = ks->netdev;
 
-       if (netif_running(dev)) {
-               ks8851_net_open(dev);
-               netif_device_attach(dev);
+       if (netif_running(netdev)) {
+               ks8851_net_open(netdev);
+               netif_device_attach(netdev);
        }
 
        return 0;
 }
-#else
-#define ks8851_suspend NULL
-#define ks8851_resume NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(ks8851_pm_ops, ks8851_suspend, ks8851_resume);
+
 static int ks8851_probe(struct spi_device *spi)
 {
        struct net_device *ndev;
@@ -1456,7 +1456,7 @@ static int ks8851_probe(struct spi_device *spi)
        SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops);
        SET_NETDEV_DEV(ndev, &spi->dev);
 
-       dev_set_drvdata(&spi->dev, ks);
+       spi_set_drvdata(spi, ks);
 
        ndev->if_port = IF_PORT_100BASET;
        ndev->netdev_ops = &ks8851_netdev_ops;
@@ -1516,7 +1516,7 @@ err_irq:
 
 static int ks8851_remove(struct spi_device *spi)
 {
-       struct ks8851_net *priv = dev_get_drvdata(&spi->dev);
+       struct ks8851_net *priv = spi_get_drvdata(spi);
 
        if (netif_msg_drv(priv))
                dev_info(&spi->dev, "remove\n");
@@ -1532,25 +1532,12 @@ static struct spi_driver ks8851_driver = {
        .driver = {
                .name = "ks8851",
                .owner = THIS_MODULE,
+               .pm = &ks8851_pm_ops,
        },
        .probe = ks8851_probe,
        .remove = ks8851_remove,
-       .suspend = ks8851_suspend,
-       .resume = ks8851_resume,
 };
-
-static int __init ks8851_init(void)
-{
-       return spi_register_driver(&ks8851_driver);
-}
-
-static void __exit ks8851_exit(void)
-{
-       spi_unregister_driver(&ks8851_driver);
-}
-
-module_init(ks8851_init);
-module_exit(ks8851_exit);
+module_spi_driver(ks8851_driver);
 
 MODULE_DESCRIPTION("KS8851 Network driver");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
index a343066f7b436d1526b0c9a58fe3beb67131c6ea..ddaf138ce0d4aa79344584f938dad12047a73c97 100644 (file)
@@ -792,20 +792,35 @@ static void ks_rcv(struct ks_net *ks, struct net_device *netdev)
 
        frame_hdr = ks->frame_head_info;
        while (ks->frame_cnt--) {
+               if (unlikely(!(frame_hdr->sts & RXFSHR_RXFV) ||
+                            frame_hdr->len >= RX_BUF_SIZE ||
+                            frame_hdr->len <= 0)) {
+
+                       /* discard an invalid packet */
+                       ks_wrreg16(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_RRXEF));
+                       netdev->stats.rx_dropped++;
+                       if (!(frame_hdr->sts & RXFSHR_RXFV))
+                               netdev->stats.rx_frame_errors++;
+                       else
+                               netdev->stats.rx_length_errors++;
+                       frame_hdr++;
+                       continue;
+               }
+
                skb = netdev_alloc_skb(netdev, frame_hdr->len + 16);
-               if (likely(skb && (frame_hdr->sts & RXFSHR_RXFV) &&
-                       (frame_hdr->len < RX_BUF_SIZE) && frame_hdr->len)) {
+               if (likely(skb)) {
                        skb_reserve(skb, 2);
                        /* read data block including CRC 4 bytes */
                        ks_read_qmu(ks, (u16 *)skb->data, frame_hdr->len);
-                       skb_put(skb, frame_hdr->len);
+                       skb_put(skb, frame_hdr->len - 4);
                        skb->protocol = eth_type_trans(skb, netdev);
                        netif_rx(skb);
+                       /* exclude CRC size */
+                       netdev->stats.rx_bytes += frame_hdr->len - 4;
+                       netdev->stats.rx_packets++;
                } else {
-                       pr_err("%s: err:skb alloc\n", __func__);
                        ks_wrreg16(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_RRXEF));
-                       if (skb)
-                               dev_kfree_skb_irq(skb);
+                       netdev->stats.rx_dropped++;
                }
                frame_hdr++;
        }
@@ -877,6 +892,8 @@ static irqreturn_t ks_irq(int irq, void *pw)
                ks_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK);
        }
 
+       if (unlikely(status & IRQ_RXOI))
+               ks->netdev->stats.rx_over_errors++;
        /* this should be the last in IRQ handler*/
        ks_restore_cmd_reg(ks);
        return IRQ_HANDLED;
@@ -1015,6 +1032,9 @@ static int ks_start_xmit(struct sk_buff *skb, struct net_device *netdev)
 
        if (likely(ks_tx_fifo_space(ks) >= skb->len + 12)) {
                ks_write_qmu(ks, skb->data, skb->len);
+               /* add tx statistics */
+               netdev->stats.tx_bytes += skb->len;
+               netdev->stats.tx_packets++;
                dev_kfree_skb(skb);
        } else
                retv = NETDEV_TX_BUSY;
index 5d98a9f7bfc7071b63b9796ec59166b9027972b0..c7b40aa21f22fe2a909754fe4e2a9a7b8bad5898 100644 (file)
@@ -1566,7 +1566,7 @@ static int enc28j60_probe(struct spi_device *spi)
        INIT_WORK(&priv->setrx_work, enc28j60_setrx_work_handler);
        INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler);
        INIT_WORK(&priv->restart_work, enc28j60_restart_work_handler);
-       dev_set_drvdata(&spi->dev, priv);       /* spi to priv reference */
+       spi_set_drvdata(spi, priv);     /* spi to priv reference */
        SET_NETDEV_DEV(dev, &spi->dev);
 
        if (!enc28j60_chipset_init(dev)) {
@@ -1618,7 +1618,7 @@ error_alloc:
 
 static int enc28j60_remove(struct spi_device *spi)
 {
-       struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
+       struct enc28j60_net *priv = spi_get_drvdata(spi);
 
        if (netif_msg_drv(priv))
                printk(KERN_DEBUG DRV_NAME ": remove\n");
index 4f9937e026e5f9ca57cb99c2d969f3a12cc3dae2..7be9788ed0f6fddcb43abd1b384b0424ad33cb22 100644 (file)
@@ -1281,7 +1281,8 @@ myri10ge_vlan_rx(struct net_device *dev, void *addr, struct sk_buff *skb)
        va = addr;
        va += MXGEFW_PAD;
        veh = (struct vlan_ethhdr *)va;
-       if ((dev->features & NETIF_F_HW_VLAN_RX) == NETIF_F_HW_VLAN_RX &&
+       if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) ==
+           NETIF_F_HW_VLAN_CTAG_RX &&
            veh->h_vlan_proto == htons(ETH_P_8021Q)) {
                /* fixup csum if needed */
                if (skb->ip_summed == CHECKSUM_COMPLETE) {
@@ -1289,7 +1290,7 @@ myri10ge_vlan_rx(struct net_device *dev, void *addr, struct sk_buff *skb)
                        skb->csum = csum_sub(skb->csum, vsum);
                }
                /* pop tag */
-               __vlan_hwaccel_put_tag(skb, ntohs(veh->h_vlan_TCI));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(veh->h_vlan_TCI));
                memmove(va + VLAN_HLEN, va, 2 * ETH_ALEN);
                skb->len -= VLAN_HLEN;
                skb->data_len -= VLAN_HLEN;
@@ -3592,10 +3593,9 @@ static int myri10ge_alloc_slices(struct myri10ge_priv *mgp)
                bytes = mgp->max_intr_slots * sizeof(*ss->rx_done.entry);
                ss->rx_done.entry = dma_alloc_coherent(&pdev->dev, bytes,
                                                       &ss->rx_done.bus,
-                                                      GFP_KERNEL);
+                                                      GFP_KERNEL | __GFP_ZERO);
                if (ss->rx_done.entry == NULL)
                        goto abort;
-               memset(ss->rx_done.entry, 0, bytes);
                bytes = sizeof(*ss->fw_stats);
                ss->fw_stats = dma_alloc_coherent(&pdev->dev, bytes,
                                                  &ss->fw_stats_bus,
@@ -3888,8 +3888,8 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        netdev->mtu = myri10ge_initial_mtu;
        netdev->hw_features = mgp->features | NETIF_F_RXCSUM;
 
-       /* fake NETIF_F_HW_VLAN_RX for good GRO performance */
-       netdev->hw_features |= NETIF_F_HW_VLAN_RX;
+       /* fake NETIF_F_HW_VLAN_CTAG_RX for good GRO performance */
+       netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
 
        netdev->features = netdev->hw_features;
 
index b0b36154636508e9f947c8358140f42fb7cbd1d5..c20766c2f65b91c57de2ba68ccb4d98058544059 100644 (file)
@@ -175,13 +175,13 @@ static int sonic_probe1(struct net_device *dev)
 
        /* Allocate the entire chunk of memory for the descriptors.
            Note that this cannot cross a 64K boundary. */
-       if ((lp->descriptors = dma_alloc_coherent(lp->device,
-                               SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                               &lp->descriptors_laddr, GFP_KERNEL)) == NULL) {
-               printk(KERN_ERR "%s: couldn't alloc DMA memory for descriptors.\n",
-                      dev_name(lp->device));
+       lp->descriptors = dma_alloc_coherent(lp->device,
+                                            SIZEOF_SONIC_DESC *
+                                            SONIC_BUS_SCALE(lp->dma_bitmode),
+                                            &lp->descriptors_laddr,
+                                            GFP_KERNEL);
+       if (lp->descriptors == NULL)
                goto out;
-       }
 
        /* Now set up the pointers to point to the appropriate places */
        lp->cda = lp->descriptors;
index 0ffde69c8d015e12db0e5943078b225fbfbd40e1..346a4e025c34100315b1ca2785d631d3bc5d3408 100644 (file)
@@ -202,13 +202,13 @@ static int macsonic_init(struct net_device *dev)
 
        /* Allocate the entire chunk of memory for the descriptors.
            Note that this cannot cross a 64K boundary. */
-       if ((lp->descriptors = dma_alloc_coherent(lp->device,
-                   SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                   &lp->descriptors_laddr, GFP_KERNEL)) == NULL) {
-               printk(KERN_ERR "%s: couldn't alloc DMA memory for descriptors.\n",
-                      dev_name(lp->device));
+       lp->descriptors = dma_alloc_coherent(lp->device,
+                                            SIZEOF_SONIC_DESC *
+                                            SONIC_BUS_SCALE(lp->dma_bitmode),
+                                            &lp->descriptors_laddr,
+                                            GFP_KERNEL);
+       if (lp->descriptors == NULL)
                return -ENOMEM;
-       }
 
        /* Now set up the pointers to point to the appropriate places */
        lp->cda = lp->descriptors;
index 77c070de621ee09145ea9c94dca1a8e1c0f9b831..d3b47003a5757067f47340c60e6d2dc08d299c10 100644 (file)
@@ -911,7 +911,7 @@ static void rx_irq(struct net_device *ndev)
                                unsigned short tag;
 
                                tag = ntohs(extsts & EXTSTS_VTG_MASK);
-                               __vlan_hwaccel_put_tag(skb, tag);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_IPV6), tag);
                        }
 #endif
                        rx_rc = netif_rx(skb);
@@ -2193,7 +2193,7 @@ static int ns83820_init_one(struct pci_dev *pci_dev,
 
 #ifdef NS83820_VLAN_ACCEL_SUPPORT
        /* We also support hardware vlan acceleration */
-       ndev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+       ndev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 #endif
 
        if (using_dac) {
index 46795e4034675c381bef7de9b6b01249a1514e50..1bd419dbda6dc34d39f1a4fe38ec30a9ca85cf16 100644 (file)
@@ -424,7 +424,6 @@ static void sonic_rx(struct net_device *dev)
                        /* Malloc up new buffer. */
                        new_skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2);
                        if (new_skb == NULL) {
-                               printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n", dev->name);
                                lp->stats.rx_dropped++;
                                break;
                        }
index 5e4748e855f632f198adadda71221306fe6d4bcc..c2e0256fe3dfb423d1ba9011d7035379e1dd3f47 100644 (file)
@@ -197,14 +197,12 @@ static int __init sonic_probe1(struct net_device *dev)
         *  We also allocate extra space for a pointer to allow freeing
         *  this structure later on (in xtsonic_cleanup_module()).
         */
-       lp->descriptors =
-               dma_alloc_coherent(lp->device,
-                       SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                       &lp->descriptors_laddr, GFP_KERNEL);
-
+       lp->descriptors = dma_alloc_coherent(lp->device,
+                                            SIZEOF_SONIC_DESC *
+                                            SONIC_BUS_SCALE(lp->dma_bitmode),
+                                            &lp->descriptors_laddr,
+                                            GFP_KERNEL);
        if (lp->descriptors == NULL) {
-               printk(KERN_ERR "%s: couldn't alloc DMA memory for "
-                               " descriptors.\n", dev_name(lp->device));
                err = -ENOMEM;
                goto out;
        }
index bfd887382e1962cfc5d73ef71813ad5a04084688..51b00941302c8d7e7eea3dcad6daa17c7702c65e 100644 (file)
@@ -80,6 +80,7 @@
 #include <linux/slab.h>
 #include <linux/prefetch.h>
 #include <net/tcp.h>
+#include <net/checksum.h>
 
 #include <asm/div64.h>
 #include <asm/irq.h>
@@ -7919,7 +7920,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
                NETIF_F_TSO | NETIF_F_TSO6 |
                NETIF_F_RXCSUM | NETIF_F_LRO;
        dev->features |= dev->hw_features |
-               NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+               NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
        if (sp->device_type & XFRAME_II_DEVICE) {
                dev->hw_features |= NETIF_F_UFO;
                if (ufo)
@@ -8337,16 +8338,13 @@ static void update_L3L4_header(struct s2io_nic *sp, struct lro *lro)
 {
        struct iphdr *ip = lro->iph;
        struct tcphdr *tcp = lro->tcph;
-       __sum16 nchk;
        struct swStat *swstats = &sp->mac_control.stats_info->sw_stat;
 
        DBG_PRINT(INFO_DBG, "%s: Been here...\n", __func__);
 
        /* Update L3 header */
+       csum_replace2(&ip->check, ip->tot_len, htons(lro->total_len));
        ip->tot_len = htons(lro->total_len);
-       ip->check = 0;
-       nchk = ip_fast_csum((u8 *)lro->iph, ip->ihl);
-       ip->check = nchk;
 
        /* Update L4 header */
        tcp->ack_seq = lro->tcp_ack;
@@ -8557,7 +8555,7 @@ static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag)
 
        skb->protocol = eth_type_trans(skb, dev);
        if (vlan_tag && sp->vlan_strip_flag)
-               __vlan_hwaccel_put_tag(skb, vlan_tag);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
        if (sp->config.napi)
                netif_receive_skb(skb);
        else
index 794444e09492978ffd5154e065a0a2bd5fbe846f..cbfaed5f2f8df2617a45f7f08150492c398104fa 100644 (file)
@@ -312,7 +312,7 @@ vxge_rx_complete(struct vxge_ring *ring, struct sk_buff *skb, u16 vlan,
 
        if (ext_info->vlan &&
            ring->vlan_tag_strip == VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE)
-               __vlan_hwaccel_put_tag(skb, ext_info->vlan);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ext_info->vlan);
        napi_gro_receive(ring->napi_p, skb);
 
        vxge_debug_entryexit(VXGE_TRACE,
@@ -3300,12 +3300,13 @@ static void vxge_tx_watchdog(struct net_device *dev)
 /**
  * vxge_vlan_rx_add_vid
  * @dev: net device pointer.
+ * @proto: vlan protocol
  * @vid: vid
  *
  * Add the vlan id to the devices vlan id table
  */
 static int
-vxge_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+vxge_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct vxgedev *vdev = netdev_priv(dev);
        struct vxge_vpath *vpath;
@@ -3323,14 +3324,15 @@ vxge_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 }
 
 /**
- * vxge_vlan_rx_add_vid
+ * vxge_vlan_rx_kill_vid
  * @dev: net device pointer.
+ * @proto: vlan protocol
  * @vid: vid
  *
  * Remove the vlan id from the device's vlan id table
  */
 static int
-vxge_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+vxge_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct vxgedev *vdev = netdev_priv(dev);
        struct vxge_vpath *vpath;
@@ -3415,12 +3417,12 @@ static int vxge_device_register(struct __vxge_hw_device *hldev,
        ndev->hw_features = NETIF_F_RXCSUM | NETIF_F_SG |
                NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                NETIF_F_TSO | NETIF_F_TSO6 |
-               NETIF_F_HW_VLAN_TX;
+               NETIF_F_HW_VLAN_CTAG_TX;
        if (vdev->config.rth_steering != NO_STEERING)
                ndev->hw_features |= NETIF_F_RXHASH;
 
        ndev->features |= ndev->hw_features |
-               NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
+               NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
 
 
        ndev->netdev_ops = &vxge_netdev_ops;
index 63e7af44366f4dc20841edf2d9a35d2212e36c8a..cb9e6383150043e3186bfc1c51ee8f334fb090c0 100644 (file)
@@ -152,8 +152,6 @@ static void netx_eth_receive(struct net_device *ndev)
 
        skb = netdev_alloc_skb(ndev, len);
        if (unlikely(skb == NULL)) {
-               printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
-                       ndev->name);
                ndev->stats.rx_dropped++;
                return;
        }
index 162da8975b059a3ef42ec9e2469660e02d3b01de..3df8287b7452d77b69bee0262a9f950125020b8e 100644 (file)
@@ -287,23 +287,16 @@ static int w90p910_init_desc(struct net_device *dev)
        ether = netdev_priv(dev);
        pdev = ether->pdev;
 
-       ether->tdesc = (struct tran_pdesc *)
-               dma_alloc_coherent(&pdev->dev, sizeof(struct tran_pdesc),
-                                       &ether->tdesc_phys, GFP_KERNEL);
-
-       if (!ether->tdesc) {
-               dev_err(&pdev->dev, "Failed to allocate memory for tx desc\n");
+       ether->tdesc = dma_alloc_coherent(&pdev->dev, sizeof(struct tran_pdesc),
+                                         &ether->tdesc_phys, GFP_KERNEL);
+       if (!ether->tdesc)
                return -ENOMEM;
-       }
-
-       ether->rdesc = (struct recv_pdesc *)
-               dma_alloc_coherent(&pdev->dev, sizeof(struct recv_pdesc),
-                                       &ether->rdesc_phys, GFP_KERNEL);
 
+       ether->rdesc = dma_alloc_coherent(&pdev->dev, sizeof(struct recv_pdesc),
+                                         &ether->rdesc_phys, GFP_KERNEL);
        if (!ether->rdesc) {
-               dev_err(&pdev->dev, "Failed to allocate memory for rx desc\n");
                dma_free_coherent(&pdev->dev, sizeof(struct tran_pdesc),
-                                       ether->tdesc, ether->tdesc_phys);
+                                 ether->tdesc, ether->tdesc_phys);
                return -ENOMEM;
        }
 
@@ -737,7 +730,6 @@ static void netdev_rx(struct net_device *dev)
                        data = ether->rdesc->recv_buf[ether->cur_rx];
                        skb = netdev_alloc_skb(dev, length + 2);
                        if (!skb) {
-                               dev_err(&pdev->dev, "get skb buffer error\n");
                                ether->stats.rx_dropped++;
                                return;
                        }
index 0b8de12bcbca998410f2097d27e239eb31607f83..b003fe53c8e2508e3de8f2c2a118060b57ac02ac 100644 (file)
@@ -2200,6 +2200,7 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct ring_desc *start_tx;
        struct ring_desc *prev_tx;
        struct nv_skb_map *prev_tx_ctx;
+       struct nv_skb_map *tmp_tx_ctx = NULL, *start_tx_ctx = NULL;
        unsigned long flags;
 
        /* add fragments to entries count */
@@ -2261,12 +2262,31 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
                do {
                        prev_tx = put_tx;
                        prev_tx_ctx = np->put_tx_ctx;
+                       if (!start_tx_ctx)
+                               start_tx_ctx = tmp_tx_ctx = np->put_tx_ctx;
+
                        bcnt = (frag_size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : frag_size;
                        np->put_tx_ctx->dma = skb_frag_dma_map(
                                                        &np->pci_dev->dev,
                                                        frag, offset,
                                                        bcnt,
                                                        DMA_TO_DEVICE);
+                       if (dma_mapping_error(&np->pci_dev->dev, np->put_tx_ctx->dma)) {
+
+                               /* Unwind the mapped fragments */
+                               do {
+                                       nv_unmap_txskb(np, start_tx_ctx);
+                                       if (unlikely(tmp_tx_ctx++ == np->last_tx_ctx))
+                                               tmp_tx_ctx = np->first_tx_ctx;
+                               } while (tmp_tx_ctx != np->put_tx_ctx);
+                               kfree_skb(skb);
+                               np->put_tx_ctx = start_tx_ctx;
+                               u64_stats_update_begin(&np->swstats_tx_syncp);
+                               np->stat_tx_dropped++;
+                               u64_stats_update_end(&np->swstats_tx_syncp);
+                               return NETDEV_TX_OK;
+                       }
+
                        np->put_tx_ctx->dma_len = bcnt;
                        np->put_tx_ctx->dma_single = 0;
                        put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma);
@@ -2327,7 +2347,8 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
        struct ring_desc_ex *start_tx;
        struct ring_desc_ex *prev_tx;
        struct nv_skb_map *prev_tx_ctx;
-       struct nv_skb_map *start_tx_ctx;
+       struct nv_skb_map *start_tx_ctx = NULL;
+       struct nv_skb_map *tmp_tx_ctx = NULL;
        unsigned long flags;
 
        /* add fragments to entries count */
@@ -2392,11 +2413,29 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
                        prev_tx = put_tx;
                        prev_tx_ctx = np->put_tx_ctx;
                        bcnt = (frag_size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : frag_size;
+                       if (!start_tx_ctx)
+                               start_tx_ctx = tmp_tx_ctx = np->put_tx_ctx;
                        np->put_tx_ctx->dma = skb_frag_dma_map(
                                                        &np->pci_dev->dev,
                                                        frag, offset,
                                                        bcnt,
                                                        DMA_TO_DEVICE);
+
+                       if (dma_mapping_error(&np->pci_dev->dev, np->put_tx_ctx->dma)) {
+
+                               /* Unwind the mapped fragments */
+                               do {
+                                       nv_unmap_txskb(np, start_tx_ctx);
+                                       if (unlikely(tmp_tx_ctx++ == np->last_tx_ctx))
+                                               tmp_tx_ctx = np->first_tx_ctx;
+                               } while (tmp_tx_ctx != np->put_tx_ctx);
+                               kfree_skb(skb);
+                               np->put_tx_ctx = start_tx_ctx;
+                               u64_stats_update_begin(&np->swstats_tx_syncp);
+                               np->stat_tx_dropped++;
+                               u64_stats_update_end(&np->swstats_tx_syncp);
+                               return NETDEV_TX_OK;
+                       }
                        np->put_tx_ctx->dma_len = bcnt;
                        np->put_tx_ctx->dma_single = 0;
                        put_tx->bufhigh = cpu_to_le32(dma_high(np->put_tx_ctx->dma));
@@ -2922,15 +2961,15 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit)
                        vlanflags = le32_to_cpu(np->get_rx.ex->buflow);
 
                        /*
-                        * There's need to check for NETIF_F_HW_VLAN_RX here.
-                        * Even if vlan rx accel is disabled,
+                        * There's need to check for NETIF_F_HW_VLAN_CTAG_RX
+                        * here. Even if vlan rx accel is disabled,
                         * NV_RX3_VLAN_TAG_PRESENT is pseudo randomly set.
                         */
-                       if (dev->features & NETIF_F_HW_VLAN_RX &&
+                       if (dev->features & NETIF_F_HW_VLAN_CTAG_RX &&
                            vlanflags & NV_RX3_VLAN_TAG_PRESENT) {
                                u16 vid = vlanflags & NV_RX3_VLAN_TAG_MASK;
 
-                               __vlan_hwaccel_put_tag(skb, vid);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
                        }
                        napi_gro_receive(&np->napi, skb);
                        u64_stats_update_begin(&np->swstats_rx_syncp);
@@ -4777,7 +4816,7 @@ static netdev_features_t nv_fix_features(struct net_device *dev,
        netdev_features_t features)
 {
        /* vlan is dependent on rx checksum offload */
-       if (features & (NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX))
+       if (features & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX))
                features |= NETIF_F_RXCSUM;
 
        return features;
@@ -4789,12 +4828,12 @@ static void nv_vlan_mode(struct net_device *dev, netdev_features_t features)
 
        spin_lock_irq(&np->lock);
 
-       if (features & NETIF_F_HW_VLAN_RX)
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
                np->txrxctl_bits |= NVREG_TXRXCTL_VLANSTRIP;
        else
                np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANSTRIP;
 
-       if (features & NETIF_F_HW_VLAN_TX)
+       if (features & NETIF_F_HW_VLAN_CTAG_TX)
                np->txrxctl_bits |= NVREG_TXRXCTL_VLANINS;
        else
                np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANINS;
@@ -4831,7 +4870,7 @@ static int nv_set_features(struct net_device *dev, netdev_features_t features)
                spin_unlock_irq(&np->lock);
        }
 
-       if (changed & (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX))
+       if (changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX))
                nv_vlan_mode(dev, features);
 
        return 0;
@@ -5025,7 +5064,6 @@ static int nv_loopback_test(struct net_device *dev)
        pkt_len = ETH_DATA_LEN;
        tx_skb = netdev_alloc_skb(dev, pkt_len);
        if (!tx_skb) {
-               netdev_err(dev, "netdev_alloc_skb() failed during loopback test\n");
                ret = 0;
                goto out;
        }
@@ -5667,7 +5705,8 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
        np->vlanctl_bits = 0;
        if (id->driver_data & DEV_HAS_VLAN) {
                np->vlanctl_bits = NVREG_VLANCONTROL_ENABLE;
-               dev->hw_features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
+               dev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX |
+                                   NETIF_F_HW_VLAN_CTAG_TX;
        }
 
        dev->features |= dev->hw_features;
@@ -5958,7 +5997,8 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
                 dev->features & NETIF_F_HIGHDMA ? "highdma " : "",
                 dev->features & (NETIF_F_IP_CSUM | NETIF_F_SG) ?
                        "csum " : "",
-                dev->features & (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX) ?
+                dev->features & (NETIF_F_HW_VLAN_CTAG_RX |
+                                 NETIF_F_HW_VLAN_CTAG_TX) ?
                        "vlan " : "",
                 dev->features & (NETIF_F_LOOPBACK) ?
                        "loopback " : "",
index efa29b712d5f1bdc9793648ebd76f9201cc3f99a..55a5548d6add50d43202cc74b46acd37934e556a 100644 (file)
@@ -1409,9 +1409,7 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)
                        dma_alloc_coherent(&pldat->pdev->dev,
                                           pldat->dma_buff_size, &dma_handle,
                                           GFP_KERNEL);
-
                if (pldat->dma_buff_base_v == NULL) {
-                       dev_err(&pdev->dev, "error getting DMA region.\n");
                        ret = -ENOMEM;
                        goto err_out_free_irq;
                }
@@ -1434,13 +1432,11 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)
        /* Get MAC address from current HW setting (POR state is all zeros) */
        __lpc_get_mac(pldat, ndev->dev_addr);
 
-#ifdef CONFIG_OF_NET
        if (!is_valid_ether_addr(ndev->dev_addr)) {
                const char *macaddr = of_get_mac_address(pdev->dev.of_node);
                if (macaddr)
                        memcpy(ndev->dev_addr, macaddr, ETH_ALEN);
        }
-#endif
        if (!is_valid_ether_addr(ndev->dev_addr))
                eth_hw_addr_random(ndev);
 
index 73ce7dd6b9544d470a6e14b163f7ae751d3d749d..0c1c65a9ce5e31edb87ccb27e5bb377523f99f1d 100644 (file)
@@ -1469,13 +1469,11 @@ pch_gbe_alloc_rx_buffers_pool(struct pch_gbe_adapter *adapter,
 
        size = rx_ring->count * bufsz + PCH_GBE_RESERVE_MEMORY;
        rx_ring->rx_buff_pool = dma_alloc_coherent(&pdev->dev, size,
-                                               &rx_ring->rx_buff_pool_logic,
-                                               GFP_KERNEL);
-       if (!rx_ring->rx_buff_pool) {
-               pr_err("Unable to allocate memory for the receive pool buffer\n");
+                                                  &rx_ring->rx_buff_pool_logic,
+                                                  GFP_KERNEL | __GFP_ZERO);
+       if (!rx_ring->rx_buff_pool)
                return -ENOMEM;
-       }
-       memset(rx_ring->rx_buff_pool, 0, size);
+
        rx_ring->rx_buff_pool_size = size;
        for (i = 0; i < rx_ring->count; i++) {
                buffer_info = &rx_ring->buffer_info[i];
@@ -1774,13 +1772,12 @@ int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter,
        tx_ring->size = tx_ring->count * (int)sizeof(struct pch_gbe_tx_desc);
 
        tx_ring->desc = dma_alloc_coherent(&pdev->dev, tx_ring->size,
-                                          &tx_ring->dma, GFP_KERNEL);
+                                          &tx_ring->dma,
+                                          GFP_KERNEL | __GFP_ZERO);
        if (!tx_ring->desc) {
                vfree(tx_ring->buffer_info);
-               pr_err("Unable to allocate memory for the transmit descriptor ring\n");
                return -ENOMEM;
        }
-       memset(tx_ring->desc, 0, tx_ring->size);
 
        tx_ring->next_to_use = 0;
        tx_ring->next_to_clean = 0;
@@ -1820,14 +1817,12 @@ int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter,
 
        rx_ring->size = rx_ring->count * (int)sizeof(struct pch_gbe_rx_desc);
        rx_ring->desc = dma_alloc_coherent(&pdev->dev, rx_ring->size,
-                                          &rx_ring->dma, GFP_KERNEL);
-
+                                          &rx_ring->dma,
+                                          GFP_KERNEL | __GFP_ZERO);
        if (!rx_ring->desc) {
-               pr_err("Unable to allocate memory for the receive descriptor ring\n");
                vfree(rx_ring->buffer_info);
                return -ENOMEM;
        }
-       memset(rx_ring->desc, 0, rx_ring->size);
        rx_ring->next_to_clean = 0;
        rx_ring->next_to_use = 0;
        for (desNo = 0; desNo < rx_ring->count; desNo++) {
@@ -2268,7 +2263,7 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu)
                if (err) {
                        adapter->rx_buffer_len = old_rx_buffer_len;
                        pch_gbe_up(adapter);
-                       return -ENOMEM;
+                       return err;
                } else {
                        netdev->mtu = new_mtu;
                        adapter->hw.mac.max_frame_size = max_frame;
index b1cfbb75ff1e09146b1c5ee6fc71562596b6e09f..a5f0b5da614933c74ebbf005edba139b4bb74b88 100644 (file)
@@ -441,12 +441,11 @@ static int pasemi_mac_setup_rx_resources(const struct net_device *dev)
 
        ring->buffers = dma_alloc_coherent(&mac->dma_pdev->dev,
                                           RX_RING_SIZE * sizeof(u64),
-                                          &ring->buf_dma, GFP_KERNEL);
+                                          &ring->buf_dma,
+                                          GFP_KERNEL | __GFP_ZERO);
        if (!ring->buffers)
                goto out_ring_desc;
 
-       memset(ring->buffers, 0, RX_RING_SIZE * sizeof(u64));
-
        write_dma_reg(PAS_DMA_RXCHAN_BASEL(chno),
                      PAS_DMA_RXCHAN_BASEL_BRBL(ring->chan.ring_dma));
 
index a8669adecc9708b633d1347b9b22798175a7a261..0e1797295a48dca4578d65fbce6a8608d4ca2afb 100644 (file)
@@ -35,6 +35,16 @@ config QLCNIC
          This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet
          devices.
 
+config QLCNIC_SRIOV
+       bool "QLOGIC QLCNIC 83XX family SR-IOV Support"
+       depends on QLCNIC && PCI_IOV
+       default y
+       ---help---
+         This configuration parameter enables Single Root Input Output
+         Virtualization support for QLE83XX Converged Ethernet devices.
+         This allows for virtual function acceleration in virtualized
+         environments.
+
 config QLGE
        tristate "QLogic QLGE 10Gb Ethernet Driver Support"
        depends on PCI
index eb3dfdbb642b18bb7ba34da39fd7de7411cb616b..322a36b76727d3522bb8bb1a35440d18ee21b696 100644 (file)
@@ -955,9 +955,10 @@ typedef struct nx_mac_list_s {
        uint8_t mac_addr[ETH_ALEN+2];
 } nx_mac_list_t;
 
-struct nx_vlan_ip_list {
+struct nx_ip_list {
        struct list_head list;
        __be32 ip_addr;
+       bool master;
 };
 
 /*
@@ -1605,7 +1606,7 @@ struct netxen_adapter {
        struct net_device *netdev;
        struct pci_dev *pdev;
        struct list_head mac_list;
-       struct list_head vlan_ip_list;
+       struct list_head ip_list;
 
        spinlock_t tx_clean_lock;
 
index 4782dcfde736cbd2d2b6621accf05180c3c54494..7692dfd4f262db9045f285aadc194c1713fb724a 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/if_vlan.h>
+#include <net/checksum.h>
 #include "netxen_nic.h"
 #include "netxen_nic_hw.h"
 
@@ -1641,9 +1642,8 @@ netxen_process_lro(struct netxen_adapter *adapter,
        th = (struct tcphdr *)((skb->data + vhdr_len) + (iph->ihl << 2));
 
        length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
+       csum_replace2(&iph->check, iph->tot_len, htons(length));
        iph->tot_len = htons(length);
-       iph->check = 0;
-       iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
        th->psh = push;
        th->seq = htonl(seq_number);
 
index 501f49207da5d635826fdcc41652fbc4868230bd..af951f343ff6d1396589ffbbf462aa2c7a80c776 100644 (file)
@@ -90,7 +90,7 @@ static irqreturn_t netxen_intr(int irq, void *data);
 static irqreturn_t netxen_msi_intr(int irq, void *data);
 static irqreturn_t netxen_msix_intr(int irq, void *data);
 
-static void netxen_free_vlan_ip_list(struct netxen_adapter *);
+static void netxen_free_ip_list(struct netxen_adapter *, bool);
 static void netxen_restore_indev_addr(struct net_device *dev, unsigned long);
 static struct rtnl_link_stats64 *netxen_nic_get_stats(struct net_device *dev,
                                                      struct rtnl_link_stats64 *stats);
@@ -1345,7 +1345,7 @@ netxen_setup_netdev(struct netxen_adapter *adapter,
        }
 
        if (adapter->capabilities & NX_FW_CAPABILITY_FVLANTX)
-               netdev->hw_features |= NETIF_F_HW_VLAN_TX;
+               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
 
        if (adapter->capabilities & NX_FW_CAPABILITY_HW_LRO)
                netdev->hw_features |= NETIF_F_LRO;
@@ -1450,7 +1450,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        spin_lock_init(&adapter->tx_clean_lock);
        INIT_LIST_HEAD(&adapter->mac_list);
-       INIT_LIST_HEAD(&adapter->vlan_ip_list);
+       INIT_LIST_HEAD(&adapter->ip_list);
 
        err = netxen_setup_pci_map(adapter);
        if (err)
@@ -1585,7 +1585,7 @@ static void netxen_nic_remove(struct pci_dev *pdev)
 
        cancel_work_sync(&adapter->tx_timeout_task);
 
-       netxen_free_vlan_ip_list(adapter);
+       netxen_free_ip_list(adapter, false);
        netxen_nic_detach(adapter);
 
        nx_decr_dev_ref_cnt(adapter);
@@ -3137,62 +3137,77 @@ netxen_destip_supported(struct netxen_adapter *adapter)
 }
 
 static void
-netxen_free_vlan_ip_list(struct netxen_adapter *adapter)
+netxen_free_ip_list(struct netxen_adapter *adapter, bool master)
 {
-       struct nx_vlan_ip_list  *cur;
-       struct list_head *head = &adapter->vlan_ip_list;
+       struct nx_ip_list  *cur, *tmp_cur;
 
-       while (!list_empty(head)) {
-               cur = list_entry(head->next, struct nx_vlan_ip_list, list);
-               netxen_config_ipaddr(adapter, cur->ip_addr, NX_IP_DOWN);
-               list_del(&cur->list);
-               kfree(cur);
+       list_for_each_entry_safe(cur, tmp_cur, &adapter->ip_list, list) {
+               if (master) {
+                       if (cur->master) {
+                               netxen_config_ipaddr(adapter, cur->ip_addr,
+                                                    NX_IP_DOWN);
+                               list_del(&cur->list);
+                               kfree(cur);
+                       }
+               } else {
+                       netxen_config_ipaddr(adapter, cur->ip_addr, NX_IP_DOWN);
+                       list_del(&cur->list);
+                       kfree(cur);
+               }
        }
-
 }
-static void
-netxen_list_config_vlan_ip(struct netxen_adapter *adapter,
+
+static bool
+netxen_list_config_ip(struct netxen_adapter *adapter,
                struct in_ifaddr *ifa, unsigned long event)
 {
        struct net_device *dev;
-       struct nx_vlan_ip_list *cur, *tmp_cur;
+       struct nx_ip_list *cur, *tmp_cur;
        struct list_head *head;
+       bool ret = false;
 
        dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL;
 
        if (dev == NULL)
-               return;
-
-       if (!is_vlan_dev(dev))
-               return;
+               goto out;
 
        switch (event) {
        case NX_IP_UP:
-               list_for_each(head, &adapter->vlan_ip_list) {
-                       cur = list_entry(head, struct nx_vlan_ip_list, list);
+               list_for_each(head, &adapter->ip_list) {
+                       cur = list_entry(head, struct nx_ip_list, list);
 
                        if (cur->ip_addr == ifa->ifa_address)
-                               return;
+                               goto out;
                }
 
-               cur = kzalloc(sizeof(struct nx_vlan_ip_list), GFP_ATOMIC);
+               cur = kzalloc(sizeof(struct nx_ip_list), GFP_ATOMIC);
                if (cur == NULL)
-                       return;
-
+                       goto out;
+               if (dev->priv_flags & IFF_802_1Q_VLAN)
+                       dev = vlan_dev_real_dev(dev);
+               cur->master = !!netif_is_bond_master(dev);
                cur->ip_addr = ifa->ifa_address;
-               list_add_tail(&cur->list, &adapter->vlan_ip_list);
+               list_add_tail(&cur->list, &adapter->ip_list);
+               netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_UP);
+               ret = true;
                break;
        case NX_IP_DOWN:
                list_for_each_entry_safe(cur, tmp_cur,
-                                       &adapter->vlan_ip_list, list) {
+                                       &adapter->ip_list, list) {
                        if (cur->ip_addr == ifa->ifa_address) {
                                list_del(&cur->list);
                                kfree(cur);
+                               netxen_config_ipaddr(adapter, ifa->ifa_address,
+                                                    NX_IP_DOWN);
+                               ret = true;
                                break;
                        }
                }
        }
+out:
+       return ret;
 }
+
 static void
 netxen_config_indev_addr(struct netxen_adapter *adapter,
                struct net_device *dev, unsigned long event)
@@ -3209,14 +3224,10 @@ netxen_config_indev_addr(struct netxen_adapter *adapter,
        for_ifa(indev) {
                switch (event) {
                case NETDEV_UP:
-                       netxen_config_ipaddr(adapter,
-                                       ifa->ifa_address, NX_IP_UP);
-                       netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP);
+                       netxen_list_config_ip(adapter, ifa, NX_IP_UP);
                        break;
                case NETDEV_DOWN:
-                       netxen_config_ipaddr(adapter,
-                                       ifa->ifa_address, NX_IP_DOWN);
-                       netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN);
+                       netxen_list_config_ip(adapter, ifa, NX_IP_DOWN);
                        break;
                default:
                        break;
@@ -3231,23 +3242,78 @@ netxen_restore_indev_addr(struct net_device *netdev, unsigned long event)
 
 {
        struct netxen_adapter *adapter = netdev_priv(netdev);
-       struct nx_vlan_ip_list *pos, *tmp_pos;
+       struct nx_ip_list *pos, *tmp_pos;
        unsigned long ip_event;
 
        ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN;
        netxen_config_indev_addr(adapter, netdev, event);
 
-       list_for_each_entry_safe(pos, tmp_pos, &adapter->vlan_ip_list, list) {
+       list_for_each_entry_safe(pos, tmp_pos, &adapter->ip_list, list) {
                netxen_config_ipaddr(adapter, pos->ip_addr, ip_event);
        }
 }
 
+static inline bool
+netxen_config_checkdev(struct net_device *dev)
+{
+       struct netxen_adapter *adapter;
+
+       if (!is_netxen_netdev(dev))
+               return false;
+       adapter = netdev_priv(dev);
+       if (!adapter)
+               return false;
+       if (!netxen_destip_supported(adapter))
+               return false;
+       if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC)
+               return false;
+
+       return true;
+}
+
+/**
+ * netxen_config_master - configure addresses based on master
+ * @dev: netxen device
+ * @event: netdev event
+ */
+static void netxen_config_master(struct net_device *dev, unsigned long event)
+{
+       struct net_device *master, *slave;
+       struct netxen_adapter *adapter = netdev_priv(dev);
+
+       rcu_read_lock();
+       master = netdev_master_upper_dev_get_rcu(dev);
+       /*
+        * This is the case where the netxen nic is being
+        * enslaved and is dev_open()ed in bond_enslave()
+        * Now we should program the bond's (and its vlans')
+        * addresses in the netxen NIC.
+        */
+       if (master && netif_is_bond_master(master) &&
+           !netif_is_bond_slave(dev)) {
+               netxen_config_indev_addr(adapter, master, event);
+               for_each_netdev_rcu(&init_net, slave)
+                       if (slave->priv_flags & IFF_802_1Q_VLAN &&
+                           vlan_dev_real_dev(slave) == master)
+                               netxen_config_indev_addr(adapter, slave, event);
+       }
+       rcu_read_unlock();
+       /*
+        * This is the case where the netxen nic is being
+        * released and is dev_close()ed in bond_release()
+        * just before IFF_BONDING is stripped.
+        */
+       if (!master && dev->priv_flags & IFF_BONDING)
+               netxen_free_ip_list(adapter, true);
+}
+
 static int netxen_netdev_event(struct notifier_block *this,
                                 unsigned long event, void *ptr)
 {
        struct netxen_adapter *adapter;
        struct net_device *dev = (struct net_device *)ptr;
        struct net_device *orig_dev = dev;
+       struct net_device *slave;
 
 recheck:
        if (dev == NULL)
@@ -3257,19 +3323,28 @@ recheck:
                dev = vlan_dev_real_dev(dev);
                goto recheck;
        }
-
-       if (!is_netxen_netdev(dev))
-               goto done;
-
-       adapter = netdev_priv(dev);
-
-       if (!adapter)
-               goto done;
-
-       if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC)
-               goto done;
-
-       netxen_config_indev_addr(adapter, orig_dev, event);
+       if (event == NETDEV_UP || event == NETDEV_DOWN) {
+               /* If this is a bonding device, look for netxen-based slaves*/
+               if (netif_is_bond_master(dev)) {
+                       rcu_read_lock();
+                       for_each_netdev_in_bond_rcu(dev, slave) {
+                               if (!netxen_config_checkdev(slave))
+                                       continue;
+                               adapter = netdev_priv(slave);
+                               netxen_config_indev_addr(adapter,
+                                                        orig_dev, event);
+                       }
+                       rcu_read_unlock();
+               } else {
+                       if (!netxen_config_checkdev(dev))
+                               goto done;
+                       adapter = netdev_priv(dev);
+                       /* Act only if the actual netxen is the target */
+                       if (orig_dev == dev)
+                               netxen_config_master(dev, event);
+                       netxen_config_indev_addr(adapter, orig_dev, event);
+               }
+       }
 done:
        return NOTIFY_DONE;
 }
@@ -3279,12 +3354,12 @@ netxen_inetaddr_event(struct notifier_block *this,
                unsigned long event, void *ptr)
 {
        struct netxen_adapter *adapter;
-       struct net_device *dev;
-
+       struct net_device *dev, *slave;
        struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+       unsigned long ip_event;
 
        dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL;
-
+       ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN;
 recheck:
        if (dev == NULL)
                goto done;
@@ -3293,31 +3368,24 @@ recheck:
                dev = vlan_dev_real_dev(dev);
                goto recheck;
        }
-
-       if (!is_netxen_netdev(dev))
-               goto done;
-
-       adapter = netdev_priv(dev);
-
-       if (!adapter || !netxen_destip_supported(adapter))
-               goto done;
-
-       if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC)
-               goto done;
-
-       switch (event) {
-       case NETDEV_UP:
-               netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_UP);
-               netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP);
-               break;
-       case NETDEV_DOWN:
-               netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_DOWN);
-               netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN);
-               break;
-       default:
-               break;
+       if (event == NETDEV_UP || event == NETDEV_DOWN) {
+               /* If this is a bonding device, look for netxen-based slaves*/
+               if (netif_is_bond_master(dev)) {
+                       rcu_read_lock();
+                       for_each_netdev_in_bond_rcu(dev, slave) {
+                               if (!netxen_config_checkdev(slave))
+                                       continue;
+                               adapter = netdev_priv(slave);
+                               netxen_list_config_ip(adapter, ifa, ip_event);
+                       }
+                       rcu_read_unlock();
+               } else {
+                       if (!netxen_config_checkdev(dev))
+                               goto done;
+                       adapter = netdev_priv(dev);
+                       netxen_list_config_ip(adapter, ifa, ip_event);
+               }
        }
-
 done:
        return NOTIFY_DONE;
 }
@@ -3334,7 +3402,7 @@ static void
 netxen_restore_indev_addr(struct net_device *dev, unsigned long event)
 { }
 static void
-netxen_free_vlan_ip_list(struct netxen_adapter *adapter)
+netxen_free_ip_list(struct netxen_adapter *adapter, bool master)
 { }
 #endif
 
index 8fd38cb6d26a54c502ab10d3250c781859a30801..91a8fcd6c24654e52d94aa6a96f68bf02e32a4ee 100644 (file)
@@ -312,7 +312,6 @@ static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev,
                lrg_buf_cb->skb = netdev_alloc_skb(qdev->ndev,
                                                   qdev->lrg_buffer_len);
                if (unlikely(!lrg_buf_cb->skb)) {
-                       netdev_err(qdev->ndev, "failed netdev_alloc_skb()\n");
                        qdev->lrg_buf_skb_check++;
                } else {
                        /*
index 7722a203e388f6d7a0e75fc60248467e920cc4e5..4b1fb3faa3b76ad71ffa39d1d7083374181d5665 100644 (file)
@@ -8,4 +8,6 @@ qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \
        qlcnic_ethtool.o qlcnic_ctx.o qlcnic_io.o \
        qlcnic_sysfs.o qlcnic_minidump.o qlcnic_83xx_hw.o \
        qlcnic_83xx_init.o qlcnic_83xx_vnic.o \
-       qlcnic_minidump.o
+       qlcnic_minidump.o qlcnic_sriov_common.o
+
+qlcnic-$(CONFIG_QLCNIC_SRIOV) += qlcnic_sriov_pf.o
index ba3c72fce1f2a5b044a07a1bb47fdf0fdfb4f80a..8d02dd75c9a231b25fb39e5c13b79e8178307dde 100644 (file)
@@ -37,9 +37,9 @@
 #include "qlcnic_83xx_hw.h"
 
 #define _QLCNIC_LINUX_MAJOR 5
-#define _QLCNIC_LINUX_MINOR 1
-#define _QLCNIC_LINUX_SUBVERSION 35
-#define QLCNIC_LINUX_VERSIONID  "5.1.35"
+#define _QLCNIC_LINUX_MINOR 2
+#define _QLCNIC_LINUX_SUBVERSION 41
+#define QLCNIC_LINUX_VERSIONID  "5.2.41"
 #define QLCNIC_DRV_IDC_VER  0x01
 #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\
                 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -449,6 +449,7 @@ struct qlcnic_hardware_context {
        struct qlc_83xx_idc idc;
        struct qlc_83xx_fw_info fw_info;
        struct qlcnic_intrpt_config *intr_tbl;
+       struct qlcnic_sriov *sriov;
        u32 *reg_tbl;
        u32 *ext_reg_tbl;
        u32 mbox_aen[QLC_83XX_MBX_AEN_CNT];
@@ -896,6 +897,7 @@ struct qlcnic_ipaddr {
 #define QLCNIC_FW_RESET_OWNER          0x2000
 #define QLCNIC_FW_HANG                 0x4000
 #define QLCNIC_FW_LRO_MSS_CAP          0x8000
+#define QLCNIC_TX_INTR_SHARED          0x10000
 #define QLCNIC_IS_MSI_FAMILY(adapter) \
        ((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
 
@@ -914,7 +916,10 @@ struct qlcnic_ipaddr {
 #define __QLCNIC_AER                   5
 #define __QLCNIC_DIAG_RES_ALLOC                6
 #define __QLCNIC_LED_ENABLE            7
-#define __QLCNIC_ELB_INPROGRESS        8
+#define __QLCNIC_ELB_INPROGRESS                8
+#define __QLCNIC_SRIOV_ENABLE          10
+#define __QLCNIC_SRIOV_CAPABLE         11
+#define __QLCNIC_MBX_POLL_ENABLE       12
 
 #define QLCNIC_INTERRUPT_TEST          1
 #define QLCNIC_LOOPBACK_TEST           2
@@ -935,7 +940,7 @@ struct qlcnic_ipaddr {
 struct qlcnic_filter {
        struct hlist_node fnode;
        u8 faddr[ETH_ALEN];
-       __le16 vlan_id;
+       u16 vlan_id;
        unsigned long ftime;
 };
 
@@ -972,9 +977,11 @@ struct qlcnic_adapter {
        u8 fw_fail_cnt;
        u8 tx_timeo_cnt;
        u8 need_fw_reset;
+       u8 reset_ctx_cnt;
 
        u16 is_up;
-       u16 pvid;
+       u16 rx_pvid;
+       u16 tx_pvid;
 
        u32 irq;
        u32 heartbeat;
@@ -1006,9 +1013,11 @@ struct qlcnic_adapter {
        struct workqueue_struct *qlcnic_wq;
        struct delayed_work fw_work;
        struct delayed_work idc_aen_work;
+       struct delayed_work mbx_poll_work;
 
        struct qlcnic_filter_hash fhash;
        struct qlcnic_filter_hash rx_fhash;
+       struct list_head vf_mc_list;
 
        spinlock_t tx_clean_lock;
        spinlock_t mac_learn_lock;
@@ -1051,7 +1060,11 @@ struct qlcnic_info_le {
        u8      total_pf;
        u8      total_rss_engines;
        __le16  max_vports;
-       u8      reserved2[64];
+       __le16  linkstate_reg_offset;
+       __le16  bit_offsets;
+       __le16  max_local_ipv6_addrs;
+       __le16  max_remote_ipv6_addrs;
+       u8      reserved2[56];
 } __packed;
 
 struct qlcnic_info {
@@ -1083,6 +1096,10 @@ struct qlcnic_info {
        u8      total_pf;
        u8      total_rss_engines;
        u16     max_vports;
+       u16     linkstate_reg_offset;
+       u16     bit_offsets;
+       u16     max_local_ipv6_addrs;
+       u16     max_remote_ipv6_addrs;
 };
 
 struct qlcnic_pci_info_le {
@@ -1348,6 +1365,7 @@ struct _cdrp_cmd {
 struct qlcnic_cmd_args {
        struct _cdrp_cmd req;
        struct _cdrp_cmd rsp;
+       int op_type;
 };
 
 int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter);
@@ -1430,9 +1448,10 @@ void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,
                struct qlcnic_host_rds_ring *rds_ring, u8 ring_id);
 int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max);
 void qlcnic_set_multi(struct net_device *netdev);
-int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *);
+void __qlcnic_set_multi(struct net_device *, u16);
+int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *, u16);
 int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *);
-void qlcnic_free_mac_list(struct qlcnic_adapter *adapter);
+void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter);
 
 int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu);
 int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *);
@@ -1509,8 +1528,13 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *);
 int qlcnic_set_default_offload_settings(struct qlcnic_adapter *);
 int qlcnic_reset_npar_config(struct qlcnic_adapter *);
 int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *);
-void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int,
-                         __le16);
+void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int, u16);
+int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
+int qlcnic_read_mac_addr(struct qlcnic_adapter *);
+int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int);
+void qlcnic_sriov_vf_schedule_multi(struct net_device *);
+void qlcnic_vf_add_mc_list(struct net_device *, u16);
+
 /*
  * QLOGIC Board information
  */
@@ -1567,11 +1591,14 @@ struct qlcnic_hardware_ops {
        int (*create_rx_ctx) (struct qlcnic_adapter *);
        int (*create_tx_ctx) (struct qlcnic_adapter *,
        struct qlcnic_host_tx_ring *, int);
+       void (*del_rx_ctx) (struct qlcnic_adapter *);
+       void (*del_tx_ctx) (struct qlcnic_adapter *,
+                           struct qlcnic_host_tx_ring *);
        int (*setup_link_event) (struct qlcnic_adapter *, int);
        int (*get_nic_info) (struct qlcnic_adapter *, struct qlcnic_info *, u8);
        int (*get_pci_info) (struct qlcnic_adapter *, struct qlcnic_pci_info *);
        int (*set_nic_info) (struct qlcnic_adapter *, struct qlcnic_info *);
-       int (*change_macvlan) (struct qlcnic_adapter *, u8*, __le16, u8);
+       int (*change_macvlan) (struct qlcnic_adapter *, u8*, u16, u8);
        void (*napi_enable) (struct qlcnic_adapter *);
        void (*napi_disable) (struct qlcnic_adapter *);
        void (*config_intr_coal) (struct qlcnic_adapter *);
@@ -1580,8 +1607,9 @@ struct qlcnic_hardware_ops {
        int (*config_loopback) (struct qlcnic_adapter *, u8);
        int (*clear_loopback) (struct qlcnic_adapter *, u8);
        int (*config_promisc_mode) (struct qlcnic_adapter *, u32);
-       void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, __le16);
+       void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, u16);
        int (*get_board_info) (struct qlcnic_adapter *);
+       void (*free_mac_list) (struct qlcnic_adapter *);
 };
 
 extern struct qlcnic_nic_template qlcnic_vf_ops;
@@ -1635,7 +1663,10 @@ static inline int qlcnic_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
 static inline int qlcnic_issue_cmd(struct qlcnic_adapter *adapter,
                                   struct qlcnic_cmd_args *cmd)
 {
-       return adapter->ahw->hw_ops->mbx_cmd(adapter, cmd);
+       if (adapter->ahw->hw_ops->mbx_cmd)
+               return adapter->ahw->hw_ops->mbx_cmd(adapter, cmd);
+
+       return -EIO;
 }
 
 static inline void qlcnic_get_func_no(struct qlcnic_adapter *adapter)
@@ -1655,12 +1686,14 @@ static inline void qlcnic_api_unlock(struct qlcnic_adapter *adapter)
 
 static inline void qlcnic_add_sysfs(struct qlcnic_adapter *adapter)
 {
-       adapter->ahw->hw_ops->add_sysfs(adapter);
+       if (adapter->ahw->hw_ops->add_sysfs)
+               adapter->ahw->hw_ops->add_sysfs(adapter);
 }
 
 static inline void qlcnic_remove_sysfs(struct qlcnic_adapter *adapter)
 {
-       adapter->ahw->hw_ops->remove_sysfs(adapter);
+       if (adapter->ahw->hw_ops->remove_sysfs)
+               adapter->ahw->hw_ops->remove_sysfs(adapter);
 }
 
 static inline void
@@ -1681,6 +1714,17 @@ static inline int qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
        return adapter->ahw->hw_ops->create_tx_ctx(adapter, ptr, ring);
 }
 
+static inline void qlcnic_fw_cmd_del_rx_ctx(struct qlcnic_adapter *adapter)
+{
+       return adapter->ahw->hw_ops->del_rx_ctx(adapter);
+}
+
+static inline void qlcnic_fw_cmd_del_tx_ctx(struct qlcnic_adapter *adapter,
+                                           struct qlcnic_host_tx_ring *ptr)
+{
+       return adapter->ahw->hw_ops->del_tx_ctx(adapter, ptr);
+}
+
 static inline int qlcnic_linkevent_request(struct qlcnic_adapter *adapter,
                                           int enable)
 {
@@ -1706,7 +1750,7 @@ static inline int qlcnic_set_nic_info(struct qlcnic_adapter *adapter,
 }
 
 static inline int qlcnic_sre_macaddr_change(struct qlcnic_adapter *adapter,
-                                           u8 *addr, __le16 id, u8 cmd)
+                                           u8 *addr, u16 id, u8 cmd)
 {
        return adapter->ahw->hw_ops->change_macvlan(adapter, addr, id, cmd);
 }
@@ -1765,7 +1809,7 @@ static inline int qlcnic_nic_set_promisc(struct qlcnic_adapter *adapter,
 }
 
 static inline void qlcnic_change_filter(struct qlcnic_adapter *adapter,
-                                       u64 *addr, __le16 id)
+                                       u64 *addr, u16 id)
 {
        adapter->ahw->hw_ops->change_l2_filter(adapter, addr, id);
 }
@@ -1775,15 +1819,22 @@ static inline int qlcnic_get_board_info(struct qlcnic_adapter *adapter)
        return adapter->ahw->hw_ops->get_board_info(adapter);
 }
 
+static inline void qlcnic_free_mac_list(struct qlcnic_adapter *adapter)
+{
+       return adapter->ahw->hw_ops->free_mac_list(adapter);
+}
+
 static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter,
                                            u32 key)
 {
-       adapter->nic_ops->request_reset(adapter, key);
+       if (adapter->nic_ops->request_reset)
+               adapter->nic_ops->request_reset(adapter, key);
 }
 
 static inline void qlcnic_cancel_idc_work(struct qlcnic_adapter *adapter)
 {
-       adapter->nic_ops->cancel_idc_work(adapter);
+       if (adapter->nic_ops->cancel_idc_work)
+               adapter->nic_ops->cancel_idc_work(adapter);
 }
 
 static inline irqreturn_t
@@ -1819,6 +1870,7 @@ static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring)
                writel(0xfbff, adapter->tgt_mask_reg);
 }
 
+extern const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops;
 extern const struct ethtool_ops qlcnic_ethtool_ops;
 extern const struct ethtool_ops qlcnic_ethtool_failed_ops;
 
@@ -1830,7 +1882,9 @@ extern const struct ethtool_ops qlcnic_ethtool_failed_ops;
        } while (0)
 
 #define PCI_DEVICE_ID_QLOGIC_QLE834X    0x8030
+#define PCI_DEVICE_ID_QLOGIC_VF_QLE834X        0x8430
 #define PCI_DEVICE_ID_QLOGIC_QLE824X   0x8020
+
 static inline bool qlcnic_82xx_check(struct qlcnic_adapter *adapter)
 {
        unsigned short device = adapter->pdev->device;
@@ -1840,8 +1894,23 @@ static inline bool qlcnic_82xx_check(struct qlcnic_adapter *adapter)
 static inline bool qlcnic_83xx_check(struct qlcnic_adapter *adapter)
 {
        unsigned short device = adapter->pdev->device;
-       return (device == PCI_DEVICE_ID_QLOGIC_QLE834X) ? true : false;
+       bool status;
+
+       status = ((device == PCI_DEVICE_ID_QLOGIC_QLE834X) ||
+                 (device == PCI_DEVICE_ID_QLOGIC_VF_QLE834X)) ? true : false;
+
+       return status;
+}
+
+static inline bool qlcnic_sriov_pf_check(struct qlcnic_adapter *adapter)
+{
+       return (adapter->ahw->op_mode == QLCNIC_SRIOV_PF_FUNC) ? true : false;
 }
 
+static inline bool qlcnic_sriov_vf_check(struct qlcnic_adapter *adapter)
+{
+       unsigned short device = adapter->pdev->device;
 
+       return (device == PCI_DEVICE_ID_QLOGIC_VF_QLE834X) ? true : false;
+}
 #endif                         /* __QLCNIC_H_ */
index edd63f1230f3d0076f9bfbdcff146f62a6c2acaa..fd0829c2839d9a2d718c34ed784f2b5ecc855ffc 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "qlcnic.h"
+#include "qlcnic_sriov.h"
 #include <linux/if_vlan.h>
 #include <linux/ipv6.h>
 #include <linux/ethtool.h>
 
 #define QLCNIC_MAX_TX_QUEUES           1
 #define RSS_HASHTYPE_IP_TCP            0x3
-
-/* status descriptor mailbox data
- * @phy_addr: physical address of buffer
- * @sds_ring_size: buffer size
- * @intrpt_id: interrupt id
- * @intrpt_val: source of interrupt
- */
-struct qlcnic_sds_mbx {
-       u64     phy_addr;
-       u8      rsvd1[16];
-       u16     sds_ring_size;
-       u16     rsvd2[3];
-       u16     intrpt_id;
-       u8      intrpt_val;
-       u8      rsvd3[5];
-} __packed;
-
-/* receive descriptor buffer data
- * phy_addr_reg: physical address of regular buffer
- * phy_addr_jmb: physical address of jumbo buffer
- * reg_ring_sz: size of regular buffer
- * reg_ring_len: no. of entries in regular buffer
- * jmb_ring_len: no. of entries in jumbo buffer
- * jmb_ring_sz: size of jumbo buffer
- */
-struct qlcnic_rds_mbx {
-       u64     phy_addr_reg;
-       u64     phy_addr_jmb;
-       u16     reg_ring_sz;
-       u16     reg_ring_len;
-       u16     jmb_ring_sz;
-       u16     jmb_ring_len;
-} __packed;
-
-/* host producers for regular and jumbo rings */
-struct __host_producer_mbx {
-       u32     reg_buf;
-       u32     jmb_buf;
-} __packed;
-
-/* Receive context mailbox data outbox registers
- * @state: state of the context
- * @vport_id: virtual port id
- * @context_id: receive context id
- * @num_pci_func: number of pci functions of the port
- * @phy_port: physical port id
- */
-struct qlcnic_rcv_mbx_out {
-       u8      rcv_num;
-       u8      sts_num;
-       u16     ctx_id;
-       u8      state;
-       u8      num_pci_func;
-       u8      phy_port;
-       u8      vport_id;
-       u32     host_csmr[QLCNIC_MAX_RING_SETS];
-       struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS];
-} __packed;
-
-struct qlcnic_add_rings_mbx_out {
-       u8      rcv_num;
-       u8      sts_num;
-       u16  ctx_id;
-       u32  host_csmr[QLCNIC_MAX_RING_SETS];
-       struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS];
-} __packed;
-
-/* Transmit context mailbox inbox registers
- * @phys_addr: DMA address of the transmit buffer
- * @cnsmr_index: host consumer index
- * @size: legth of transmit buffer ring
- * @intr_id: interrput id
- * @src: src of interrupt
- */
-struct qlcnic_tx_mbx {
-       u64     phys_addr;
-       u64     cnsmr_index;
-       u16     size;
-       u16     intr_id;
-       u8      src;
-       u8      rsvd[3];
-} __packed;
-
-/* Transmit context mailbox outbox registers
- * @host_prod: host producer index
- * @ctx_id: transmit context id
- * @state: state of the transmit context
- */
-struct qlcnic_tx_mbx_out {
-       u32     host_prod;
-       u16     ctx_id;
-       u8      state;
-       u8      rsvd;
-} __packed;
+#define QLC_83XX_FW_MBX_CMD            0
 
 static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
        {QLCNIC_CMD_CONFIGURE_IP_ADDR, 6, 1},
@@ -156,9 +64,11 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
        {QLCNIC_CMD_SET_LED_CONFIG, 5, 1},
        {QLCNIC_CMD_GET_LED_CONFIG, 1, 5},
        {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26},
+       {QLCNIC_CMD_CONFIG_VPORT, 4, 4},
+       {QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},
 };
 
-static const u32 qlcnic_83xx_ext_reg_tbl[] = {
+const u32 qlcnic_83xx_ext_reg_tbl[] = {
        0x38CC,         /* Global Reset */
        0x38F0,         /* Wildcard */
        0x38FC,         /* Informant */
@@ -204,7 +114,7 @@ static const u32 qlcnic_83xx_ext_reg_tbl[] = {
        0x34A4,         /* QLC_83XX_ASIC_TEMP */
 };
 
-static const u32 qlcnic_83xx_reg_tbl[] = {
+const u32 qlcnic_83xx_reg_tbl[] = {
        0x34A8,         /* PEG_HALT_STAT1 */
        0x34AC,         /* PEG_HALT_STAT2 */
        0x34B0,         /* FW_HEARTBEAT */
@@ -247,6 +157,8 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {
        .process_lb_rcv_ring_diag       = qlcnic_83xx_process_rcv_ring_diag,
        .create_rx_ctx                  = qlcnic_83xx_create_rx_ctx,
        .create_tx_ctx                  = qlcnic_83xx_create_tx_ctx,
+       .del_rx_ctx                     = qlcnic_83xx_del_rx_ctx,
+       .del_tx_ctx                     = qlcnic_83xx_del_tx_ctx,
        .setup_link_event               = qlcnic_83xx_setup_link_event,
        .get_nic_info                   = qlcnic_83xx_get_nic_info,
        .get_pci_info                   = qlcnic_83xx_get_pci_info,
@@ -260,6 +172,7 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {
        .config_promisc_mode            = qlcnic_83xx_nic_set_promisc,
        .change_l2_filter               = qlcnic_83xx_change_l2_filter,
        .get_board_info                 = qlcnic_83xx_get_port_info,
+       .free_mac_list                  = qlcnic_82xx_free_mac_list,
 };
 
 static struct qlcnic_nic_template qlcnic_83xx_ops = {
@@ -355,14 +268,20 @@ int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr)
                                              num_intr));
        /* account for AEN interrupt MSI-X based interrupts */
        num_msix += 1;
-       num_msix += adapter->max_drv_tx_rings;
+
+       if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
+               num_msix += adapter->max_drv_tx_rings;
+
        err = qlcnic_enable_msix(adapter, num_msix);
        if (err == -ENOMEM)
                return err;
        if (adapter->flags & QLCNIC_MSIX_ENABLED)
                num_msix = adapter->ahw->num_msix;
-       else
+       else {
+               if (qlcnic_sriov_vf_check(adapter))
+                       return -EINVAL;
                num_msix = 1;
+       }
        /* setup interrupt mapping table for fw */
        ahw->intr_tbl = vzalloc(num_msix *
                                sizeof(struct qlcnic_intrpt_config));
@@ -421,12 +340,13 @@ inline void qlcnic_83xx_enable_legacy_msix_mbx_intr(struct qlcnic_adapter
        writel(0, adapter->ahw->pci_base0 + mask);
 }
 
-inline void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *adapter)
+void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *adapter)
 {
        u32 mask;
 
        mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);
        writel(1, adapter->ahw->pci_base0 + mask);
+       QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, 0);
 }
 
 static inline void qlcnic_83xx_get_mbx_data(struct qlcnic_adapter *adapter,
@@ -482,7 +402,8 @@ static void qlcnic_83xx_poll_process_aen(struct qlcnic_adapter *adapter)
 
        event = readl(QLCNIC_MBX_FW(adapter->ahw, 0));
        if (event &  QLCNIC_MBX_ASYNC_EVENT)
-               qlcnic_83xx_process_aen(adapter);
+               __qlcnic_83xx_process_aen(adapter);
+
 out:
        qlcnic_83xx_enable_legacy_msix_mbx_intr(adapter);
        spin_unlock_irqrestore(&adapter->ahw->mbx_lock, flags);
@@ -535,17 +456,15 @@ done:
 
 void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter)
 {
-       u32 val = 0, num_msix = adapter->ahw->num_msix - 1;
+       u32 num_msix;
+
+       qlcnic_83xx_disable_mbx_intr(adapter);
 
        if (adapter->flags & QLCNIC_MSIX_ENABLED)
                num_msix = adapter->ahw->num_msix - 1;
        else
                num_msix = 0;
 
-       QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, val);
-
-       qlcnic_83xx_disable_mbx_intr(adapter);
-
        msleep(20);
        synchronize_irq(adapter->msix_entries[num_msix].vector);
        free_irq(adapter->msix_entries[num_msix].vector, adapter);
@@ -595,7 +514,7 @@ int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter)
 void qlcnic_83xx_get_func_no(struct qlcnic_adapter *adapter)
 {
        u32 val = QLCRDX(adapter->ahw, QLCNIC_INFORMANT);
-       adapter->ahw->pci_func = val & 0xf;
+       adapter->ahw->pci_func = (val >> 24) & 0xff;
 }
 
 int qlcnic_83xx_cam_lock(struct qlcnic_adapter *adapter)
@@ -707,6 +626,11 @@ void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter,
        ahw->fw_hal_version = 2;
        qlcnic_get_func_no(adapter);
 
+       if (qlcnic_sriov_vf_check(adapter)) {
+               qlcnic_sriov_vf_set_ops(adapter);
+               return;
+       }
+
        /* Determine function privilege level */
        op_mode = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE);
        if (op_mode == QLC_83XX_DEFAULT_OPMODE)
@@ -722,6 +646,9 @@ void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter,
                         ahw->fw_hal_version);
                adapter->nic_ops = &qlcnic_vf_ops;
        } else {
+               if (pci_find_ext_capability(adapter->pdev,
+                                           PCI_EXT_CAP_ID_SRIOV))
+                       set_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state);
                adapter->nic_ops = &qlcnic_83xx_ops;
        }
 }
@@ -755,7 +682,7 @@ static void qlcnic_dump_mbx(struct qlcnic_adapter *adapter,
 }
 
 /* Mailbox response for mac rcode */
-static u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter)
+u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter)
 {
        u32 fw_data;
        u8 mac_cmd_rcode;
@@ -769,7 +696,7 @@ static u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter)
        return 1;
 }
 
-static u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *adapter)
+u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *adapter)
 {
        u32 data;
        unsigned long wait_time = 0;
@@ -832,7 +759,7 @@ poll:
                /* Get the FW response data */
                fw_data = readl(QLCNIC_MBX_FW(ahw, 0));
                if (fw_data &  QLCNIC_MBX_ASYNC_EVENT) {
-                       qlcnic_83xx_process_aen(adapter);
+                       __qlcnic_83xx_process_aen(adapter);
                        mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL);
                        if (mbx_val)
                                goto poll;
@@ -884,6 +811,7 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
        size = ARRAY_SIZE(qlcnic_83xx_mbx_tbl);
        for (i = 0; i < size; i++) {
                if (type == mbx_tbl[i].cmd) {
+                       mbx->op_type = QLC_83XX_FW_MBX_CMD;
                        mbx->req.num = mbx_tbl[i].in_args;
                        mbx->rsp.num = mbx_tbl[i].out_args;
                        mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32),
@@ -901,10 +829,10 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
                        memset(mbx->rsp.arg, 0, sizeof(u32) * mbx->rsp.num);
                        temp = adapter->ahw->fw_hal_version << 29;
                        mbx->req.arg[0] = (type | (mbx->req.num << 16) | temp);
-                       break;
+                       return 0;
                }
        }
-       return 0;
+       return -EINVAL;
 }
 
 void qlcnic_83xx_idc_aen_work(struct work_struct *work)
@@ -935,7 +863,7 @@ static void qlcnic_83xx_handle_idc_comp_aen(struct qlcnic_adapter *adapter,
        return;
 }
 
-void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
+void __qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
 {
        u32 event[QLC_83XX_MBX_AEN_CNT];
        int i;
@@ -960,6 +888,9 @@ void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
                break;
        case QLCNIC_MBX_TIME_EXTEND_EVENT:
                break;
+       case QLCNIC_MBX_BC_EVENT:
+               qlcnic_sriov_handle_bc_event(adapter, event[1]);
+               break;
        case QLCNIC_MBX_SFP_INSERT_EVENT:
                dev_info(&adapter->pdev->dev, "SFP+ Insert AEN:0x%x.\n",
                         QLCNIC_MBX_RSP(event[0]));
@@ -977,6 +908,53 @@ void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
        QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER);
 }
 
+static void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u32 resp, event;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ahw->mbx_lock, flags);
+
+       resp = QLCRDX(ahw, QLCNIC_FW_MBX_CTRL);
+       if (resp & QLCNIC_SET_OWNER) {
+               event = readl(QLCNIC_MBX_FW(ahw, 0));
+               if (event &  QLCNIC_MBX_ASYNC_EVENT)
+                       __qlcnic_83xx_process_aen(adapter);
+       }
+
+       spin_unlock_irqrestore(&ahw->mbx_lock, flags);
+}
+
+static void qlcnic_83xx_mbx_poll_work(struct work_struct *work)
+{
+       struct qlcnic_adapter *adapter;
+
+       adapter = container_of(work, struct qlcnic_adapter, mbx_poll_work.work);
+
+       if (!test_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state))
+               return;
+
+       qlcnic_83xx_process_aen(adapter);
+       queue_delayed_work(adapter->qlcnic_wq, &adapter->mbx_poll_work,
+                          (HZ / 10));
+}
+
+void qlcnic_83xx_enable_mbx_poll(struct qlcnic_adapter *adapter)
+{
+       if (test_and_set_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state))
+               return;
+
+       INIT_DELAYED_WORK(&adapter->mbx_poll_work, qlcnic_83xx_mbx_poll_work);
+}
+
+void qlcnic_83xx_disable_mbx_poll(struct qlcnic_adapter *adapter)
+{
+       if (!test_and_clear_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state))
+               return;
+       cancel_delayed_work_sync(&adapter->mbx_poll_work);
+}
+
 static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)
 {
        int index, i, err, sds_mbx_size;
@@ -1004,7 +982,8 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)
                sds = &recv_ctx->sds_rings[i];
                sds->consumer = 0;
                memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds));
-               sds_mbx.phy_addr = sds->phys_addr;
+               sds_mbx.phy_addr_low = LSD(sds->phys_addr);
+               sds_mbx.phy_addr_high = MSD(sds->phys_addr);
                sds_mbx.sds_ring_size = sds->num_desc;
 
                if (adapter->flags & QLCNIC_MSIX_ENABLED)
@@ -1050,6 +1029,32 @@ out:
        return err;
 }
 
+void qlcnic_83xx_del_rx_ctx(struct qlcnic_adapter *adapter)
+{
+       int err;
+       u32 temp = 0;
+       struct qlcnic_cmd_args cmd;
+       struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX))
+               return;
+
+       if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
+               cmd.req.arg[0] |= (0x3 << 29);
+
+       if (qlcnic_sriov_pf_check(adapter))
+               qlcnic_pf_set_interface_id_del_rx_ctx(adapter, &temp);
+
+       cmd.req.arg[1] = recv_ctx->context_id | temp;
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err)
+               dev_err(&adapter->pdev->dev,
+                       "Failed to destroy rx ctx in firmware\n");
+
+       recv_ctx->state = QLCNIC_HOST_CTX_STATE_FREED;
+       qlcnic_free_mbx_args(&cmd);
+}
+
 int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
 {
        int i, err, index, sds_mbx_size, rds_mbx_size;
@@ -1080,9 +1085,17 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
        /* set mailbox hdr and capabilities */
        qlcnic_alloc_mbx_args(&cmd, adapter,
                              QLCNIC_CMD_CREATE_RX_CTX);
+
+       if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
+               cmd.req.arg[0] |= (0x3 << 29);
+
        cmd.req.arg[1] = cap;
        cmd.req.arg[5] = 1 | (num_rds << 5) | (num_sds << 8) |
                         (QLC_83XX_HOST_RDS_MODE_UNIQUE << 16);
+
+       if (qlcnic_sriov_pf_check(adapter))
+               qlcnic_pf_set_interface_id_create_rx_ctx(adapter,
+                                                        &cmd.req.arg[6]);
        /* set up status rings, mbx 8-57/87 */
        index = QLC_83XX_HOST_SDS_MBX_IDX;
        for (i = 0; i < num_sds; i++) {
@@ -1090,7 +1103,8 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
                sds = &recv_ctx->sds_rings[i];
                sds->consumer = 0;
                memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds));
-               sds_mbx.phy_addr = sds->phys_addr;
+               sds_mbx.phy_addr_low = LSD(sds->phys_addr);
+               sds_mbx.phy_addr_high = MSD(sds->phys_addr);
                sds_mbx.sds_ring_size = sds->num_desc;
                if (adapter->flags & QLCNIC_MSIX_ENABLED)
                        intrpt_id = ahw->intr_tbl[i].id;
@@ -1110,13 +1124,15 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
        rds = &recv_ctx->rds_rings[0];
        rds->producer = 0;
        memset(&rds_mbx, 0, rds_mbx_size);
-       rds_mbx.phy_addr_reg = rds->phys_addr;
+       rds_mbx.phy_addr_reg_low = LSD(rds->phys_addr);
+       rds_mbx.phy_addr_reg_high = MSD(rds->phys_addr);
        rds_mbx.reg_ring_sz = rds->dma_size;
        rds_mbx.reg_ring_len = rds->num_desc;
        /* Jumbo ring */
        rds = &recv_ctx->rds_rings[1];
        rds->producer = 0;
-       rds_mbx.phy_addr_jmb = rds->phys_addr;
+       rds_mbx.phy_addr_jmb_low = LSD(rds->phys_addr);
+       rds_mbx.phy_addr_jmb_high = MSD(rds->phys_addr);
        rds_mbx.jmb_ring_sz = rds->dma_size;
        rds_mbx.jmb_ring_len = rds->num_desc;
        buf = &cmd.req.arg[index];
@@ -1163,16 +1179,39 @@ out:
        return err;
 }
 
+void qlcnic_83xx_del_tx_ctx(struct qlcnic_adapter *adapter,
+                           struct qlcnic_host_tx_ring *tx_ring)
+{
+       struct qlcnic_cmd_args cmd;
+       u32 temp = 0;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX))
+               return;
+
+       if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
+               cmd.req.arg[0] |= (0x3 << 29);
+
+       if (qlcnic_sriov_pf_check(adapter))
+               qlcnic_pf_set_interface_id_del_tx_ctx(adapter, &temp);
+
+       cmd.req.arg[1] = tx_ring->ctx_id | temp;
+       if (qlcnic_issue_cmd(adapter, &cmd))
+               dev_err(&adapter->pdev->dev,
+                       "Failed to destroy tx ctx in firmware\n");
+       qlcnic_free_mbx_args(&cmd);
+}
+
 int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
                              struct qlcnic_host_tx_ring *tx, int ring)
 {
        int err;
        u16 msix_id;
-       u32 *buf, intr_mask;
+       u32 *buf, intr_mask, temp = 0;
        struct qlcnic_cmd_args cmd;
        struct qlcnic_tx_mbx mbx;
        struct qlcnic_tx_mbx_out *mbx_out;
        struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u32 msix_vector;
 
        /* Reset host resources */
        tx->producer = 0;
@@ -1182,13 +1221,21 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
        memset(&mbx, 0, sizeof(struct qlcnic_tx_mbx));
 
        /* setup mailbox inbox registerss */
-       mbx.phys_addr = tx->phys_addr;
-       mbx.cnsmr_index = tx->hw_cons_phys_addr;
+       mbx.phys_addr_low = LSD(tx->phys_addr);
+       mbx.phys_addr_high = MSD(tx->phys_addr);
+       mbx.cnsmr_index_low = LSD(tx->hw_cons_phys_addr);
+       mbx.cnsmr_index_high = MSD(tx->hw_cons_phys_addr);
        mbx.size = tx->num_desc;
-       if (adapter->flags & QLCNIC_MSIX_ENABLED)
-               msix_id = ahw->intr_tbl[adapter->max_sds_rings + ring].id;
-       else
+       if (adapter->flags & QLCNIC_MSIX_ENABLED) {
+               if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
+                       msix_vector = adapter->max_sds_rings + ring;
+               else
+                       msix_vector = adapter->max_sds_rings - 1;
+               msix_id = ahw->intr_tbl[msix_vector].id;
+       } else {
                msix_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID);
+       }
+
        if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST)
                mbx.intr_id = msix_id;
        else
@@ -1196,8 +1243,15 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
        mbx.src = 0;
 
        qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+
+       if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
+               cmd.req.arg[0] |= (0x3 << 29);
+
+       if (qlcnic_sriov_pf_check(adapter))
+               qlcnic_pf_set_interface_id_create_tx_ctx(adapter, &temp);
+
        cmd.req.arg[1] = QLCNIC_CAP0_LEGACY_CONTEXT;
-       cmd.req.arg[5] = QLCNIC_MAX_TX_QUEUES;
+       cmd.req.arg[5] = QLCNIC_MAX_TX_QUEUES | temp;
        buf = &cmd.req.arg[6];
        memcpy(buf, &mbx, sizeof(struct qlcnic_tx_mbx));
        /* send the mailbox command*/
@@ -1210,7 +1264,8 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
        mbx_out = (struct qlcnic_tx_mbx_out *)&cmd.rsp.arg[2];
        tx->crb_cmd_producer = ahw->pci_base0 + mbx_out->host_prod;
        tx->ctx_id = mbx_out->ctx_id;
-       if (adapter->flags & QLCNIC_MSIX_ENABLED) {
+       if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
+           !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
                intr_mask = ahw->intr_tbl[adapter->max_sds_rings + ring].src;
                tx->crb_intr_mask = ahw->pci_base0 + intr_mask;
        }
@@ -1267,7 +1322,8 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test)
 
        if (adapter->ahw->diag_test == QLCNIC_LOOPBACK_TEST) {
                /* disable and free mailbox interrupt */
-               qlcnic_83xx_free_mbx_intr(adapter);
+               if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
+                       qlcnic_83xx_free_mbx_intr(adapter);
                adapter->ahw->loopback_state = 0;
                adapter->ahw->hw_ops->setup_link_event(adapter, 1);
        }
@@ -1295,12 +1351,14 @@ static void qlcnic_83xx_diag_free_res(struct net_device *netdev,
        qlcnic_detach(adapter);
 
        if (adapter->ahw->diag_test == QLCNIC_LOOPBACK_TEST) {
-               err = qlcnic_83xx_setup_mbx_intr(adapter);
-               if (err) {
-                       dev_err(&adapter->pdev->dev,
-                               "%s: failed to setup mbx interrupt\n",
-                               __func__);
-                       goto out;
+               if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
+                       err = qlcnic_83xx_setup_mbx_intr(adapter);
+                       if (err) {
+                               dev_err(&adapter->pdev->dev,
+                                       "%s: failed to setup mbx interrupt\n",
+                                       __func__);
+                               goto out;
+                       }
                }
        }
        adapter->ahw->diag_test = 0;
@@ -1373,12 +1431,60 @@ mbx_err:
        }
 }
 
+int  qlcnic_83xx_set_led(struct net_device *netdev,
+                        enum ethtool_phys_id_state state)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       int err = -EIO, active = 1;
+
+       if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
+               netdev_warn(netdev,
+                           "LED test is not supported in non-privileged mode\n");
+               return -EOPNOTSUPP;
+       }
+
+       switch (state) {
+       case ETHTOOL_ID_ACTIVE:
+               if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state))
+                       return -EBUSY;
+
+               if (test_bit(__QLCNIC_RESETTING, &adapter->state))
+                       break;
+
+               err = qlcnic_83xx_config_led(adapter, active, 0);
+               if (err)
+                       netdev_err(netdev, "Failed to set LED blink state\n");
+               break;
+       case ETHTOOL_ID_INACTIVE:
+               active = 0;
+
+               if (test_bit(__QLCNIC_RESETTING, &adapter->state))
+                       break;
+
+               err = qlcnic_83xx_config_led(adapter, active, 0);
+               if (err)
+                       netdev_err(netdev, "Failed to reset LED blink state\n");
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       if (!active || err)
+               clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
+
+       return err;
+}
+
 void qlcnic_83xx_register_nic_idc_func(struct qlcnic_adapter *adapter,
                                       int enable)
 {
        struct qlcnic_cmd_args cmd;
        int status;
 
+       if (qlcnic_sriov_vf_check(adapter))
+               return;
+
        if (enable) {
                qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INIT_NIC_FUNC);
                cmd.req.arg[1] = BIT_0 | BIT_31;
@@ -1441,24 +1547,35 @@ int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *adapter, int enable)
        return err;
 }
 
+static void qlcnic_83xx_set_interface_id_promisc(struct qlcnic_adapter *adapter,
+                                                u32 *interface_id)
+{
+       if (qlcnic_sriov_pf_check(adapter)) {
+               qlcnic_pf_set_interface_id_promisc(adapter, interface_id);
+       } else {
+               if (!qlcnic_sriov_vf_check(adapter))
+                       *interface_id = adapter->recv_ctx->context_id << 16;
+       }
+}
+
 int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
 {
        int err;
-       u32 temp;
+       u32 temp = 0;
        struct qlcnic_cmd_args cmd;
 
        if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
                return -EIO;
 
        qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_MAC_RX_MODE);
-       temp = adapter->recv_ctx->context_id << 16;
+       qlcnic_83xx_set_interface_id_promisc(adapter, &temp);
        cmd.req.arg[1] = (mode ? 1 : 0) | temp;
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err)
                dev_info(&adapter->pdev->dev,
                         "Promiscous mode config failed\n");
-       qlcnic_free_mbx_args(&cmd);
 
+       qlcnic_free_mbx_args(&cmd);
        return err;
 }
 
@@ -1490,7 +1607,9 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
        /* Poll for link up event before running traffic */
        do {
                msleep(500);
-               qlcnic_83xx_process_aen(adapter);
+               if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
+                       qlcnic_83xx_process_aen(adapter);
+
                if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
                        dev_info(&adapter->pdev->dev,
                                 "Firmware didn't sent link up event to loopback request\n");
@@ -1550,7 +1669,9 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
        /* Wait for Link and IDC Completion AEN */
        do {
                msleep(300);
-               qlcnic_83xx_process_aen(adapter);
+               if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
+                       qlcnic_83xx_process_aen(adapter);
+
                if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
                        dev_err(&adapter->pdev->dev,
                                "FW did not generate IDC completion AEN\n");
@@ -1590,7 +1711,9 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
        /* Wait for Link and IDC Completion AEN */
        do {
                msleep(300);
-               qlcnic_83xx_process_aen(adapter);
+               if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
+                       qlcnic_83xx_process_aen(adapter);
+
                if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
                        dev_err(&adapter->pdev->dev,
                                "Firmware didn't sent IDC completion AEN\n");
@@ -1604,21 +1727,31 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
        return status;
 }
 
+static void qlcnic_83xx_set_interface_id_ipaddr(struct qlcnic_adapter *adapter,
+                                               u32 *interface_id)
+{
+       if (qlcnic_sriov_pf_check(adapter)) {
+               qlcnic_pf_set_interface_id_ipaddr(adapter, interface_id);
+       } else {
+               if (!qlcnic_sriov_vf_check(adapter))
+                       *interface_id = adapter->recv_ctx->context_id << 16;
+       }
+}
+
 void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip,
                               int mode)
 {
        int err;
-       u32 temp, temp_ip;
+       u32 temp = 0, temp_ip;
        struct qlcnic_cmd_args cmd;
 
        qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_IP_ADDR);
-       if (mode == QLCNIC_IP_UP) {
-               temp = adapter->recv_ctx->context_id << 16;
+       qlcnic_83xx_set_interface_id_ipaddr(adapter, &temp);
+
+       if (mode == QLCNIC_IP_UP)
                cmd.req.arg[1] = 1 | temp;
-       } else {
-               temp = adapter->recv_ctx->context_id << 16;
+       else
                cmd.req.arg[1] = 2 | temp;
-       }
 
        /*
         * Adapter needs IP address in network byte order.
@@ -1635,6 +1768,7 @@ void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip,
                dev_err(&adapter->netdev->dev,
                        "could not notify %s IP 0x%x request\n",
                        (mode == QLCNIC_IP_UP) ? "Add" : "Remove", ip);
+
        qlcnic_free_mbx_args(&cmd);
 }
 
@@ -1701,11 +1835,22 @@ int qlcnic_83xx_config_rss(struct qlcnic_adapter *adapter, int enable)
 
 }
 
+static void qlcnic_83xx_set_interface_id_macaddr(struct qlcnic_adapter *adapter,
+                                                u32 *interface_id)
+{
+       if (qlcnic_sriov_pf_check(adapter)) {
+               qlcnic_pf_set_interface_id_macaddr(adapter, interface_id);
+       } else {
+               if (!qlcnic_sriov_vf_check(adapter))
+                       *interface_id = adapter->recv_ctx->context_id << 16;
+       }
+}
+
 int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
-                                  __le16 vlan_id, u8 op)
+                                  u16 vlan_id, u8 op)
 {
        int err;
-       u32 *buf;
+       u32 *buf, temp = 0;
        struct qlcnic_cmd_args cmd;
        struct qlcnic_macvlan_mbx mv;
 
@@ -1715,11 +1860,21 @@ int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
        err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN);
        if (err)
                return err;
-       cmd.req.arg[1] = op | (1 << 8) |
-                       (adapter->recv_ctx->context_id << 16);
 
-       mv.vlan = le16_to_cpu(vlan_id);
-       memcpy(&mv.mac, addr, ETH_ALEN);
+       if (vlan_id)
+               op = (op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ?
+                    QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL;
+
+       cmd.req.arg[1] = op | (1 << 8);
+       qlcnic_83xx_set_interface_id_macaddr(adapter, &temp);
+       cmd.req.arg[1] |= temp;
+       mv.vlan = vlan_id;
+       mv.mac_addr0 = addr[0];
+       mv.mac_addr1 = addr[1];
+       mv.mac_addr2 = addr[2];
+       mv.mac_addr3 = addr[3];
+       mv.mac_addr4 = addr[4];
+       mv.mac_addr5 = addr[5];
        buf = &cmd.req.arg[2];
        memcpy(buf, &mv, sizeof(struct qlcnic_macvlan_mbx));
        err = qlcnic_issue_cmd(adapter, &cmd);
@@ -1732,7 +1887,7 @@ int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
 }
 
 void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *adapter, u64 *addr,
-                                 __le16 vlan_id)
+                                 u16 vlan_id)
 {
        u8 mac[ETH_ALEN];
        memcpy(&mac, addr, ETH_ALEN);
@@ -1832,7 +1987,7 @@ irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data)
 
        event = readl(QLCNIC_MBX_FW(adapter->ahw, 0));
        if (event &  QLCNIC_MBX_ASYNC_EVENT)
-               qlcnic_83xx_process_aen(adapter);
+               __qlcnic_83xx_process_aen(adapter);
 out:
        mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);
        writel(0, adapter->ahw->pci_base0 + mask);
@@ -2008,14 +2163,17 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter,
 int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *adapter, bool op_type)
 {
        int i, index, err;
-       bool type;
        u8 max_ints;
-       u32 val, temp;
+       u32 val, temp, type;
        struct qlcnic_cmd_args cmd;
 
        max_ints = adapter->ahw->num_msix - 1;
        qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT);
        cmd.req.arg[1] = max_ints;
+
+       if (qlcnic_sriov_vf_check(adapter))
+               cmd.req.arg[1] |= (adapter->ahw->pci_func << 8) | BIT_16;
+
        for (i = 0, index = 2; i < max_ints; i++) {
                type = op_type ? QLCNIC_INTRPT_ADD : QLCNIC_INTRPT_DEL;
                val = type | (adapter->ahw->intr_tbl[i].type << 4);
@@ -2169,7 +2327,7 @@ static int qlcnic_83xx_poll_flash_status_reg(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_enable_flash_write_op(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_enable_flash_write(struct qlcnic_adapter *adapter)
 {
        int ret;
        u32 cmd;
@@ -2187,7 +2345,7 @@ static int qlcnic_83xx_enable_flash_write_op(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_disable_flash_write_op(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_disable_flash_write(struct qlcnic_adapter *adapter)
 {
        int ret;
 
@@ -2261,7 +2419,7 @@ int qlcnic_83xx_erase_flash_sector(struct qlcnic_adapter *adapter,
                return -EIO;
 
        if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
-               ret = qlcnic_83xx_enable_flash_write_op(adapter);
+               ret = qlcnic_83xx_enable_flash_write(adapter);
                if (ret) {
                        qlcnic_83xx_unlock_flash(adapter);
                        dev_err(&adapter->pdev->dev,
@@ -2303,7 +2461,7 @@ int qlcnic_83xx_erase_flash_sector(struct qlcnic_adapter *adapter,
        }
 
        if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
-               ret = qlcnic_83xx_disable_flash_write_op(adapter);
+               ret = qlcnic_83xx_disable_flash_write(adapter);
                if (ret) {
                        qlcnic_83xx_unlock_flash(adapter);
                        dev_err(&adapter->pdev->dev,
@@ -2343,8 +2501,8 @@ int qlcnic_83xx_flash_bulk_write(struct qlcnic_adapter *adapter, u32 addr,
        u32 temp;
        int ret = -EIO;
 
-       if ((count < QLC_83XX_FLASH_BULK_WRITE_MIN) ||
-           (count > QLC_83XX_FLASH_BULK_WRITE_MAX)) {
+       if ((count < QLC_83XX_FLASH_WRITE_MIN) ||
+           (count > QLC_83XX_FLASH_WRITE_MAX)) {
                dev_err(&adapter->pdev->dev,
                        "%s: Invalid word count\n", __func__);
                return -EIO;
@@ -2622,13 +2780,19 @@ int qlcnic_83xx_flash_read32(struct qlcnic_adapter *adapter, u32 flash_addr,
 
 int qlcnic_83xx_test_link(struct qlcnic_adapter *adapter)
 {
+       u8 pci_func;
        int err;
        u32 config = 0, state;
        struct qlcnic_cmd_args cmd;
        struct qlcnic_hardware_context *ahw = adapter->ahw;
 
-       state = readl(ahw->pci_base0 + QLC_83XX_LINK_STATE(ahw->pci_func));
-       if (!QLC_83xx_FUNC_VAL(state, ahw->pci_func)) {
+       if (qlcnic_sriov_vf_check(adapter))
+               pci_func = adapter->portnum;
+       else
+               pci_func = ahw->pci_func;
+
+       state = readl(ahw->pci_base0 + QLC_83XX_LINK_STATE(pci_func));
+       if (!QLC_83xx_FUNC_VAL(state, pci_func)) {
                dev_info(&adapter->pdev->dev, "link state down\n");
                return config;
        }
index 61f81f6c84a996375f3a09a17c5c6fe6fa710b48..4be411c2628a0e518f12b0bb55cc93d615564a06 100644 (file)
@@ -12,6 +12,8 @@
 #include <linux/etherdevice.h>
 #include "qlcnic_hw.h"
 
+#define QLCNIC_83XX_BAR0_LENGTH 0x4000
+
 /* Directly mapped registers */
 #define QLC_83XX_CRB_WIN_BASE          0x3800
 #define QLC_83XX_CRB_WIN_FUNC(f)       (QLC_83XX_CRB_WIN_BASE+((f)*4))
 
 #define QLC_83XX_MAX_RESET_SEQ_ENTRIES 16
 
+/* status descriptor mailbox data
+ * @phy_addr_{low|high}: physical address of buffer
+ * @sds_ring_size: buffer size
+ * @intrpt_id: interrupt id
+ * @intrpt_val: source of interrupt
+ */
+struct qlcnic_sds_mbx {
+       u32     phy_addr_low;
+       u32     phy_addr_high;
+       u32     rsvd1[4];
+#if defined(__LITTLE_ENDIAN)
+       u16     sds_ring_size;
+       u16     rsvd2;
+       u16     rsvd3[2];
+       u16     intrpt_id;
+       u8      intrpt_val;
+       u8      rsvd4;
+#elif defined(__BIG_ENDIAN)
+       u16     rsvd2;
+       u16     sds_ring_size;
+       u16     rsvd3[2];
+       u8      rsvd4;
+       u8      intrpt_val;
+       u16     intrpt_id;
+#endif
+       u32     rsvd5;
+} __packed;
+
+/* receive descriptor buffer data
+ * phy_addr_reg_{low|high}: physical address of regular buffer
+ * phy_addr_jmb_{low|high}: physical address of jumbo buffer
+ * reg_ring_sz: size of regular buffer
+ * reg_ring_len: no. of entries in regular buffer
+ * jmb_ring_len: no. of entries in jumbo buffer
+ * jmb_ring_sz: size of jumbo buffer
+ */
+struct qlcnic_rds_mbx {
+       u32     phy_addr_reg_low;
+       u32     phy_addr_reg_high;
+       u32     phy_addr_jmb_low;
+       u32     phy_addr_jmb_high;
+#if defined(__LITTLE_ENDIAN)
+       u16     reg_ring_sz;
+       u16     reg_ring_len;
+       u16     jmb_ring_sz;
+       u16     jmb_ring_len;
+#elif defined(__BIG_ENDIAN)
+       u16     reg_ring_len;
+       u16     reg_ring_sz;
+       u16     jmb_ring_len;
+       u16     jmb_ring_sz;
+#endif
+} __packed;
+
+/* host producers for regular and jumbo rings */
+struct __host_producer_mbx {
+       u32     reg_buf;
+       u32     jmb_buf;
+} __packed;
+
+/* Receive context mailbox data outbox registers
+ * @state: state of the context
+ * @vport_id: virtual port id
+ * @context_id: receive context id
+ * @num_pci_func: number of pci functions of the port
+ * @phy_port: physical port id
+ */
+struct qlcnic_rcv_mbx_out {
+#if defined(__LITTLE_ENDIAN)
+       u8      rcv_num;
+       u8      sts_num;
+       u16     ctx_id;
+       u8      state;
+       u8      num_pci_func;
+       u8      phy_port;
+       u8      vport_id;
+#elif defined(__BIG_ENDIAN)
+       u16     ctx_id;
+       u8      sts_num;
+       u8      rcv_num;
+       u8      vport_id;
+       u8      phy_port;
+       u8      num_pci_func;
+       u8      state;
+#endif
+       u32     host_csmr[QLCNIC_MAX_RING_SETS];
+       struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS];
+} __packed;
+
+struct qlcnic_add_rings_mbx_out {
+#if defined(__LITTLE_ENDIAN)
+       u8      rcv_num;
+       u8      sts_num;
+       u16     ctx_id;
+#elif defined(__BIG_ENDIAN)
+       u16     ctx_id;
+       u8      sts_num;
+       u8      rcv_num;
+#endif
+       u32  host_csmr[QLCNIC_MAX_RING_SETS];
+       struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS];
+} __packed;
+
+/* Transmit context mailbox inbox registers
+ * @phys_addr_{low|high}: DMA address of the transmit buffer
+ * @cnsmr_index_{low|high}: host consumer index
+ * @size: legth of transmit buffer ring
+ * @intr_id: interrput id
+ * @src: src of interrupt
+ */
+struct qlcnic_tx_mbx {
+       u32     phys_addr_low;
+       u32     phys_addr_high;
+       u32     cnsmr_index_low;
+       u32     cnsmr_index_high;
+#if defined(__LITTLE_ENDIAN)
+       u16     size;
+       u16     intr_id;
+       u8      src;
+       u8      rsvd[3];
+#elif defined(__BIG_ENDIAN)
+       u16     intr_id;
+       u16     size;
+       u8      rsvd[3];
+       u8      src;
+#endif
+} __packed;
+
+/* Transmit context mailbox outbox registers
+ * @host_prod: host producer index
+ * @ctx_id: transmit context id
+ * @state: state of the transmit context
+ */
+
+struct qlcnic_tx_mbx_out {
+       u32     host_prod;
+#if defined(__LITTLE_ENDIAN)
+       u16     ctx_id;
+       u8      state;
+       u8      rsvd;
+#elif defined(__BIG_ENDIAN)
+       u8      rsvd;
+       u8      state;
+       u16     ctx_id;
+#endif
+} __packed;
+
 struct qlcnic_intrpt_config {
        u8      type;
        u8      enabled;
@@ -94,8 +243,23 @@ struct qlcnic_intrpt_config {
 };
 
 struct qlcnic_macvlan_mbx {
-       u8      mac[ETH_ALEN];
+#if defined(__LITTLE_ENDIAN)
+       u8      mac_addr0;
+       u8      mac_addr1;
+       u8      mac_addr2;
+       u8      mac_addr3;
+       u8      mac_addr4;
+       u8      mac_addr5;
        u16     vlan;
+#elif defined(__BIG_ENDIAN)
+       u8      mac_addr3;
+       u8      mac_addr2;
+       u8      mac_addr1;
+       u8      mac_addr0;
+       u16     vlan;
+       u8      mac_addr5;
+       u8      mac_addr4;
+#endif
 };
 
 struct qlc_83xx_fw_info {
@@ -153,6 +317,18 @@ struct qlc_83xx_idc {
        char            **name;
 };
 
+/* Device States */
+enum qlcnic_83xx_states {
+       QLC_83XX_IDC_DEV_UNKNOWN,
+       QLC_83XX_IDC_DEV_COLD,
+       QLC_83XX_IDC_DEV_INIT,
+       QLC_83XX_IDC_DEV_READY,
+       QLC_83XX_IDC_DEV_NEED_RESET,
+       QLC_83XX_IDC_DEV_NEED_QUISCENT,
+       QLC_83XX_IDC_DEV_FAILED,
+       QLC_83XX_IDC_DEV_QUISCENT
+};
+
 #define QLCNIC_MBX_RSP(reg)            LSW(reg)
 #define QLCNIC_MBX_NUM_REGS(reg)       (MSW(reg) & 0x1FF)
 #define QLCNIC_MBX_STATUS(reg)         (((reg) >> 25) & 0x7F)
@@ -226,6 +402,7 @@ struct qlc_83xx_idc {
 #define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val)        (val & 0x20000)
 #define QLC_83XX_VIRTUAL_NIC_MODE                      0xFF
 #define QLC_83XX_DEFAULT_MODE                          0x0
+#define QLC_83XX_SRIOV_MODE                            0x1
 #define QLCNIC_BRDTYPE_83XX_10G                        0x0083
 
 #define QLC_83XX_FLASH_SPI_STATUS              0x2808E010
@@ -242,8 +419,8 @@ struct qlc_83xx_idc {
 #define QLC_83XX_FLASH_BULK_WRITE_CMD          0xcadcadca
 #define QLC_83XX_FLASH_READ_RETRY_COUNT        5000
 #define QLC_83XX_FLASH_STATUS_READY            0x6
-#define QLC_83XX_FLASH_BULK_WRITE_MIN          2
-#define QLC_83XX_FLASH_BULK_WRITE_MAX          64
+#define QLC_83XX_FLASH_WRITE_MIN               2
+#define QLC_83XX_FLASH_WRITE_MAX               64
 #define QLC_83XX_FLASH_STATUS_REG_POLL_DELAY   1
 #define QLC_83XX_ERASE_MODE                    1
 #define QLC_83XX_WRITE_MODE                    2
@@ -336,7 +513,7 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *, u8);
 int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *, int);
 int qlcnic_83xx_config_rss(struct qlcnic_adapter *, int);
 int qlcnic_83xx_config_intr_coalesce(struct qlcnic_adapter *);
-void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *, u64 *, __le16);
+void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *, u64 *, u16);
 int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *, struct qlcnic_pci_info *);
 int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *);
 void qlcnic_83xx_register_nic_idc_func(struct qlcnic_adapter *, int);
@@ -351,11 +528,14 @@ int qlcnic_ind_rd(struct qlcnic_adapter *, u32);
 int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *);
 int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *,
                              struct qlcnic_host_tx_ring *, int);
+void qlcnic_83xx_del_rx_ctx(struct qlcnic_adapter *);
+void qlcnic_83xx_del_tx_ctx(struct qlcnic_adapter *,
+                           struct qlcnic_host_tx_ring *);
 int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *, struct qlcnic_info *, u8);
 int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *, int);
 void qlcnic_83xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *);
 int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *, bool);
-int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *, u8 *, __le16, u8);
+int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *, u8 *, u16, u8);
 int qlcnic_83xx_get_mac_address(struct qlcnic_adapter *, u8 *);
 void qlcnic_83xx_configure_mac(struct qlcnic_adapter *, u8 *, u8,
                               struct qlcnic_cmd_args *);
@@ -368,6 +548,7 @@ void qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *);
 irqreturn_t qlcnic_83xx_handle_aen(int, void *);
 int qlcnic_83xx_get_port_info(struct qlcnic_adapter *);
 void qlcnic_83xx_enable_mbx_intrpt(struct qlcnic_adapter *);
+void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *);
 irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *);
 irqreturn_t qlcnic_83xx_intr(int, void *);
 irqreturn_t qlcnic_83xx_tmp_intr(int, void *);
@@ -377,7 +558,7 @@ void qlcnic_83xx_disable_intr(struct qlcnic_adapter *,
                             struct qlcnic_host_sds_ring *);
 void qlcnic_83xx_check_vf(struct qlcnic_adapter *,
                          const struct pci_device_id *);
-void qlcnic_83xx_process_aen(struct qlcnic_adapter *);
+void __qlcnic_83xx_process_aen(struct qlcnic_adapter *);
 int qlcnic_83xx_get_port_config(struct qlcnic_adapter *);
 int qlcnic_83xx_set_port_config(struct qlcnic_adapter *);
 int qlcnic_enable_eswitch(struct qlcnic_adapter *, u8, u8);
@@ -401,7 +582,7 @@ int qlcnic_83xx_read_flash_descriptor_table(struct qlcnic_adapter *);
 int qlcnic_83xx_flash_read32(struct qlcnic_adapter *, u32, u8 *, int);
 int qlcnic_83xx_lockless_flash_read32(struct qlcnic_adapter *,
                                      u32, u8 *, int);
-int qlcnic_83xx_init(struct qlcnic_adapter *);
+int qlcnic_83xx_init(struct qlcnic_adapter *, int);
 int qlcnic_83xx_idc_ready_state_entry(struct qlcnic_adapter *);
 int qlcnic_83xx_check_hw_status(struct qlcnic_adapter *p_dev);
 void qlcnic_83xx_idc_poll_dev_state(struct work_struct *);
@@ -434,5 +615,12 @@ int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *);
 int qlcnic_83xx_get_registers(struct qlcnic_adapter *, u32 *);
 int qlcnic_83xx_loopback_test(struct net_device *, u8);
 int qlcnic_83xx_interrupt_test(struct net_device *);
+int qlcnic_83xx_set_led(struct net_device *, enum ethtool_phys_id_state);
 int qlcnic_83xx_flash_test(struct qlcnic_adapter *);
+int qlcnic_83xx_enable_flash_write(struct qlcnic_adapter *);
+int qlcnic_83xx_disable_flash_write(struct qlcnic_adapter *);
+u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *);
+u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *);
+void qlcnic_83xx_enable_mbx_poll(struct qlcnic_adapter *);
+void qlcnic_83xx_disable_mbx_poll(struct qlcnic_adapter *);
 #endif
index 5c033f268ca5c9ff2ba6c74c8221ac619c8f3483..6ea3a096054c1d2336b911a43d93b6620a3ebf58 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qlcnic for copyright and licensing details.
  */
 
+#include "qlcnic_sriov.h"
 #include "qlcnic.h"
 #include "qlcnic_hw.h"
 
 #define QLC_83XX_OPCODE_POLL_READ_LIST         0x0100
 
 static int qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter);
-static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
 static int qlcnic_83xx_check_heartbeat(struct qlcnic_adapter *p_dev);
 static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter);
 
 /* Template header */
 struct qlc_83xx_reset_hdr {
+#if defined(__LITTLE_ENDIAN)
        u16     version;
        u16     signature;
        u16     size;
@@ -39,14 +40,31 @@ struct qlc_83xx_reset_hdr {
        u16     checksum;
        u16     init_offset;
        u16     start_offset;
+#elif defined(__BIG_ENDIAN)
+       u16     signature;
+       u16     version;
+       u16     entries;
+       u16     size;
+       u16     checksum;
+       u16     hdr_size;
+       u16     start_offset;
+       u16     init_offset;
+#endif
 } __packed;
 
 /* Command entry header. */
 struct qlc_83xx_entry_hdr {
-       u16 cmd;
-       u16 size;
-       u16 count;
-       u16 delay;
+#if defined(__LITTLE_ENDIAN)
+       u16     cmd;
+       u16     size;
+       u16     count;
+       u16     delay;
+#elif defined(__BIG_ENDIAN)
+       u16     size;
+       u16     cmd;
+       u16     delay;
+       u16     count;
+#endif
 } __packed;
 
 /* Generic poll command */
@@ -60,10 +78,17 @@ struct qlc_83xx_rmw {
        u32     mask;
        u32     xor_value;
        u32     or_value;
+#if defined(__LITTLE_ENDIAN)
        u8      shl;
        u8      shr;
        u8      index_a;
        u8      rsvd;
+#elif defined(__BIG_ENDIAN)
+       u8      rsvd;
+       u8      index_a;
+       u8      shr;
+       u8      shl;
+#endif
 } __packed;
 
 /* Generic command with 2 DWORD */
@@ -90,18 +115,6 @@ static const char *const qlc_83xx_idc_states[] = {
        "Quiesce"
 };
 
-/* Device States */
-enum qlcnic_83xx_states {
-       QLC_83XX_IDC_DEV_UNKNOWN,
-       QLC_83XX_IDC_DEV_COLD,
-       QLC_83XX_IDC_DEV_INIT,
-       QLC_83XX_IDC_DEV_READY,
-       QLC_83XX_IDC_DEV_NEED_RESET,
-       QLC_83XX_IDC_DEV_NEED_QUISCENT,
-       QLC_83XX_IDC_DEV_FAILED,
-       QLC_83XX_IDC_DEV_QUISCENT
-};
-
 static int
 qlcnic_83xx_idc_check_driver_presence_reg(struct qlcnic_adapter *adapter)
 {
@@ -137,7 +150,8 @@ static int qlcnic_83xx_idc_update_audit_reg(struct qlcnic_adapter *adapter,
                        return -EBUSY;
        }
 
-       val = adapter->portnum & 0xf;
+       val = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_AUDIT);
+       val |= (adapter->portnum & 0xf);
        val |= mode << 7;
        if (mode)
                seconds = jiffies / HZ - adapter->ahw->idc.sec_counter;
@@ -376,14 +390,18 @@ static void qlcnic_83xx_idc_detach_driver(struct qlcnic_adapter *adapter)
        struct net_device *netdev = adapter->netdev;
 
        netif_device_detach(netdev);
+
        /* Disable mailbox interrupt */
-       QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, 0);
+       qlcnic_83xx_disable_mbx_intr(adapter);
        qlcnic_down(adapter, netdev);
        for (i = 0; i < adapter->ahw->num_msix; i++) {
                adapter->ahw->intr_tbl[i].id = i;
                adapter->ahw->intr_tbl[i].enabled = 0;
                adapter->ahw->intr_tbl[i].src = 0;
        }
+
+       if (qlcnic_sriov_pf_check(adapter))
+               qlcnic_sriov_pf_reset(adapter);
 }
 
 /**
@@ -585,9 +603,15 @@ static int qlcnic_83xx_idc_check_fan_failure(struct qlcnic_adapter *adapter)
 
 static int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
 {
+       int err;
+
        /* register for NIC IDC AEN Events */
        qlcnic_83xx_register_nic_idc_func(adapter, 1);
 
+       err = qlcnic_sriov_pf_reinit(adapter);
+       if (err)
+               return err;
+
        qlcnic_83xx_enable_mbx_intrpt(adapter);
 
        if (qlcnic_83xx_configure_opmode(adapter)) {
@@ -1893,6 +1917,9 @@ int qlcnic_83xx_config_default_opmode(struct qlcnic_adapter *adapter)
        qlcnic_get_func_no(adapter);
        op_mode = QLCRDX(ahw, QLC_83XX_DRV_OP_MODE);
 
+       if (test_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state))
+               op_mode = QLC_83XX_DEFAULT_OPMODE;
+
        if (op_mode == QLC_83XX_DEFAULT_OPMODE) {
                adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver;
                ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry;
@@ -1922,6 +1949,16 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
        ahw->max_mac_filters = nic_info.max_mac_filters;
        ahw->max_mtu = nic_info.max_mtu;
 
+       /* VNIC mode is detected by BIT_23 in capabilities. This bit is also
+        * set in case device is SRIOV capable. VNIC and SRIOV are mutually
+        * exclusive. So in case of sriov capable device load driver in
+        * default mode
+        */
+       if (test_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state)) {
+               ahw->nic_mode = QLC_83XX_DEFAULT_MODE;
+               return ahw->nic_mode;
+       }
+
        if (ahw->capabilities & BIT_23)
                ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE;
        else
@@ -1930,7 +1967,7 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
        return ahw->nic_mode;
 }
 
-static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
 {
        int ret;
 
@@ -2008,10 +2045,13 @@ static void qlcnic_83xx_clear_function_resources(struct qlcnic_adapter *adapter)
        }
 }
 
-int qlcnic_83xx_init(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
 
+       if (qlcnic_sriov_vf_check(adapter))
+               return qlcnic_sriov_vf_init(adapter, pci_using_dac);
+
        if (qlcnic_83xx_check_hw_status(adapter))
                return -EIO;
 
index a69097c6b84d4824cc3ed46e982c84fdfd3785c5..43562c256379539b8f739d4b72950886c067b75e 100644 (file)
@@ -382,8 +382,7 @@ out_free_rq:
        return err;
 }
 
-static void
-qlcnic_fw_cmd_destroy_rx_ctx(struct qlcnic_adapter *adapter)
+void qlcnic_82xx_fw_cmd_del_rx_ctx(struct qlcnic_adapter *adapter)
 {
        int err;
        struct qlcnic_cmd_args cmd;
@@ -422,22 +421,20 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
 
        rq_size = SIZEOF_HOSTRQ_TX(struct qlcnic_hostrq_tx_ctx);
        rq_addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size,
-                       &rq_phys_addr, GFP_KERNEL);
+                                    &rq_phys_addr, GFP_KERNEL | __GFP_ZERO);
        if (!rq_addr)
                return -ENOMEM;
 
        rsp_size = SIZEOF_CARDRSP_TX(struct qlcnic_cardrsp_tx_ctx);
        rsp_addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size,
-                       &rsp_phys_addr, GFP_KERNEL);
+                                     &rsp_phys_addr, GFP_KERNEL | __GFP_ZERO);
        if (!rsp_addr) {
                err = -ENOMEM;
                goto out_free_rq;
        }
 
-       memset(rq_addr, 0, rq_size);
        prq = rq_addr;
 
-       memset(rsp_addr, 0, rsp_size);
        prsp = rsp_addr;
 
        prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr);
@@ -486,13 +483,13 @@ out_free_rq:
        return err;
 }
 
-static void
-qlcnic_fw_cmd_destroy_tx_ctx(struct qlcnic_adapter *adapter,
-                            struct qlcnic_host_tx_ring *tx_ring)
+void qlcnic_82xx_fw_cmd_del_tx_ctx(struct qlcnic_adapter *adapter,
+                                  struct qlcnic_host_tx_ring *tx_ring)
 {
        struct qlcnic_cmd_args cmd;
 
        qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX);
+
        cmd.req.arg[1] = tx_ring->ctx_id;
        if (qlcnic_issue_cmd(adapter, &cmd))
                dev_err(&adapter->pdev->dev,
@@ -532,20 +529,15 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
                ptr = (__le32 *)dma_alloc_coherent(&pdev->dev, sizeof(u32),
                                                   &tx_ring->hw_cons_phys_addr,
                                                   GFP_KERNEL);
-
-               if (ptr == NULL) {
-                       dev_err(&pdev->dev, "failed to allocate tx consumer\n");
+               if (ptr == NULL)
                        return -ENOMEM;
-               }
+
                tx_ring->hw_consumer = ptr;
                /* cmd desc ring */
                addr = dma_alloc_coherent(&pdev->dev, TX_DESC_RINGSIZE(tx_ring),
                                          &tx_ring->phys_addr,
                                          GFP_KERNEL);
-
                if (addr == NULL) {
-                       dev_err(&pdev->dev,
-                               "failed to allocate tx desc ring\n");
                        err = -ENOMEM;
                        goto err_out_free;
                }
@@ -556,11 +548,9 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
        for (ring = 0; ring < adapter->max_rds_rings; ring++) {
                rds_ring = &recv_ctx->rds_rings[ring];
                addr = dma_alloc_coherent(&adapter->pdev->dev,
-                               RCV_DESC_RINGSIZE(rds_ring),
-                               &rds_ring->phys_addr, GFP_KERNEL);
+                                         RCV_DESC_RINGSIZE(rds_ring),
+                                         &rds_ring->phys_addr, GFP_KERNEL);
                if (addr == NULL) {
-                       dev_err(&pdev->dev,
-                               "failed to allocate rds ring [%d]\n", ring);
                        err = -ENOMEM;
                        goto err_out_free;
                }
@@ -572,11 +562,9 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
                sds_ring = &recv_ctx->sds_rings[ring];
 
                addr = dma_alloc_coherent(&adapter->pdev->dev,
-                               STATUS_DESC_RINGSIZE(sds_ring),
-                               &sds_ring->phys_addr, GFP_KERNEL);
+                                         STATUS_DESC_RINGSIZE(sds_ring),
+                                         &sds_ring->phys_addr, GFP_KERNEL);
                if (addr == NULL) {
-                       dev_err(&pdev->dev,
-                               "failed to allocate sds ring [%d]\n", ring);
                        err = -ENOMEM;
                        goto err_out_free;
                }
@@ -616,13 +604,12 @@ int qlcnic_fw_create_ctx(struct qlcnic_adapter *dev)
                                                  &dev->tx_ring[ring],
                                                  ring);
                if (err) {
-                       qlcnic_fw_cmd_destroy_rx_ctx(dev);
+                       qlcnic_fw_cmd_del_rx_ctx(dev);
                        if (ring == 0)
                                goto err_out;
 
                        for (i = 0; i < ring; i++)
-                               qlcnic_fw_cmd_destroy_tx_ctx(dev,
-                                                            &dev->tx_ring[i]);
+                               qlcnic_fw_cmd_del_tx_ctx(dev, &dev->tx_ring[i]);
 
                        goto err_out;
                }
@@ -644,10 +631,10 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
        int ring;
 
        if (test_and_clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) {
-               qlcnic_fw_cmd_destroy_rx_ctx(adapter);
+               qlcnic_fw_cmd_del_rx_ctx(adapter);
                for (ring = 0; ring < adapter->max_drv_tx_rings; ring++)
-                       qlcnic_fw_cmd_destroy_tx_ctx(adapter,
-                                                    &adapter->tx_ring[ring]);
+                       qlcnic_fw_cmd_del_tx_ctx(adapter,
+                                                &adapter->tx_ring[ring]);
 
                if (qlcnic_83xx_check(adapter) &&
                    (adapter->flags & QLCNIC_MSIX_ENABLED)) {
@@ -655,7 +642,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
                                qlcnic_83xx_config_intrpt(adapter, 0);
                }
                /* Allow dma queues to drain after context reset */
-               mdelay(20);
+               msleep(20);
        }
 }
 
@@ -753,10 +740,9 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,
        size_t  nic_size = sizeof(struct qlcnic_info_le);
 
        nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size,
-                               &nic_dma_t, GFP_KERNEL);
+                                          &nic_dma_t, GFP_KERNEL | __GFP_ZERO);
        if (!nic_info_addr)
                return -ENOMEM;
-       memset(nic_info_addr, 0, nic_size);
 
        nic_info = nic_info_addr;
 
@@ -804,11 +790,10 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter,
                return err;
 
        nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size,
-                       &nic_dma_t, GFP_KERNEL);
+                                          &nic_dma_t, GFP_KERNEL | __GFP_ZERO);
        if (!nic_info_addr)
                return -ENOMEM;
 
-       memset(nic_info_addr, 0, nic_size);
        nic_info = nic_info_addr;
 
        nic_info->pci_func = cpu_to_le16(nic->pci_func);
@@ -854,10 +839,10 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter,
        size_t pci_size = npar_size * QLCNIC_MAX_PCI_FUNC;
 
        pci_info_addr = dma_alloc_coherent(&adapter->pdev->dev, pci_size,
-                       &pci_info_dma_t, GFP_KERNEL);
+                                          &pci_info_dma_t,
+                                          GFP_KERNEL | __GFP_ZERO);
        if (!pci_info_addr)
                return -ENOMEM;
-       memset(pci_info_addr, 0, pci_size);
 
        npar = pci_info_addr;
        qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
@@ -949,12 +934,9 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
        }
 
        stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size,
-                       &stats_dma_t, GFP_KERNEL);
-       if (!stats_addr) {
-               dev_err(&adapter->pdev->dev, "Unable to allocate memory\n");
+                                       &stats_dma_t, GFP_KERNEL | __GFP_ZERO);
+       if (!stats_addr)
                return -ENOMEM;
-       }
-       memset(stats_addr, 0, stats_size);
 
        arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12;
        arg1 |= rx_tx << 15 | stats_size << 16;
@@ -1003,13 +985,10 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,
                return -ENOMEM;
 
        stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size,
-                       &stats_dma_t, GFP_KERNEL);
-       if (!stats_addr) {
-               dev_err(&adapter->pdev->dev,
-                       "%s: Unable to allocate memory.\n", __func__);
+                                       &stats_dma_t, GFP_KERNEL | __GFP_ZERO);
+       if (!stats_addr)
                return -ENOMEM;
-       }
-       memset(stats_addr, 0, stats_size);
+
        qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS);
        cmd.req.arg[1] = stats_size << 16;
        cmd.req.arg[2] = MSD(stats_dma_t);
index 5641f8ec49abab443b1122bfa19c51000408e493..9f7aade4667ce64ef8b7364f3b2179cda94577dd 100644 (file)
@@ -149,7 +149,8 @@ static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = {
 
 static inline int qlcnic_82xx_statistics(void)
 {
-       return QLCNIC_STATS_LEN + ARRAY_SIZE(qlcnic_83xx_mac_stats_strings);
+       return ARRAY_SIZE(qlcnic_device_gstrings_stats) +
+              ARRAY_SIZE(qlcnic_83xx_mac_stats_strings);
 }
 
 static inline int qlcnic_83xx_statistics(void)
@@ -858,9 +859,11 @@ clear_diag_irq:
        return ret;
 }
 
-#define QLCNIC_ILB_PKT_SIZE 64
-#define QLCNIC_NUM_ILB_PKT     16
-#define QLCNIC_ILB_MAX_RCV_LOOP 10
+#define QLCNIC_ILB_PKT_SIZE            64
+#define QLCNIC_NUM_ILB_PKT             16
+#define QLCNIC_ILB_MAX_RCV_LOOP                10
+#define QLCNIC_LB_PKT_POLL_DELAY_MSEC  1
+#define QLCNIC_LB_PKT_POLL_COUNT       20
 
 static void qlcnic_create_loopback_buff(unsigned char *data, u8 mac[])
 {
@@ -897,9 +900,9 @@ int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode)
                loop = 0;
 
                do {
-                       msleep(1);
+                       msleep(QLCNIC_LB_PKT_POLL_DELAY_MSEC);
                        qlcnic_process_rcv_ring_diag(sds_ring);
-                       if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP)
+                       if (loop++ > QLCNIC_LB_PKT_POLL_COUNT)
                                break;
                } while (!adapter->ahw->diag_cnt);
 
@@ -1070,8 +1073,7 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
        }
 }
 
-static void
-qlcnic_fill_stats(u64 *data, void *stats, int type)
+static u64 *qlcnic_fill_stats(u64 *data, void *stats, int type)
 {
        if (type == QLCNIC_MAC_STATS) {
                struct qlcnic_mac_statistics *mac_stats =
@@ -1120,6 +1122,7 @@ qlcnic_fill_stats(u64 *data, void *stats, int type)
                *data++ = QLCNIC_FILL_STATS(esw_stats->local_frames);
                *data++ = QLCNIC_FILL_STATS(esw_stats->numbytes);
        }
+       return data;
 }
 
 static void qlcnic_get_ethtool_stats(struct net_device *dev,
@@ -1147,7 +1150,7 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev,
                /* Retrieve MAC statistics from firmware */
                memset(&mac_stats, 0, sizeof(struct qlcnic_mac_statistics));
                qlcnic_get_mac_stats(adapter, &mac_stats);
-               qlcnic_fill_stats(data, &mac_stats, QLCNIC_MAC_STATS);
+               data = qlcnic_fill_stats(data, &mac_stats, QLCNIC_MAC_STATS);
        }
 
        if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
@@ -1159,7 +1162,7 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev,
        if (ret)
                return;
 
-       qlcnic_fill_stats(data, &port_stats.rx, QLCNIC_ESW_STATS);
+       data = qlcnic_fill_stats(data, &port_stats.rx, QLCNIC_ESW_STATS);
        ret = qlcnic_get_port_stats(adapter, adapter->ahw->pci_func,
                        QLCNIC_QUERY_TX_COUNTER, &port_stats.tx);
        if (ret)
@@ -1176,7 +1179,8 @@ static int qlcnic_set_led(struct net_device *dev,
        int err = -EIO, active = 1;
 
        if (qlcnic_83xx_check(adapter))
-               return -EOPNOTSUPP;
+               return qlcnic_83xx_set_led(dev, state);
+
        if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
                netdev_warn(dev, "LED test not supported for non "
                                "privilege function\n");
@@ -1537,3 +1541,25 @@ const struct ethtool_ops qlcnic_ethtool_ops = {
        .get_dump_data = qlcnic_get_dump_data,
        .set_dump = qlcnic_set_dump,
 };
+
+const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops = {
+       .get_settings           = qlcnic_get_settings,
+       .get_drvinfo            = qlcnic_get_drvinfo,
+       .get_regs_len           = qlcnic_get_regs_len,
+       .get_regs               = qlcnic_get_regs,
+       .get_link               = ethtool_op_get_link,
+       .get_eeprom_len         = qlcnic_get_eeprom_len,
+       .get_eeprom             = qlcnic_get_eeprom,
+       .get_ringparam          = qlcnic_get_ringparam,
+       .set_ringparam          = qlcnic_set_ringparam,
+       .get_channels           = qlcnic_get_channels,
+       .get_pauseparam         = qlcnic_get_pauseparam,
+       .get_wol                = qlcnic_get_wol,
+       .get_strings            = qlcnic_get_strings,
+       .get_ethtool_stats      = qlcnic_get_ethtool_stats,
+       .get_sset_count         = qlcnic_get_sset_count,
+       .get_coalesce           = qlcnic_get_intr_coalesce,
+       .set_coalesce           = qlcnic_set_intr_coalesce,
+       .set_msglevel           = qlcnic_set_msglevel,
+       .get_msglevel           = qlcnic_get_msglevel,
+};
index 44197ca1456ccc7a3754df1f3ca3026028374818..c0f0c0d0a7908822461cb74953d58a682cd85098 100644 (file)
@@ -669,7 +669,7 @@ enum {
 #define QLCNIC_CMDPEG_CHECK_RETRY_COUNT        60
 #define QLCNIC_CMDPEG_CHECK_DELAY      500
 #define QLCNIC_HEARTBEAT_PERIOD_MSECS  200
-#define QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT     45
+#define QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT     10
 
 #define QLCNIC_MAX_MC_COUNT            38
 #define QLCNIC_WATCHDOG_TIMEOUTVALUE   5
@@ -714,7 +714,9 @@ enum {
        QLCNIC_MGMT_FUNC        = 0,
        QLCNIC_PRIV_FUNC        = 1,
        QLCNIC_NON_PRIV_FUNC    = 2,
-       QLCNIC_UNKNOWN_FUNC_MODE = 3
+       QLCNIC_SRIOV_PF_FUNC    = 3,
+       QLCNIC_SRIOV_VF_FUNC    = 4,
+       QLCNIC_UNKNOWN_FUNC_MODE = 5
 };
 
 enum {
index f89cc7a3fe6c545702de105308e44490b530302e..6a6512ba9f38824f0d72a844aaa0d96a95415a8f 100644 (file)
@@ -423,7 +423,7 @@ qlcnic_send_cmd_descs(struct qlcnic_adapter *adapter,
 }
 
 int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
-                                  __le16 vlan_id, u8 op)
+                                  u16 vlan_id, u8 op)
 {
        struct qlcnic_nic_req req;
        struct qlcnic_mac_req *mac_req;
@@ -441,7 +441,7 @@ int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
        memcpy(mac_req->mac_addr, addr, 6);
 
        vlan_req = (struct qlcnic_vlan_req *)&req.words[1];
-       vlan_req->vlan_id = vlan_id;
+       vlan_req->vlan_id = cpu_to_le16(vlan_id);
 
        return qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);
 }
@@ -468,7 +468,7 @@ int qlcnic_nic_del_mac(struct qlcnic_adapter *adapter, const u8 *addr)
        return err;
 }
 
-int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)
+int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan)
 {
        struct list_head *head;
        struct qlcnic_mac_list_s *cur;
@@ -487,7 +487,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)
        memcpy(cur->mac_addr, addr, ETH_ALEN);
 
        if (qlcnic_sre_macaddr_change(adapter,
-                               cur->mac_addr, 0, QLCNIC_MAC_ADD)) {
+                               cur->mac_addr, vlan, QLCNIC_MAC_ADD)) {
                kfree(cur);
                return -EIO;
        }
@@ -496,7 +496,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)
        return 0;
 }
 
-void qlcnic_set_multi(struct net_device *netdev)
+void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct netdev_hw_addr *ha;
@@ -508,8 +508,9 @@ void qlcnic_set_multi(struct net_device *netdev)
        if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
                return;
 
-       qlcnic_nic_add_mac(adapter, adapter->mac_addr);
-       qlcnic_nic_add_mac(adapter, bcast_addr);
+       if (!qlcnic_sriov_vf_check(adapter))
+               qlcnic_nic_add_mac(adapter, adapter->mac_addr, vlan);
+       qlcnic_nic_add_mac(adapter, bcast_addr, vlan);
 
        if (netdev->flags & IFF_PROMISC) {
                if (!(adapter->flags & QLCNIC_PROMISC_DISABLED))
@@ -523,23 +524,55 @@ void qlcnic_set_multi(struct net_device *netdev)
                goto send_fw_cmd;
        }
 
-       if (!netdev_mc_empty(netdev)) {
+       if (!netdev_mc_empty(netdev) && !qlcnic_sriov_vf_check(adapter)) {
                netdev_for_each_mc_addr(ha, netdev) {
-                       qlcnic_nic_add_mac(adapter, ha->addr);
+                       qlcnic_nic_add_mac(adapter, ha->addr, vlan);
                }
        }
 
+       if (qlcnic_sriov_vf_check(adapter))
+               qlcnic_vf_add_mc_list(netdev, vlan);
+
 send_fw_cmd:
-       if (mode == VPORT_MISS_MODE_ACCEPT_ALL && !adapter->fdb_mac_learn) {
-               qlcnic_alloc_lb_filters_mem(adapter);
-               adapter->drv_mac_learn = true;
-       } else {
-               adapter->drv_mac_learn = false;
+       if (!qlcnic_sriov_vf_check(adapter)) {
+               if (mode == VPORT_MISS_MODE_ACCEPT_ALL &&
+                   !adapter->fdb_mac_learn) {
+                       qlcnic_alloc_lb_filters_mem(adapter);
+                       adapter->drv_mac_learn = true;
+               } else {
+                       adapter->drv_mac_learn = false;
+               }
        }
 
        qlcnic_nic_set_promisc(adapter, mode);
 }
 
+void qlcnic_set_multi(struct net_device *netdev)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct netdev_hw_addr *ha;
+       struct qlcnic_mac_list_s *cur;
+
+       if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
+               return;
+       if (qlcnic_sriov_vf_check(adapter)) {
+               if (!netdev_mc_empty(netdev)) {
+                       netdev_for_each_mc_addr(ha, netdev) {
+                               cur = kzalloc(sizeof(struct qlcnic_mac_list_s),
+                                             GFP_ATOMIC);
+                               if (cur == NULL)
+                                       break;
+                               memcpy(cur->mac_addr,
+                                      ha->addr, ETH_ALEN);
+                               list_add_tail(&cur->list, &adapter->vf_mc_list);
+                       }
+               }
+               qlcnic_sriov_vf_schedule_multi(adapter->netdev);
+               return;
+       }
+       __qlcnic_set_multi(netdev, 0);
+}
+
 int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
 {
        struct qlcnic_nic_req req;
@@ -559,7 +592,7 @@ int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
                                (struct cmd_desc_type0 *)&req, 1);
 }
 
-void qlcnic_free_mac_list(struct qlcnic_adapter *adapter)
+void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter)
 {
        struct qlcnic_mac_list_s *cur;
        struct list_head *head = &adapter->mac_list;
index 5b8749eda11fd2034a978446f4ffa481b0c27d91..95b1b57328385dac93c0268fb6bae262ae20a793 100644 (file)
@@ -83,6 +83,8 @@ enum qlcnic_regs {
 #define QLCNIC_CMD_CONFIG_PORT                 0x2e
 #define QLCNIC_CMD_TEMP_SIZE                   0x2f
 #define QLCNIC_CMD_GET_TEMP_HDR                        0x30
+#define QLCNIC_CMD_BC_EVENT_SETUP              0x31
+#define        QLCNIC_CMD_CONFIG_VPORT                 0x32
 #define QLCNIC_CMD_GET_MAC_STATS               0x37
 #define QLCNIC_CMD_SET_DRV_VER                 0x38
 #define QLCNIC_CMD_CONFIGURE_RSS               0x41
@@ -114,6 +116,7 @@ enum qlcnic_regs {
 #define QLCNIC_SET_FAC_DEF_MAC                 5
 
 #define QLCNIC_MBX_LINK_EVENT          0x8001
+#define QLCNIC_MBX_BC_EVENT            0x8002
 #define QLCNIC_MBX_COMP_EVENT          0x8100
 #define QLCNIC_MBX_REQUEST_EVENT       0x8101
 #define QLCNIC_MBX_TIME_EXTEND_EVENT   0x8102
@@ -156,7 +159,7 @@ int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32);
 int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
                         struct net_device *netdev);
 void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter,
-                              u64 *uaddr, __le16 vlan_id);
+                              u64 *uaddr, u16 vlan_id);
 void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter);
 int qlcnic_82xx_config_rss(struct qlcnic_adapter *adapter, int);
 void qlcnic_82xx_config_ipaddr(struct qlcnic_adapter *adapter,
@@ -175,7 +178,10 @@ int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter,
 int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *);
 int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *,
                                     struct qlcnic_host_tx_ring *tx_ring, int);
-int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *, u8 *, __le16, u8);
+void qlcnic_82xx_fw_cmd_del_rx_ctx(struct qlcnic_adapter *);
+void qlcnic_82xx_fw_cmd_del_tx_ctx(struct qlcnic_adapter *,
+                                  struct qlcnic_host_tx_ring *);
+int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *, u8 *, u16, u8);
 int qlcnic_82xx_get_mac_address(struct qlcnic_adapter *, u8*);
 int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *, struct qlcnic_info *, u8);
 int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *);
index 5fa847fe388a9df3b7f78b44b63f72bffaaeeced..d3f8797efcc3d7a6012ea56ddb4feda210727f22 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/if_vlan.h>
 #include <net/ip.h>
 #include <linux/ipv6.h>
+#include <net/checksum.h>
 
 #include "qlcnic.h"
 
@@ -146,7 +147,10 @@ static inline u8 qlcnic_mac_hash(u64 mac)
 static inline u32 qlcnic_get_ref_handle(struct qlcnic_adapter *adapter,
                                        u16 handle, u8 ring_id)
 {
-       if (adapter->pdev->device == PCI_DEVICE_ID_QLOGIC_QLE834X)
+       unsigned short device = adapter->pdev->device;
+
+       if ((device == PCI_DEVICE_ID_QLOGIC_QLE834X) ||
+           (device == PCI_DEVICE_ID_QLOGIC_VF_QLE834X))
                return handle | (ring_id << 15);
        else
                return handle;
@@ -158,7 +162,7 @@ static inline int qlcnic_82xx_is_lb_pkt(u64 sts_data)
 }
 
 void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter, struct sk_buff *skb,
-                         int loopback_pkt, __le16 vlan_id)
+                         int loopback_pkt, u16 vlan_id)
 {
        struct ethhdr *phdr = (struct ethhdr *)(skb->data);
        struct qlcnic_filter *fil, *tmp_fil;
@@ -236,7 +240,7 @@ void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter, struct sk_buff *skb,
 }
 
 void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
-                              __le16 vlan_id)
+                              u16 vlan_id)
 {
        struct cmd_desc_type0 *hwdesc;
        struct qlcnic_nic_req *req;
@@ -261,7 +265,7 @@ void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
        memcpy(mac_req->mac_addr, &uaddr, ETH_ALEN);
 
        vlan_req = (struct qlcnic_vlan_req *)&req->words[1];
-       vlan_req->vlan_id = vlan_id;
+       vlan_req->vlan_id = cpu_to_le16(vlan_id);
 
        tx_ring->producer = get_next_index(producer, tx_ring->num_desc);
        smp_mb();
@@ -277,7 +281,7 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
        struct net_device *netdev = adapter->netdev;
        struct ethhdr *phdr = (struct ethhdr *)(skb->data);
        u64 src_addr = 0;
-       __le16 vlan_id = 0;
+       u16 vlan_id = 0;
        u8 hindex;
 
        if (ether_addr_equal(phdr->h_source, adapter->mac_addr))
@@ -340,14 +344,14 @@ static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
                flags = FLAGS_VLAN_OOB;
                vlan_tci = vlan_tx_tag_get(skb);
        }
-       if (unlikely(adapter->pvid)) {
+       if (unlikely(adapter->tx_pvid)) {
                if (vlan_tci && !(adapter->flags & QLCNIC_TAGGING_ENABLED))
                        return -EIO;
                if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED))
                        goto set_flags;
 
                flags = FLAGS_VLAN_OOB;
-               vlan_tci = adapter->pvid;
+               vlan_tci = adapter->tx_pvid;
        }
 set_flags:
        qlcnic_set_tx_vlan_tci(first_desc, vlan_tci);
@@ -975,10 +979,10 @@ static inline int qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter,
                memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2);
                skb_pull(skb, VLAN_HLEN);
        }
-       if (!adapter->pvid)
+       if (!adapter->rx_pvid)
                return 0;
 
-       if (*vlan_tag == adapter->pvid) {
+       if (*vlan_tag == adapter->rx_pvid) {
                /* Outer vlan tag. Packet should follow non-vlan path */
                *vlan_tag = 0xffff;
                return 0;
@@ -1024,8 +1028,7 @@ qlcnic_process_rcv(struct qlcnic_adapter *adapter,
            (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
                t_vid = 0;
                is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
-               qlcnic_add_lb_filter(adapter, skb, is_lb_pkt,
-                                    cpu_to_le16(t_vid));
+               qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
        }
 
        if (length > rds_ring->skb_size)
@@ -1045,7 +1048,7 @@ qlcnic_process_rcv(struct qlcnic_adapter *adapter,
        skb->protocol = eth_type_trans(skb, netdev);
 
        if (vid != 0xffff)
-               __vlan_hwaccel_put_tag(skb, vid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
 
        napi_gro_receive(&sds_ring->napi, skb);
 
@@ -1102,8 +1105,7 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,
            (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
                t_vid = 0;
                is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
-               qlcnic_add_lb_filter(adapter, skb, is_lb_pkt,
-                                    cpu_to_le16(t_vid));
+               qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
        }
 
        if (timestamp)
@@ -1131,9 +1133,8 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,
                iph = (struct iphdr *)skb->data;
                th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
                length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
+               csum_replace2(&iph->check, iph->tot_len, htons(length));
                iph->tot_len = htons(length);
-               iph->check = 0;
-               iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
        }
 
        th->psh = push;
@@ -1149,7 +1150,7 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,
        }
 
        if (vid != 0xffff)
-               __vlan_hwaccel_put_tag(skb, vid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
        netif_receive_skb(skb);
 
        adapter->stats.lro_pkts++;
@@ -1496,8 +1497,7 @@ qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter,
            (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
                t_vid = 0;
                is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 0);
-               qlcnic_add_lb_filter(adapter, skb, is_lb_pkt,
-                                    cpu_to_le16(t_vid));
+               qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
        }
 
        if (length > rds_ring->skb_size)
@@ -1514,7 +1514,7 @@ qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter,
        skb->protocol = eth_type_trans(skb, netdev);
 
        if (vid != 0xffff)
-               __vlan_hwaccel_put_tag(skb, vid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
 
        napi_gro_receive(&sds_ring->napi, skb);
 
@@ -1566,8 +1566,7 @@ qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter,
            (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
                t_vid = 0;
                is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 1);
-               qlcnic_add_lb_filter(adapter, skb, is_lb_pkt,
-                                    cpu_to_le16(t_vid));
+               qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
        }
        if (qlcnic_83xx_is_tstamp(sts_data[1]))
                data_offset = l4_hdr_offset + QLCNIC_TCP_TS_HDR_SIZE;
@@ -1594,9 +1593,8 @@ qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter,
                iph = (struct iphdr *)skb->data;
                th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
                length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
+               csum_replace2(&iph->check, iph->tot_len, htons(length));
                iph->tot_len = htons(length);
-               iph->check = 0;
-               iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
        }
 
        th->psh = push;
@@ -1612,7 +1610,7 @@ qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter,
        }
 
        if (vid != 0xffff)
-               __vlan_hwaccel_put_tag(skb, vid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
 
        netif_receive_skb(skb);
 
@@ -1691,6 +1689,29 @@ skip:
        return count;
 }
 
+static int qlcnic_83xx_msix_sriov_vf_poll(struct napi_struct *napi, int budget)
+{
+       int tx_complete;
+       int work_done;
+       struct qlcnic_host_sds_ring *sds_ring;
+       struct qlcnic_adapter *adapter;
+       struct qlcnic_host_tx_ring *tx_ring;
+
+       sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
+       adapter = sds_ring->adapter;
+       /* tx ring count = 1 */
+       tx_ring = adapter->tx_ring;
+
+       tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
+       work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);
+       if ((work_done < budget) && tx_complete) {
+               napi_complete(&sds_ring->napi);
+               qlcnic_83xx_enable_intr(adapter, sds_ring);
+       }
+
+       return work_done;
+}
+
 static int qlcnic_83xx_poll(struct napi_struct *napi, int budget)
 {
        int tx_complete;
@@ -1768,7 +1789,8 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
                        qlcnic_83xx_enable_intr(adapter, sds_ring);
        }
 
-       if (adapter->flags & QLCNIC_MSIX_ENABLED) {
+       if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
+           !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
                for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        napi_enable(&tx_ring->napi);
@@ -1795,7 +1817,8 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
                napi_disable(&sds_ring->napi);
        }
 
-       if (adapter->flags & QLCNIC_MSIX_ENABLED) {
+       if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
+           !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
                for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        qlcnic_83xx_disable_tx_intr(adapter, tx_ring);
@@ -1808,7 +1831,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
 int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
                         struct net_device *netdev)
 {
-       int ring, max_sds_rings;
+       int ring, max_sds_rings, temp;
        struct qlcnic_host_sds_ring *sds_ring;
        struct qlcnic_host_tx_ring *tx_ring;
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
@@ -1819,14 +1842,23 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
        max_sds_rings = adapter->max_sds_rings;
        for (ring = 0; ring < adapter->max_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
-               if (adapter->flags & QLCNIC_MSIX_ENABLED)
-                       netif_napi_add(netdev, &sds_ring->napi,
-                                      qlcnic_83xx_rx_poll,
-                                      QLCNIC_NETDEV_WEIGHT * 2);
-               else
+               if (adapter->flags & QLCNIC_MSIX_ENABLED) {
+                       if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
+                               netif_napi_add(netdev, &sds_ring->napi,
+                                              qlcnic_83xx_rx_poll,
+                                              QLCNIC_NETDEV_WEIGHT * 2);
+                       } else {
+                               temp = QLCNIC_NETDEV_WEIGHT / max_sds_rings;
+                               netif_napi_add(netdev, &sds_ring->napi,
+                                              qlcnic_83xx_msix_sriov_vf_poll,
+                                              temp);
+                       }
+
+               } else {
                        netif_napi_add(netdev, &sds_ring->napi,
                                       qlcnic_83xx_poll,
                                       QLCNIC_NETDEV_WEIGHT / max_sds_rings);
+               }
        }
 
        if (qlcnic_alloc_tx_rings(adapter, netdev)) {
@@ -1834,7 +1866,8 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
                return -ENOMEM;
        }
 
-       if (adapter->flags & QLCNIC_MSIX_ENABLED) {
+       if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
+           !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
                for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        netif_napi_add(netdev, &tx_ring->napi,
@@ -1860,7 +1893,8 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter)
 
        qlcnic_free_sds_rings(adapter->recv_ctx);
 
-       if ((adapter->flags & QLCNIC_MSIX_ENABLED)) {
+       if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
+           !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
                for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        netif_napi_del(&tx_ring->napi);
index 28a6d483836431f7d06fdb44c54fa3c854432449..247a9f9b7bdc1139abeb6a5f92046b142ba3fea5 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/interrupt.h>
 
 #include "qlcnic.h"
+#include "qlcnic_sriov.h"
 #include "qlcnic_hw.h"
 
 #include <linux/swab.h>
@@ -85,8 +86,8 @@ static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *);
 static int qlcnicvf_start_firmware(struct qlcnic_adapter *);
 static void qlcnic_set_netdev_features(struct qlcnic_adapter *,
                                struct qlcnic_esw_func_cfg *);
-static int qlcnic_vlan_rx_add(struct net_device *, u16);
-static int qlcnic_vlan_rx_del(struct net_device *, u16);
+static int qlcnic_vlan_rx_add(struct net_device *, __be16, u16);
+static int qlcnic_vlan_rx_del(struct net_device *, __be16, u16);
 
 #define QLCNIC_IS_TSO_CAPABLE(adapter) \
        ((adapter)->ahw->capabilities & QLCNIC_FW_CAPABILITY_TSO)
@@ -109,6 +110,7 @@ static u32 qlcnic_vlan_tx_check(struct qlcnic_adapter *adapter)
 static DEFINE_PCI_DEVICE_TABLE(qlcnic_pci_tbl) = {
        ENTRY(PCI_DEVICE_ID_QLOGIC_QLE824X),
        ENTRY(PCI_DEVICE_ID_QLOGIC_QLE834X),
+       ENTRY(PCI_DEVICE_ID_QLOGIC_VF_QLE834X),
        {0,}
 };
 
@@ -198,8 +200,7 @@ void qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx)
        recv_ctx->sds_rings = NULL;
 }
 
-static int
-qlcnic_read_mac_addr(struct qlcnic_adapter *adapter)
+int qlcnic_read_mac_addr(struct qlcnic_adapter *adapter)
 {
        u8 mac_addr[ETH_ALEN];
        struct net_device *netdev = adapter->netdev;
@@ -225,6 +226,9 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p)
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct sockaddr *addr = p;
 
+       if (qlcnic_sriov_vf_check(adapter))
+               return -EINVAL;
+
        if ((adapter->flags & QLCNIC_MAC_OVERRIDE_DISABLED))
                return -EOPNOTSUPP;
 
@@ -253,11 +257,8 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        int err = -EOPNOTSUPP;
 
-       if (!adapter->fdb_mac_learn) {
-               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
-                       __func__);
-               return err;
-       }
+       if (!adapter->fdb_mac_learn)
+               return ndo_dflt_fdb_del(ndm, tb, netdev, addr);
 
        if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
                if (is_unicast_ether_addr(addr))
@@ -277,11 +278,8 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        int err = 0;
 
-       if (!adapter->fdb_mac_learn) {
-               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
-                       __func__);
-               return -EOPNOTSUPP;
-       }
+       if (!adapter->fdb_mac_learn)
+               return ndo_dflt_fdb_add(ndm, tb, netdev, addr, flags);
 
        if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
                pr_info("%s: FDB e-switch is not enabled\n", __func__);
@@ -292,7 +290,7 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                return err;
 
        if (is_unicast_ether_addr(addr))
-               err = qlcnic_nic_add_mac(adapter, addr);
+               err = qlcnic_nic_add_mac(adapter, addr, 0);
        else if (is_multicast_ether_addr(addr))
                err = dev_mc_add_excl(netdev, addr);
        else
@@ -306,11 +304,8 @@ static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
 
-       if (!adapter->fdb_mac_learn) {
-               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
-                       __func__);
-               return -EOPNOTSUPP;
-       }
+       if (!adapter->fdb_mac_learn)
+               return ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
 
        if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
                idx = ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
@@ -346,6 +341,12 @@ static const struct net_device_ops qlcnic_netdev_ops = {
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = qlcnic_poll_controller,
 #endif
+#ifdef CONFIG_QLCNIC_SRIOV
+       .ndo_set_vf_mac         = qlcnic_sriov_set_vf_mac,
+       .ndo_set_vf_tx_rate     = qlcnic_sriov_set_vf_tx_rate,
+       .ndo_get_vf_config      = qlcnic_sriov_get_vf_config,
+       .ndo_set_vf_vlan        = qlcnic_sriov_set_vf_vlan,
+#endif
 };
 
 static const struct net_device_ops qlcnic_netdev_failed_ops = {
@@ -387,6 +388,8 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {
        .process_lb_rcv_ring_diag       = qlcnic_82xx_process_rcv_ring_diag,
        .create_rx_ctx                  = qlcnic_82xx_fw_cmd_create_rx_ctx,
        .create_tx_ctx                  = qlcnic_82xx_fw_cmd_create_tx_ctx,
+       .del_rx_ctx                     = qlcnic_82xx_fw_cmd_del_rx_ctx,
+       .del_tx_ctx                     = qlcnic_82xx_fw_cmd_del_tx_ctx,
        .setup_link_event               = qlcnic_82xx_linkevent_request,
        .get_nic_info                   = qlcnic_82xx_get_nic_info,
        .get_pci_info                   = qlcnic_82xx_get_pci_info,
@@ -402,13 +405,22 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {
        .config_promisc_mode            = qlcnic_82xx_nic_set_promisc,
        .change_l2_filter               = qlcnic_82xx_change_filter,
        .get_board_info                 = qlcnic_82xx_get_board_info,
+       .free_mac_list                  = qlcnic_82xx_free_mac_list,
 };
 
 int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
 {
        struct pci_dev *pdev = adapter->pdev;
        int err = -1, i;
-       int max_tx_rings;
+       int max_tx_rings, tx_vector;
+
+       if (adapter->flags & QLCNIC_TX_INTR_SHARED) {
+               max_tx_rings = 0;
+               tx_vector = 0;
+       } else {
+               max_tx_rings = adapter->max_drv_tx_rings;
+               tx_vector = 1;
+       }
 
        if (!adapter->msix_entries) {
                adapter->msix_entries = kcalloc(num_msix,
@@ -431,7 +443,6 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
                        if (qlcnic_83xx_check(adapter)) {
                                adapter->ahw->num_msix = num_msix;
                                /* subtract mail box and tx ring vectors */
-                               max_tx_rings = adapter->max_drv_tx_rings;
                                adapter->max_sds_rings = num_msix -
                                                         max_tx_rings - 1;
                        } else {
@@ -444,11 +455,11 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
                                 "Unable to allocate %d MSI-X interrupt vectors\n",
                                 num_msix);
                        if (qlcnic_83xx_check(adapter)) {
-                               if (err < QLC_83XX_MINIMUM_VECTOR)
+                               if (err < (QLC_83XX_MINIMUM_VECTOR - tx_vector))
                                        return err;
-                               err -= (adapter->max_drv_tx_rings + 1);
+                               err -= (max_tx_rings + 1);
                                num_msix = rounddown_pow_of_two(err);
-                               num_msix += (adapter->max_drv_tx_rings + 1);
+                               num_msix += (max_tx_rings + 1);
                        } else {
                                num_msix = rounddown_pow_of_two(err);
                        }
@@ -542,11 +553,10 @@ void qlcnic_teardown_intr(struct qlcnic_adapter *adapter)
        }
 }
 
-static void
-qlcnic_cleanup_pci_map(struct qlcnic_adapter *adapter)
+static void qlcnic_cleanup_pci_map(struct qlcnic_hardware_context *ahw)
 {
-       if (adapter->ahw->pci_base0 != NULL)
-               iounmap(adapter->ahw->pci_base0);
+       if (ahw->pci_base0 != NULL)
+               iounmap(ahw->pci_base0);
 }
 
 static int qlcnic_get_act_pci_func(struct qlcnic_adapter *adapter)
@@ -721,6 +731,7 @@ static void qlcnic_get_bar_length(u32 dev_id, ulong *bar)
                *bar = QLCNIC_82XX_BAR0_LENGTH;
                break;
        case PCI_DEVICE_ID_QLOGIC_QLE834X:
+       case PCI_DEVICE_ID_QLOGIC_VF_QLE834X:
                *bar = QLCNIC_83XX_BAR0_LENGTH;
                break;
        default:
@@ -751,7 +762,7 @@ static int qlcnic_setup_pci_map(struct pci_dev *pdev,
                return -EIO;
        }
 
-       dev_info(&pdev->dev, "%dMB memory map\n", (int)(mem_len>>20));
+       dev_info(&pdev->dev, "%dKB memory map\n", (int)(mem_len >> 10));
 
        ahw->pci_base0 = mem_ptr0;
        ahw->pci_len0 = pci_len0;
@@ -891,24 +902,50 @@ void qlcnic_set_vlan_config(struct qlcnic_adapter *adapter,
        else
                adapter->flags |= QLCNIC_TAGGING_ENABLED;
 
-       if (esw_cfg->vlan_id)
-               adapter->pvid = esw_cfg->vlan_id;
-       else
-               adapter->pvid = 0;
+       if (esw_cfg->vlan_id) {
+               adapter->rx_pvid = esw_cfg->vlan_id;
+               adapter->tx_pvid = esw_cfg->vlan_id;
+       } else {
+               adapter->rx_pvid = 0;
+               adapter->tx_pvid = 0;
+       }
 }
 
 static int
-qlcnic_vlan_rx_add(struct net_device *netdev, u16 vid)
+qlcnic_vlan_rx_add(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       int err;
+
+       if (qlcnic_sriov_vf_check(adapter)) {
+               err = qlcnic_sriov_cfg_vf_guest_vlan(adapter, vid, 1);
+               if (err) {
+                       netdev_err(netdev,
+                                  "Cannot add VLAN filter for VLAN id %d, err=%d",
+                                  vid, err);
+                       return err;
+               }
+       }
+
        set_bit(vid, adapter->vlans);
        return 0;
 }
 
 static int
-qlcnic_vlan_rx_del(struct net_device *netdev, u16 vid)
+qlcnic_vlan_rx_del(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       int err;
+
+       if (qlcnic_sriov_vf_check(adapter)) {
+               err = qlcnic_sriov_cfg_vf_guest_vlan(adapter, vid, 0);
+               if (err) {
+                       netdev_err(netdev,
+                                  "Cannot delete VLAN filter for VLAN id %d, err=%d",
+                                  vid, err);
+                       return err;
+               }
+       }
 
        qlcnic_restore_indev_addr(netdev, NETDEV_DOWN);
        clear_bit(vid, adapter->vlans);
@@ -1292,7 +1329,8 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
                        }
                }
                if (qlcnic_83xx_check(adapter) &&
-                   (adapter->flags & QLCNIC_MSIX_ENABLED)) {
+                   (adapter->flags & QLCNIC_MSIX_ENABLED) &&
+                   !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
                        handler = qlcnic_msix_tx_intr;
                        for (ring = 0; ring < adapter->max_drv_tx_rings;
                             ring++) {
@@ -1328,7 +1366,8 @@ qlcnic_free_irq(struct qlcnic_adapter *adapter)
                                free_irq(sds_ring->irq, sds_ring);
                        }
                }
-               if (qlcnic_83xx_check(adapter)) {
+               if (qlcnic_83xx_check(adapter) &&
+                   !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
                        for (ring = 0; ring < adapter->max_drv_tx_rings;
                             ring++) {
                                tx_ring = &adapter->tx_ring[ring];
@@ -1418,9 +1457,12 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
        if (!test_and_clear_bit(__QLCNIC_DEV_UP, &adapter->state))
                return;
 
+       if (qlcnic_sriov_vf_check(adapter))
+               qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);
        smp_mb();
        spin_lock(&adapter->tx_clean_lock);
        netif_carrier_off(netdev);
+       adapter->ahw->linkup = 0;
        netif_tx_disable(netdev);
 
        qlcnic_free_mac_list(adapter);
@@ -1685,7 +1727,7 @@ qlcnic_reset_context(struct qlcnic_adapter *adapter)
        return err;
 }
 
-static int
+int
 qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
                    int pci_using_dac)
 {
@@ -1701,11 +1743,14 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
 
        qlcnic_change_mtu(netdev, netdev->mtu);
 
-       SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops);
+       if (qlcnic_sriov_vf_check(adapter))
+               SET_ETHTOOL_OPS(netdev, &qlcnic_sriov_vf_ethtool_ops);
+       else
+               SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops);
 
        netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
                             NETIF_F_IPV6_CSUM | NETIF_F_GRO |
-                            NETIF_F_HW_VLAN_RX);
+                            NETIF_F_HW_VLAN_CTAG_RX);
        netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM |
                                  NETIF_F_IPV6_CSUM);
 
@@ -1720,7 +1765,10 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
        }
 
        if (qlcnic_vlan_tx_check(adapter))
-               netdev->features |= (NETIF_F_HW_VLAN_TX);
+               netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX);
+
+       if (qlcnic_sriov_vf_check(adapter))
+               netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO)
                netdev->features |= NETIF_F_LRO;
@@ -1820,6 +1868,9 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        u32 capab2;
        char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */
 
+       if (pdev->is_virtfn)
+               return -ENODEV;
+
        err = pci_enable_device(pdev);
        if (err)
                return err;
@@ -1844,12 +1895,18 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (!ahw)
                goto err_out_free_res;
 
-       if (ent->device == PCI_DEVICE_ID_QLOGIC_QLE824X) {
+       switch (ent->device) {
+       case PCI_DEVICE_ID_QLOGIC_QLE824X:
                ahw->hw_ops = &qlcnic_hw_ops;
-               ahw->reg_tbl = (u32 *)qlcnic_reg_tbl;
-       } else if (ent->device == PCI_DEVICE_ID_QLOGIC_QLE834X) {
+               ahw->reg_tbl = (u32 *) qlcnic_reg_tbl;
+               break;
+       case PCI_DEVICE_ID_QLOGIC_QLE834X:
                qlcnic_83xx_register_map(ahw);
-       } else {
+               break;
+       case PCI_DEVICE_ID_QLOGIC_VF_QLE834X:
+               qlcnic_sriov_vf_register_map(ahw);
+               break;
+       default:
                goto err_out_free_hw_res;
        }
 
@@ -1911,11 +1968,13 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        } else if (qlcnic_83xx_check(adapter)) {
                qlcnic_83xx_check_vf(adapter, ent);
                adapter->portnum = adapter->ahw->pci_func;
-               err = qlcnic_83xx_init(adapter);
+               err = qlcnic_83xx_init(adapter, pci_using_dac);
                if (err) {
                        dev_err(&pdev->dev, "%s: failed\n", __func__);
                        goto err_out_free_hw;
                }
+               if (qlcnic_sriov_vf_check(adapter))
+                       return 0;
        } else {
                dev_err(&pdev->dev,
                        "%s: failed. Please Reboot\n", __func__);
@@ -1932,6 +1991,12 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                        module_name(THIS_MODULE),
                        board_name, adapter->ahw->revision_id);
        }
+
+       if (qlcnic_83xx_check(adapter) && !qlcnic_use_msi_x &&
+           !!qlcnic_use_msi)
+               dev_warn(&pdev->dev,
+                        "83xx adapter do not support MSI interrupts\n");
+
        err = qlcnic_setup_intr(adapter, 0);
        if (err) {
                dev_err(&pdev->dev, "Failed to setup interrupt\n");
@@ -1999,7 +2064,7 @@ err_out_free_netdev:
        free_netdev(netdev);
 
 err_out_iounmap:
-       qlcnic_cleanup_pci_map(adapter);
+       qlcnic_cleanup_pci_map(ahw);
 
 err_out_free_hw_res:
        kfree(ahw);
@@ -2024,11 +2089,13 @@ static void qlcnic_remove(struct pci_dev *pdev)
                return;
 
        netdev = adapter->netdev;
+       qlcnic_sriov_pf_disable(adapter);
 
        qlcnic_cancel_idc_work(adapter);
        ahw = adapter->ahw;
 
        unregister_netdev(netdev);
+       qlcnic_sriov_cleanup(adapter);
 
        if (qlcnic_83xx_check(adapter)) {
                qlcnic_83xx_free_mbx_intr(adapter);
@@ -2054,7 +2121,7 @@ static void qlcnic_remove(struct pci_dev *pdev)
 
        qlcnic_remove_sysfs(adapter);
 
-       qlcnic_cleanup_pci_map(adapter);
+       qlcnic_cleanup_pci_map(adapter->ahw);
 
        qlcnic_release_firmware(adapter);
 
@@ -2084,6 +2151,7 @@ static int __qlcnic_shutdown(struct pci_dev *pdev)
        if (netif_running(netdev))
                qlcnic_down(adapter, netdev);
 
+       qlcnic_sriov_cleanup(adapter);
        if (qlcnic_82xx_check(adapter))
                qlcnic_clr_all_drv_state(adapter, 0);
 
@@ -3238,8 +3306,10 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, size_t len)
 
        qlcnic_detach(adapter);
 
-       if (qlcnic_83xx_check(adapter))
+       if (qlcnic_83xx_check(adapter)) {
                qlcnic_83xx_free_mbx_intr(adapter);
+               qlcnic_83xx_enable_mbx_poll(adapter);
+       }
 
        qlcnic_teardown_intr(adapter);
        err = qlcnic_setup_intr(adapter, data);
@@ -3253,6 +3323,7 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, size_t len)
                /* register for NIC IDC AEN Events */
                qlcnic_83xx_register_nic_idc_func(adapter, 1);
                err = qlcnic_83xx_setup_mbx_intr(adapter);
+               qlcnic_83xx_disable_mbx_poll(adapter);
                if (err) {
                        dev_err(&adapter->pdev->dev,
                                "failed to setup mbx interrupt\n");
@@ -3318,7 +3389,7 @@ void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event)
 
        rcu_read_lock();
        for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) {
-               dev = __vlan_find_dev_deep(netdev, vid);
+               dev = __vlan_find_dev_deep(netdev, htons(ETH_P_8021Q), vid);
                if (!dev)
                        continue;
                qlcnic_config_indev_addr(adapter, dev, event);
@@ -3432,7 +3503,10 @@ static struct pci_driver qlcnic_driver = {
        .resume = qlcnic_resume,
 #endif
        .shutdown = qlcnic_shutdown,
-       .err_handler = &qlcnic_err_handler
+       .err_handler = &qlcnic_err_handler,
+#ifdef CONFIG_QLCNIC_SRIOV
+       .sriov_configure = qlcnic_pci_sriov_configure,
+#endif
 
 };
 
index abbd22c814a67836a76c43487d62d53b1a6331e8..4b9bab18ebd9b637cbc101e9e7e663cf9d62d709 100644 (file)
@@ -810,11 +810,8 @@ static int __qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter,
 
        tmp_addr = dma_alloc_coherent(&adapter->pdev->dev, temp_size,
                                      &tmp_addr_t, GFP_KERNEL);
-       if (!tmp_addr) {
-               dev_err(&adapter->pdev->dev,
-                       "Can't get memory for FW dump template\n");
+       if (!tmp_addr)
                return -ENOMEM;
-       }
 
        if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_TEMP_HDR)) {
                err = -ENOMEM;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h
new file mode 100644 (file)
index 0000000..d85fbb5
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c) 2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#ifndef _QLCNIC_83XX_SRIOV_H_
+#define _QLCNIC_83XX_SRIOV_H_
+
+#include "qlcnic.h"
+#include <linux/types.h>
+#include <linux/pci.h>
+
+extern const u32 qlcnic_83xx_reg_tbl[];
+extern const u32 qlcnic_83xx_ext_reg_tbl[];
+
+struct qlcnic_bc_payload {
+       u64 payload[126];
+};
+
+struct qlcnic_bc_hdr {
+#if defined(__LITTLE_ENDIAN)
+       u8      version;
+       u8      msg_type:4;
+       u8      rsvd1:3;
+       u8      op_type:1;
+       u8      num_cmds;
+       u8      num_frags;
+       u8      frag_num;
+       u8      cmd_op;
+       u16     seq_id;
+       u64     rsvd3;
+#elif defined(__BIG_ENDIAN)
+       u8      num_frags;
+       u8      num_cmds;
+       u8      op_type:1;
+       u8      rsvd1:3;
+       u8      msg_type:4;
+       u8      version;
+       u16     seq_id;
+       u8      cmd_op;
+       u8      frag_num;
+       u64     rsvd3;
+#endif
+};
+
+enum qlcnic_bc_commands {
+       QLCNIC_BC_CMD_CHANNEL_INIT = 0x0,
+       QLCNIC_BC_CMD_CHANNEL_TERM = 0x1,
+       QLCNIC_BC_CMD_GET_ACL = 0x2,
+       QLCNIC_BC_CMD_CFG_GUEST_VLAN = 0x3,
+};
+
+#define QLC_BC_CMD 1
+
+struct qlcnic_trans_list {
+       /* Lock for manipulating list */
+       spinlock_t              lock;
+       struct list_head        wait_list;
+       int                     count;
+};
+
+enum qlcnic_trans_state {
+       QLC_INIT = 0,
+       QLC_WAIT_FOR_CHANNEL_FREE,
+       QLC_WAIT_FOR_RESP,
+       QLC_ABORT,
+       QLC_END,
+};
+
+struct qlcnic_bc_trans {
+       u8                              func_id;
+       u8                              active;
+       u8                              curr_rsp_frag;
+       u8                              curr_req_frag;
+       u16                             cmd_id;
+       u16                             req_pay_size;
+       u16                             rsp_pay_size;
+       u32                             trans_id;
+       enum qlcnic_trans_state         trans_state;
+       struct list_head                list;
+       struct qlcnic_bc_hdr            *req_hdr;
+       struct qlcnic_bc_hdr            *rsp_hdr;
+       struct qlcnic_bc_payload        *req_pay;
+       struct qlcnic_bc_payload        *rsp_pay;
+       struct completion               resp_cmpl;
+       struct qlcnic_vf_info           *vf;
+};
+
+enum qlcnic_vf_state {
+       QLC_BC_VF_SEND = 0,
+       QLC_BC_VF_RECV,
+       QLC_BC_VF_CHANNEL,
+       QLC_BC_VF_STATE,
+       QLC_BC_VF_FLR,
+       QLC_BC_VF_SOFT_FLR,
+};
+
+enum qlcnic_vlan_mode {
+       QLC_NO_VLAN_MODE = 0,
+       QLC_PVID_MODE,
+       QLC_GUEST_VLAN_MODE,
+};
+
+struct qlcnic_resources {
+       u16 num_tx_mac_filters;
+       u16 num_rx_ucast_mac_filters;
+       u16 num_rx_mcast_mac_filters;
+
+       u16 num_txvlan_keys;
+
+       u16 num_rx_queues;
+       u16 num_tx_queues;
+
+       u16 num_rx_buf_rings;
+       u16 num_rx_status_rings;
+
+       u16 num_destip;
+       u32 num_lro_flows_supported;
+       u16 max_local_ipv6_addrs;
+       u16 max_remote_ipv6_addrs;
+};
+
+struct qlcnic_vport {
+       u16                     handle;
+       u16                     max_tx_bw;
+       u16                     min_tx_bw;
+       u8                      vlan_mode;
+       u16                     vlan;
+       u8                      qos;
+       u8                      mac[6];
+};
+
+struct qlcnic_vf_info {
+       u8                              pci_func;
+       u16                             rx_ctx_id;
+       u16                             tx_ctx_id;
+       unsigned long                   state;
+       struct completion               ch_free_cmpl;
+       struct work_struct              trans_work;
+       struct work_struct              flr_work;
+       /* It synchronizes commands sent from VF */
+       struct mutex                    send_cmd_lock;
+       struct qlcnic_bc_trans          *send_cmd;
+       struct qlcnic_bc_trans          *flr_trans;
+       struct qlcnic_trans_list        rcv_act;
+       struct qlcnic_trans_list        rcv_pend;
+       struct qlcnic_adapter           *adapter;
+       struct qlcnic_vport             *vp;
+};
+
+struct qlcnic_async_work_list {
+       struct list_head        list;
+       struct work_struct      work;
+       void                    *ptr;
+};
+
+struct qlcnic_back_channel {
+       u16                     trans_counter;
+       struct workqueue_struct *bc_trans_wq;
+       struct workqueue_struct *bc_async_wq;
+       struct workqueue_struct *bc_flr_wq;
+       struct list_head        async_list;
+};
+
+struct qlcnic_sriov {
+       u16                             vp_handle;
+       u8                              num_vfs;
+       u8                              any_vlan;
+       u8                              vlan_mode;
+       u16                             num_allowed_vlans;
+       u16                             *allowed_vlans;
+       u16                             vlan;
+       struct qlcnic_resources         ff_max;
+       struct qlcnic_back_channel      bc;
+       struct qlcnic_vf_info           *vf_info;
+};
+
+int qlcnic_sriov_init(struct qlcnic_adapter *, int);
+void qlcnic_sriov_cleanup(struct qlcnic_adapter *);
+void __qlcnic_sriov_cleanup(struct qlcnic_adapter *);
+void qlcnic_sriov_vf_register_map(struct qlcnic_hardware_context *);
+int qlcnic_sriov_vf_init(struct qlcnic_adapter *, int);
+void qlcnic_sriov_vf_set_ops(struct qlcnic_adapter *);
+int qlcnic_sriov_func_to_index(struct qlcnic_adapter *, u8);
+int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8);
+void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32);
+int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8);
+void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *);
+void qlcnic_sriov_cleanup_list(struct qlcnic_trans_list *);
+int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *,
+                               struct qlcnic_bc_trans *);
+int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *,
+                                  struct qlcnic_info *, u16);
+int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *, u16, u8);
+
+static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter)
+{
+       return test_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state) ? true : false;
+}
+
+#ifdef CONFIG_QLCNIC_SRIOV
+void qlcnic_sriov_pf_process_bc_cmd(struct qlcnic_adapter *,
+                                   struct qlcnic_bc_trans *,
+                                   struct qlcnic_cmd_args *);
+void qlcnic_sriov_pf_disable(struct qlcnic_adapter *);
+void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *);
+int qlcnic_pci_sriov_configure(struct pci_dev *, int);
+void qlcnic_pf_set_interface_id_create_rx_ctx(struct qlcnic_adapter *, u32 *);
+void qlcnic_pf_set_interface_id_create_tx_ctx(struct qlcnic_adapter *, u32 *);
+void qlcnic_pf_set_interface_id_del_rx_ctx(struct qlcnic_adapter *, u32 *);
+void qlcnic_pf_set_interface_id_del_tx_ctx(struct qlcnic_adapter *, u32 *);
+void qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *, u32 *);
+void qlcnic_pf_set_interface_id_ipaddr(struct qlcnic_adapter *, u32 *);
+void qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *, u32 *);
+void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *, struct qlcnic_vf_info *);
+bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *,
+                                struct qlcnic_bc_trans *,
+                                struct qlcnic_vf_info *);
+void qlcnic_sriov_pf_reset(struct qlcnic_adapter *);
+int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *);
+int qlcnic_sriov_set_vf_mac(struct net_device *, int, u8 *);
+int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int);
+int qlcnic_sriov_get_vf_config(struct net_device *, int ,
+                              struct ifla_vf_info *);
+int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8);
+#else
+static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {}
+static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {}
+static inline void
+qlcnic_pf_set_interface_id_create_rx_ctx(struct qlcnic_adapter *adapter,
+                                        u32 *int_id) {}
+static inline void
+qlcnic_pf_set_interface_id_create_tx_ctx(struct qlcnic_adapter *adapter,
+                                        u32 *int_id) {}
+static inline void
+qlcnic_pf_set_interface_id_del_rx_ctx(struct qlcnic_adapter *adapter,
+                                     u32 *int_id) {}
+static inline void
+qlcnic_pf_set_interface_id_del_tx_ctx(struct qlcnic_adapter *adapter,
+                                     u32 *int_id) {}
+static inline void
+qlcnic_pf_set_interface_id_ipaddr(struct qlcnic_adapter *adapter, u32 *int_id)
+{}
+static inline void
+qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *adapter, u32 *int_id)
+{}
+static inline void
+qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *adapter, u32 *int_id)
+{}
+static inline void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *sriov,
+                                             struct qlcnic_vf_info *vf) {}
+static inline bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *adapter,
+                                              struct qlcnic_bc_trans *trans,
+                                              struct qlcnic_vf_info *vf)
+{ return false; }
+static inline void qlcnic_sriov_pf_reset(struct qlcnic_adapter *adapter) {}
+static inline int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *adapter)
+{ return 0; }
+#endif
+
+#endif
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
new file mode 100644 (file)
index 0000000..44d547d
--- /dev/null
@@ -0,0 +1,1954 @@
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c) 2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#include "qlcnic_sriov.h"
+#include "qlcnic.h"
+#include "qlcnic_83xx_hw.h"
+#include <linux/types.h>
+
+#define QLC_BC_COMMAND 0
+#define QLC_BC_RESPONSE        1
+
+#define QLC_MBOX_RESP_TIMEOUT          (10 * HZ)
+#define QLC_MBOX_CH_FREE_TIMEOUT       (10 * HZ)
+
+#define QLC_BC_MSG             0
+#define QLC_BC_CFREE           1
+#define QLC_BC_FLR             2
+#define QLC_BC_HDR_SZ          16
+#define QLC_BC_PAYLOAD_SZ      (1024 - QLC_BC_HDR_SZ)
+
+#define QLC_DEFAULT_RCV_DESCRIPTORS_SRIOV_VF           2048
+#define QLC_DEFAULT_JUMBO_RCV_DESCRIPTORS_SRIOV_VF     512
+
+#define QLC_83XX_VF_RESET_FAIL_THRESH  8
+#define QLC_BC_CMD_MAX_RETRY_CNT       5
+
+static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *);
+static int qlcnic_sriov_alloc_bc_mbx_args(struct qlcnic_cmd_args *, u32);
+static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *);
+static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *);
+static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *);
+static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *,
+                                 struct qlcnic_cmd_args *);
+
+static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = {
+       .read_crb                       = qlcnic_83xx_read_crb,
+       .write_crb                      = qlcnic_83xx_write_crb,
+       .read_reg                       = qlcnic_83xx_rd_reg_indirect,
+       .write_reg                      = qlcnic_83xx_wrt_reg_indirect,
+       .get_mac_address                = qlcnic_83xx_get_mac_address,
+       .setup_intr                     = qlcnic_83xx_setup_intr,
+       .alloc_mbx_args                 = qlcnic_83xx_alloc_mbx_args,
+       .mbx_cmd                        = qlcnic_sriov_vf_mbx_op,
+       .get_func_no                    = qlcnic_83xx_get_func_no,
+       .api_lock                       = qlcnic_83xx_cam_lock,
+       .api_unlock                     = qlcnic_83xx_cam_unlock,
+       .process_lb_rcv_ring_diag       = qlcnic_83xx_process_rcv_ring_diag,
+       .create_rx_ctx                  = qlcnic_83xx_create_rx_ctx,
+       .create_tx_ctx                  = qlcnic_83xx_create_tx_ctx,
+       .del_rx_ctx                     = qlcnic_83xx_del_rx_ctx,
+       .del_tx_ctx                     = qlcnic_83xx_del_tx_ctx,
+       .setup_link_event               = qlcnic_83xx_setup_link_event,
+       .get_nic_info                   = qlcnic_83xx_get_nic_info,
+       .get_pci_info                   = qlcnic_83xx_get_pci_info,
+       .set_nic_info                   = qlcnic_83xx_set_nic_info,
+       .change_macvlan                 = qlcnic_83xx_sre_macaddr_change,
+       .napi_enable                    = qlcnic_83xx_napi_enable,
+       .napi_disable                   = qlcnic_83xx_napi_disable,
+       .config_intr_coal               = qlcnic_83xx_config_intr_coal,
+       .config_rss                     = qlcnic_83xx_config_rss,
+       .config_hw_lro                  = qlcnic_83xx_config_hw_lro,
+       .config_promisc_mode            = qlcnic_83xx_nic_set_promisc,
+       .change_l2_filter               = qlcnic_83xx_change_l2_filter,
+       .get_board_info                 = qlcnic_83xx_get_port_info,
+       .free_mac_list                  = qlcnic_sriov_vf_free_mac_list,
+};
+
+static struct qlcnic_nic_template qlcnic_sriov_vf_ops = {
+       .config_bridged_mode    = qlcnic_config_bridged_mode,
+       .config_led             = qlcnic_config_led,
+       .cancel_idc_work        = qlcnic_sriov_vf_cancel_fw_work,
+       .napi_add               = qlcnic_83xx_napi_add,
+       .napi_del               = qlcnic_83xx_napi_del,
+       .config_ipaddr          = qlcnic_83xx_config_ipaddr,
+       .clear_legacy_intr      = qlcnic_83xx_clear_legacy_intr,
+};
+
+static const struct qlcnic_mailbox_metadata qlcnic_sriov_bc_mbx_tbl[] = {
+       {QLCNIC_BC_CMD_CHANNEL_INIT, 2, 2},
+       {QLCNIC_BC_CMD_CHANNEL_TERM, 2, 2},
+       {QLCNIC_BC_CMD_GET_ACL, 3, 14},
+       {QLCNIC_BC_CMD_CFG_GUEST_VLAN, 2, 2},
+};
+
+static inline bool qlcnic_sriov_bc_msg_check(u32 val)
+{
+       return (val & (1 << QLC_BC_MSG)) ? true : false;
+}
+
+static inline bool qlcnic_sriov_channel_free_check(u32 val)
+{
+       return (val & (1 << QLC_BC_CFREE)) ? true : false;
+}
+
+static inline bool qlcnic_sriov_flr_check(u32 val)
+{
+       return (val & (1 << QLC_BC_FLR)) ? true : false;
+}
+
+static inline u8 qlcnic_sriov_target_func_id(u32 val)
+{
+       return (val >> 4) & 0xff;
+}
+
+static int qlcnic_sriov_virtid_fn(struct qlcnic_adapter *adapter, int vf_id)
+{
+       struct pci_dev *dev = adapter->pdev;
+       int pos;
+       u16 stride, offset;
+
+       if (qlcnic_sriov_vf_check(adapter))
+               return 0;
+
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
+       pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
+       pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
+
+       return (dev->devfn + offset + stride * vf_id) & 0xff;
+}
+
+int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
+{
+       struct qlcnic_sriov *sriov;
+       struct qlcnic_back_channel *bc;
+       struct workqueue_struct *wq;
+       struct qlcnic_vport *vp;
+       struct qlcnic_vf_info *vf;
+       int err, i;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return -EIO;
+
+       sriov  = kzalloc(sizeof(struct qlcnic_sriov), GFP_KERNEL);
+       if (!sriov)
+               return -ENOMEM;
+
+       adapter->ahw->sriov = sriov;
+       sriov->num_vfs = num_vfs;
+       bc = &sriov->bc;
+       sriov->vf_info = kzalloc(sizeof(struct qlcnic_vf_info) *
+                                num_vfs, GFP_KERNEL);
+       if (!sriov->vf_info) {
+               err = -ENOMEM;
+               goto qlcnic_free_sriov;
+       }
+
+       wq = create_singlethread_workqueue("bc-trans");
+       if (wq == NULL) {
+               err = -ENOMEM;
+               dev_err(&adapter->pdev->dev,
+                       "Cannot create bc-trans workqueue\n");
+               goto qlcnic_free_vf_info;
+       }
+
+       bc->bc_trans_wq = wq;
+
+       wq = create_singlethread_workqueue("async");
+       if (wq == NULL) {
+               err = -ENOMEM;
+               dev_err(&adapter->pdev->dev, "Cannot create async workqueue\n");
+               goto qlcnic_destroy_trans_wq;
+       }
+
+       bc->bc_async_wq =  wq;
+       INIT_LIST_HEAD(&bc->async_list);
+
+       for (i = 0; i < num_vfs; i++) {
+               vf = &sriov->vf_info[i];
+               vf->adapter = adapter;
+               vf->pci_func = qlcnic_sriov_virtid_fn(adapter, i);
+               mutex_init(&vf->send_cmd_lock);
+               INIT_LIST_HEAD(&vf->rcv_act.wait_list);
+               INIT_LIST_HEAD(&vf->rcv_pend.wait_list);
+               spin_lock_init(&vf->rcv_act.lock);
+               spin_lock_init(&vf->rcv_pend.lock);
+               init_completion(&vf->ch_free_cmpl);
+
+               if (qlcnic_sriov_pf_check(adapter)) {
+                       vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL);
+                       if (!vp) {
+                               err = -ENOMEM;
+                               goto qlcnic_destroy_async_wq;
+                       }
+                       sriov->vf_info[i].vp = vp;
+                       vp->max_tx_bw = MAX_BW;
+                       random_ether_addr(vp->mac);
+                       dev_info(&adapter->pdev->dev,
+                                "MAC Address %pM is configured for VF %d\n",
+                                vp->mac, i);
+               }
+       }
+
+       return 0;
+
+qlcnic_destroy_async_wq:
+       destroy_workqueue(bc->bc_async_wq);
+
+qlcnic_destroy_trans_wq:
+       destroy_workqueue(bc->bc_trans_wq);
+
+qlcnic_free_vf_info:
+       kfree(sriov->vf_info);
+
+qlcnic_free_sriov:
+       kfree(adapter->ahw->sriov);
+       return err;
+}
+
+void qlcnic_sriov_cleanup_list(struct qlcnic_trans_list *t_list)
+{
+       struct qlcnic_bc_trans *trans;
+       struct qlcnic_cmd_args cmd;
+       unsigned long flags;
+
+       spin_lock_irqsave(&t_list->lock, flags);
+
+       while (!list_empty(&t_list->wait_list)) {
+               trans = list_first_entry(&t_list->wait_list,
+                                        struct qlcnic_bc_trans, list);
+               list_del(&trans->list);
+               t_list->count--;
+               cmd.req.arg = (u32 *)trans->req_pay;
+               cmd.rsp.arg = (u32 *)trans->rsp_pay;
+               qlcnic_free_mbx_args(&cmd);
+               qlcnic_sriov_cleanup_transaction(trans);
+       }
+
+       spin_unlock_irqrestore(&t_list->lock, flags);
+}
+
+void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_back_channel *bc = &sriov->bc;
+       struct qlcnic_vf_info *vf;
+       int i;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return;
+
+       qlcnic_sriov_cleanup_async_list(bc);
+       destroy_workqueue(bc->bc_async_wq);
+
+       for (i = 0; i < sriov->num_vfs; i++) {
+               vf = &sriov->vf_info[i];
+               qlcnic_sriov_cleanup_list(&vf->rcv_pend);
+               cancel_work_sync(&vf->trans_work);
+               qlcnic_sriov_cleanup_list(&vf->rcv_act);
+       }
+
+       destroy_workqueue(bc->bc_trans_wq);
+
+       for (i = 0; i < sriov->num_vfs; i++)
+               kfree(sriov->vf_info[i].vp);
+
+       kfree(sriov->vf_info);
+       kfree(adapter->ahw->sriov);
+}
+
+static void qlcnic_sriov_vf_cleanup(struct qlcnic_adapter *adapter)
+{
+       qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM);
+       qlcnic_sriov_cfg_bc_intr(adapter, 0);
+       __qlcnic_sriov_cleanup(adapter);
+}
+
+void qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter)
+{
+       if (qlcnic_sriov_pf_check(adapter))
+               qlcnic_sriov_pf_cleanup(adapter);
+
+       if (qlcnic_sriov_vf_check(adapter))
+               qlcnic_sriov_vf_cleanup(adapter);
+}
+
+static int qlcnic_sriov_post_bc_msg(struct qlcnic_adapter *adapter, u32 *hdr,
+                                   u32 *pay, u8 pci_func, u8 size)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       unsigned long flags;
+       u32 rsp, mbx_val, fw_data, rsp_num, mbx_cmd, val;
+       u16 opcode;
+       u8 mbx_err_code;
+       int i, j;
+
+       opcode = ((struct qlcnic_bc_hdr *)hdr)->cmd_op;
+
+       if (!test_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status)) {
+               dev_info(&adapter->pdev->dev,
+                        "Mailbox cmd attempted, 0x%x\n", opcode);
+               dev_info(&adapter->pdev->dev, "Mailbox detached\n");
+               return 0;
+       }
+
+       spin_lock_irqsave(&ahw->mbx_lock, flags);
+
+       mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL);
+       if (mbx_val) {
+               QLCDB(adapter, DRV, "Mailbox cmd attempted, 0x%x\n", opcode);
+               spin_unlock_irqrestore(&ahw->mbx_lock, flags);
+               return QLCNIC_RCODE_TIMEOUT;
+       }
+       /* Fill in mailbox registers */
+       val = size + (sizeof(struct qlcnic_bc_hdr) / sizeof(u32));
+       mbx_cmd = 0x31 | (val << 16) | (adapter->ahw->fw_hal_version << 29);
+
+       writel(mbx_cmd, QLCNIC_MBX_HOST(ahw, 0));
+       mbx_cmd = 0x1 | (1 << 4);
+
+       if (qlcnic_sriov_pf_check(adapter))
+               mbx_cmd |= (pci_func << 5);
+
+       writel(mbx_cmd, QLCNIC_MBX_HOST(ahw, 1));
+       for (i = 2, j = 0; j < (sizeof(struct qlcnic_bc_hdr) / sizeof(u32));
+                       i++, j++) {
+               writel(*(hdr++), QLCNIC_MBX_HOST(ahw, i));
+       }
+       for (j = 0; j < size; j++, i++)
+               writel(*(pay++), QLCNIC_MBX_HOST(ahw, i));
+
+       /* Signal FW about the impending command */
+       QLCWRX(ahw, QLCNIC_HOST_MBX_CTRL, QLCNIC_SET_OWNER);
+
+       /* Waiting for the mailbox cmd to complete and while waiting here
+        * some AEN might arrive. If more than 5 seconds expire we can
+        * assume something is wrong.
+        */
+poll:
+       rsp = qlcnic_83xx_mbx_poll(adapter);
+       if (rsp != QLCNIC_RCODE_TIMEOUT) {
+               /* Get the FW response data */
+               fw_data = readl(QLCNIC_MBX_FW(ahw, 0));
+               if (fw_data &  QLCNIC_MBX_ASYNC_EVENT) {
+                       __qlcnic_83xx_process_aen(adapter);
+                       mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL);
+                       if (mbx_val)
+                               goto poll;
+               }
+               mbx_err_code = QLCNIC_MBX_STATUS(fw_data);
+               rsp_num = QLCNIC_MBX_NUM_REGS(fw_data);
+               opcode = QLCNIC_MBX_RSP(fw_data);
+
+               switch (mbx_err_code) {
+               case QLCNIC_MBX_RSP_OK:
+               case QLCNIC_MBX_PORT_RSP_OK:
+                       rsp = QLCNIC_RCODE_SUCCESS;
+                       break;
+               default:
+                       if (opcode == QLCNIC_CMD_CONFIG_MAC_VLAN) {
+                               rsp = qlcnic_83xx_mac_rcode(adapter);
+                               if (!rsp)
+                                       goto out;
+                       }
+                       dev_err(&adapter->pdev->dev,
+                               "MBX command 0x%x failed with err:0x%x\n",
+                               opcode, mbx_err_code);
+                       rsp = mbx_err_code;
+                       break;
+               }
+               goto out;
+       }
+
+       dev_err(&adapter->pdev->dev, "MBX command 0x%x timed out\n",
+               QLCNIC_MBX_RSP(mbx_cmd));
+       rsp = QLCNIC_RCODE_TIMEOUT;
+out:
+       /* clear fw mbx control register */
+       QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER);
+       spin_unlock_irqrestore(&adapter->ahw->mbx_lock, flags);
+       return rsp;
+}
+
+static void qlcnic_sriov_vf_cfg_buff_desc(struct qlcnic_adapter *adapter)
+{
+       adapter->num_rxd = QLC_DEFAULT_RCV_DESCRIPTORS_SRIOV_VF;
+       adapter->max_rxd = MAX_RCV_DESCRIPTORS_10G;
+       adapter->num_jumbo_rxd = QLC_DEFAULT_JUMBO_RCV_DESCRIPTORS_SRIOV_VF;
+       adapter->max_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_10G;
+       adapter->num_txd = MAX_CMD_DESCRIPTORS;
+       adapter->max_rds_rings = MAX_RDS_RINGS;
+}
+
+int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *adapter,
+                                  struct qlcnic_info *npar_info, u16 vport_id)
+{
+       struct device *dev = &adapter->pdev->dev;
+       struct qlcnic_cmd_args cmd;
+       int err;
+       u32 status;
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
+       if (err)
+               return err;
+
+       cmd.req.arg[1] = vport_id << 16 | 0x1;
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to get vport info, err=%d\n", err);
+               qlcnic_free_mbx_args(&cmd);
+               return err;
+       }
+
+       status = cmd.rsp.arg[2] & 0xffff;
+       if (status & BIT_0)
+               npar_info->min_tx_bw = MSW(cmd.rsp.arg[2]);
+       if (status & BIT_1)
+               npar_info->max_tx_bw = LSW(cmd.rsp.arg[3]);
+       if (status & BIT_2)
+               npar_info->max_tx_ques = MSW(cmd.rsp.arg[3]);
+       if (status & BIT_3)
+               npar_info->max_tx_mac_filters = LSW(cmd.rsp.arg[4]);
+       if (status & BIT_4)
+               npar_info->max_rx_mcast_mac_filters = MSW(cmd.rsp.arg[4]);
+       if (status & BIT_5)
+               npar_info->max_rx_ucast_mac_filters = LSW(cmd.rsp.arg[5]);
+       if (status & BIT_6)
+               npar_info->max_rx_ip_addr = MSW(cmd.rsp.arg[5]);
+       if (status & BIT_7)
+               npar_info->max_rx_lro_flow = LSW(cmd.rsp.arg[6]);
+       if (status & BIT_8)
+               npar_info->max_rx_status_rings = MSW(cmd.rsp.arg[6]);
+       if (status & BIT_9)
+               npar_info->max_rx_buf_rings = LSW(cmd.rsp.arg[7]);
+
+       npar_info->max_rx_ques = MSW(cmd.rsp.arg[7]);
+       npar_info->max_tx_vlan_keys = LSW(cmd.rsp.arg[8]);
+       npar_info->max_local_ipv6_addrs = MSW(cmd.rsp.arg[8]);
+       npar_info->max_remote_ipv6_addrs = LSW(cmd.rsp.arg[9]);
+
+       dev_info(dev, "\n\tmin_tx_bw: %d, max_tx_bw: %d max_tx_ques: %d,\n"
+                "\tmax_tx_mac_filters: %d max_rx_mcast_mac_filters: %d,\n"
+                "\tmax_rx_ucast_mac_filters: 0x%x, max_rx_ip_addr: %d,\n"
+                "\tmax_rx_lro_flow: %d max_rx_status_rings: %d,\n"
+                "\tmax_rx_buf_rings: %d, max_rx_ques: %d, max_tx_vlan_keys %d\n"
+                "\tlocal_ipv6_addr: %d, remote_ipv6_addr: %d\n",
+                npar_info->min_tx_bw, npar_info->max_tx_bw,
+                npar_info->max_tx_ques, npar_info->max_tx_mac_filters,
+                npar_info->max_rx_mcast_mac_filters,
+                npar_info->max_rx_ucast_mac_filters, npar_info->max_rx_ip_addr,
+                npar_info->max_rx_lro_flow, npar_info->max_rx_status_rings,
+                npar_info->max_rx_buf_rings, npar_info->max_rx_ques,
+                npar_info->max_tx_vlan_keys, npar_info->max_local_ipv6_addrs,
+                npar_info->max_remote_ipv6_addrs);
+
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static int qlcnic_sriov_set_pvid_mode(struct qlcnic_adapter *adapter,
+                                     struct qlcnic_cmd_args *cmd)
+{
+       adapter->rx_pvid = (cmd->rsp.arg[1] >> 16) & 0xffff;
+       adapter->flags &= ~QLCNIC_TAGGING_ENABLED;
+       return 0;
+}
+
+static int qlcnic_sriov_set_guest_vlan_mode(struct qlcnic_adapter *adapter,
+                                           struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       int i, num_vlans;
+       u16 *vlans;
+
+       if (sriov->allowed_vlans)
+               return 0;
+
+       sriov->any_vlan = cmd->rsp.arg[2] & 0xf;
+       if (!sriov->any_vlan)
+               return 0;
+
+       sriov->num_allowed_vlans = cmd->rsp.arg[2] >> 16;
+       num_vlans = sriov->num_allowed_vlans;
+       sriov->allowed_vlans = kzalloc(sizeof(u16) * num_vlans, GFP_KERNEL);
+       if (!sriov->allowed_vlans)
+               return -ENOMEM;
+
+       vlans = (u16 *)&cmd->rsp.arg[3];
+       for (i = 0; i < num_vlans; i++)
+               sriov->allowed_vlans[i] = vlans[i];
+
+       return 0;
+}
+
+static int qlcnic_sriov_get_vf_acl(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_cmd_args cmd;
+       int ret;
+
+       ret = qlcnic_sriov_alloc_bc_mbx_args(&cmd, QLCNIC_BC_CMD_GET_ACL);
+       if (ret)
+               return ret;
+
+       ret = qlcnic_issue_cmd(adapter, &cmd);
+       if (ret) {
+               dev_err(&adapter->pdev->dev, "Failed to get ACL, err=%d\n",
+                       ret);
+       } else {
+               sriov->vlan_mode = cmd.rsp.arg[1] & 0x3;
+               switch (sriov->vlan_mode) {
+               case QLC_GUEST_VLAN_MODE:
+                       ret = qlcnic_sriov_set_guest_vlan_mode(adapter, &cmd);
+                       break;
+               case QLC_PVID_MODE:
+                       ret = qlcnic_sriov_set_pvid_mode(adapter, &cmd);
+                       break;
+               }
+       }
+
+       qlcnic_free_mbx_args(&cmd);
+       return ret;
+}
+
+static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_info nic_info;
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       int err;
+
+       err = qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, 0);
+       if (err)
+               return err;
+
+       err = qlcnic_get_nic_info(adapter, &nic_info, ahw->pci_func);
+       if (err)
+               return -EIO;
+
+       err = qlcnic_sriov_get_vf_acl(adapter);
+       if (err)
+               return err;
+
+       if (qlcnic_83xx_get_port_info(adapter))
+               return -EIO;
+
+       qlcnic_sriov_vf_cfg_buff_desc(adapter);
+       adapter->flags |= QLCNIC_ADAPTER_INITIALIZED;
+       dev_info(&adapter->pdev->dev, "HAL Version: %d\n",
+                adapter->ahw->fw_hal_version);
+
+       ahw->physical_port = (u8) nic_info.phys_port;
+       ahw->switch_mode = nic_info.switch_mode;
+       ahw->max_mtu = nic_info.max_mtu;
+       ahw->op_mode = nic_info.op_mode;
+       ahw->capabilities = nic_info.capabilities;
+       return 0;
+}
+
+static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
+                                int pci_using_dac)
+{
+       int err;
+
+       INIT_LIST_HEAD(&adapter->vf_mc_list);
+       if (!qlcnic_use_msi_x && !!qlcnic_use_msi)
+               dev_warn(&adapter->pdev->dev,
+                        "83xx adapter do not support MSI interrupts\n");
+
+       err = qlcnic_setup_intr(adapter, 1);
+       if (err) {
+               dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n");
+               goto err_out_disable_msi;
+       }
+
+       err = qlcnic_83xx_setup_mbx_intr(adapter);
+       if (err)
+               goto err_out_disable_msi;
+
+       err = qlcnic_sriov_init(adapter, 1);
+       if (err)
+               goto err_out_disable_mbx_intr;
+
+       err = qlcnic_sriov_cfg_bc_intr(adapter, 1);
+       if (err)
+               goto err_out_cleanup_sriov;
+
+       err = qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_INIT);
+       if (err)
+               goto err_out_disable_bc_intr;
+
+       err = qlcnic_sriov_vf_init_driver(adapter);
+       if (err)
+               goto err_out_send_channel_term;
+
+       err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac);
+       if (err)
+               goto err_out_send_channel_term;
+
+       pci_set_drvdata(adapter->pdev, adapter);
+       dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n",
+                adapter->netdev->name);
+       qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
+                            adapter->ahw->idc.delay);
+       return 0;
+
+err_out_send_channel_term:
+       qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM);
+
+err_out_disable_bc_intr:
+       qlcnic_sriov_cfg_bc_intr(adapter, 0);
+
+err_out_cleanup_sriov:
+       __qlcnic_sriov_cleanup(adapter);
+
+err_out_disable_mbx_intr:
+       qlcnic_83xx_free_mbx_intr(adapter);
+
+err_out_disable_msi:
+       qlcnic_teardown_intr(adapter);
+       return err;
+}
+
+static int qlcnic_sriov_check_dev_ready(struct qlcnic_adapter *adapter)
+{
+       u32 state;
+
+       do {
+               msleep(20);
+               if (++adapter->fw_fail_cnt > QLC_BC_CMD_MAX_RETRY_CNT)
+                       return -EIO;
+               state = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_STATE);
+       } while (state != QLC_83XX_IDC_DEV_READY);
+
+       return 0;
+}
+
+int qlcnic_sriov_vf_init(struct qlcnic_adapter *adapter, int pci_using_dac)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       int err;
+
+       spin_lock_init(&ahw->mbx_lock);
+       set_bit(QLC_83XX_MBX_READY, &ahw->idc.status);
+       set_bit(QLC_83XX_MODULE_LOADED, &ahw->idc.status);
+       ahw->idc.delay = QLC_83XX_IDC_FW_POLL_DELAY;
+       ahw->reset_context = 0;
+       adapter->fw_fail_cnt = 0;
+       ahw->msix_supported = 1;
+       adapter->need_fw_reset = 0;
+       adapter->flags |= QLCNIC_TX_INTR_SHARED;
+
+       err = qlcnic_sriov_check_dev_ready(adapter);
+       if (err)
+               return err;
+
+       err = qlcnic_sriov_setup_vf(adapter, pci_using_dac);
+       if (err)
+               return err;
+
+       if (qlcnic_read_mac_addr(adapter))
+               dev_warn(&adapter->pdev->dev, "failed to read mac addr\n");
+
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       return 0;
+}
+
+void qlcnic_sriov_vf_set_ops(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+
+       ahw->op_mode = QLCNIC_SRIOV_VF_FUNC;
+       dev_info(&adapter->pdev->dev,
+                "HAL Version: %d Non Privileged SRIOV function\n",
+                ahw->fw_hal_version);
+       adapter->nic_ops = &qlcnic_sriov_vf_ops;
+       set_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
+       return;
+}
+
+void qlcnic_sriov_vf_register_map(struct qlcnic_hardware_context *ahw)
+{
+       ahw->hw_ops             = &qlcnic_sriov_vf_hw_ops;
+       ahw->reg_tbl            = (u32 *)qlcnic_83xx_reg_tbl;
+       ahw->ext_reg_tbl        = (u32 *)qlcnic_83xx_ext_reg_tbl;
+}
+
+static u32 qlcnic_sriov_get_bc_paysize(u32 real_pay_size, u8 curr_frag)
+{
+       u32 pay_size;
+
+       pay_size = real_pay_size / ((curr_frag + 1) * QLC_BC_PAYLOAD_SZ);
+
+       if (pay_size)
+               pay_size = QLC_BC_PAYLOAD_SZ;
+       else
+               pay_size = real_pay_size % QLC_BC_PAYLOAD_SZ;
+
+       return pay_size;
+}
+
+int qlcnic_sriov_func_to_index(struct qlcnic_adapter *adapter, u8 pci_func)
+{
+       struct qlcnic_vf_info *vf_info = adapter->ahw->sriov->vf_info;
+       u8 i;
+
+       if (qlcnic_sriov_vf_check(adapter))
+               return 0;
+
+       for (i = 0; i < adapter->ahw->sriov->num_vfs; i++) {
+               if (vf_info[i].pci_func == pci_func)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+
+static inline int qlcnic_sriov_alloc_bc_trans(struct qlcnic_bc_trans **trans)
+{
+       *trans = kzalloc(sizeof(struct qlcnic_bc_trans), GFP_ATOMIC);
+       if (!*trans)
+               return -ENOMEM;
+
+       init_completion(&(*trans)->resp_cmpl);
+       return 0;
+}
+
+static inline int qlcnic_sriov_alloc_bc_msg(struct qlcnic_bc_hdr **hdr,
+                                           u32 size)
+{
+       *hdr = kzalloc(sizeof(struct qlcnic_bc_hdr) * size, GFP_ATOMIC);
+       if (!*hdr)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int qlcnic_sriov_alloc_bc_mbx_args(struct qlcnic_cmd_args *mbx, u32 type)
+{
+       const struct qlcnic_mailbox_metadata *mbx_tbl;
+       int i, size;
+
+       mbx_tbl = qlcnic_sriov_bc_mbx_tbl;
+       size = ARRAY_SIZE(qlcnic_sriov_bc_mbx_tbl);
+
+       for (i = 0; i < size; i++) {
+               if (type == mbx_tbl[i].cmd) {
+                       mbx->op_type = QLC_BC_CMD;
+                       mbx->req.num = mbx_tbl[i].in_args;
+                       mbx->rsp.num = mbx_tbl[i].out_args;
+                       mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32),
+                                              GFP_ATOMIC);
+                       if (!mbx->req.arg)
+                               return -ENOMEM;
+                       mbx->rsp.arg = kcalloc(mbx->rsp.num, sizeof(u32),
+                                              GFP_ATOMIC);
+                       if (!mbx->rsp.arg) {
+                               kfree(mbx->req.arg);
+                               mbx->req.arg = NULL;
+                               return -ENOMEM;
+                       }
+                       memset(mbx->req.arg, 0, sizeof(u32) * mbx->req.num);
+                       memset(mbx->rsp.arg, 0, sizeof(u32) * mbx->rsp.num);
+                       mbx->req.arg[0] = (type | (mbx->req.num << 16) |
+                                          (3 << 29));
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+static int qlcnic_sriov_prepare_bc_hdr(struct qlcnic_bc_trans *trans,
+                                      struct qlcnic_cmd_args *cmd,
+                                      u16 seq, u8 msg_type)
+{
+       struct qlcnic_bc_hdr *hdr;
+       int i;
+       u32 num_regs, bc_pay_sz;
+       u16 remainder;
+       u8 cmd_op, num_frags, t_num_frags;
+
+       bc_pay_sz = QLC_BC_PAYLOAD_SZ;
+       if (msg_type == QLC_BC_COMMAND) {
+               trans->req_pay = (struct qlcnic_bc_payload *)cmd->req.arg;
+               trans->rsp_pay = (struct qlcnic_bc_payload *)cmd->rsp.arg;
+               num_regs = cmd->req.num;
+               trans->req_pay_size = (num_regs * 4);
+               num_regs = cmd->rsp.num;
+               trans->rsp_pay_size = (num_regs * 4);
+               cmd_op = cmd->req.arg[0] & 0xff;
+               remainder = (trans->req_pay_size) % (bc_pay_sz);
+               num_frags = (trans->req_pay_size) / (bc_pay_sz);
+               if (remainder)
+                       num_frags++;
+               t_num_frags = num_frags;
+               if (qlcnic_sriov_alloc_bc_msg(&trans->req_hdr, num_frags))
+                       return -ENOMEM;
+               remainder = (trans->rsp_pay_size) % (bc_pay_sz);
+               num_frags = (trans->rsp_pay_size) / (bc_pay_sz);
+               if (remainder)
+                       num_frags++;
+               if (qlcnic_sriov_alloc_bc_msg(&trans->rsp_hdr, num_frags))
+                       return -ENOMEM;
+               num_frags  = t_num_frags;
+               hdr = trans->req_hdr;
+       }  else {
+               cmd->req.arg = (u32 *)trans->req_pay;
+               cmd->rsp.arg = (u32 *)trans->rsp_pay;
+               cmd_op = cmd->req.arg[0] & 0xff;
+               remainder = (trans->rsp_pay_size) % (bc_pay_sz);
+               num_frags = (trans->rsp_pay_size) / (bc_pay_sz);
+               if (remainder)
+                       num_frags++;
+               cmd->req.num = trans->req_pay_size / 4;
+               cmd->rsp.num = trans->rsp_pay_size / 4;
+               hdr = trans->rsp_hdr;
+       }
+
+       trans->trans_id = seq;
+       trans->cmd_id = cmd_op;
+       for (i = 0; i < num_frags; i++) {
+               hdr[i].version = 2;
+               hdr[i].msg_type = msg_type;
+               hdr[i].op_type = cmd->op_type;
+               hdr[i].num_cmds = 1;
+               hdr[i].num_frags = num_frags;
+               hdr[i].frag_num = i + 1;
+               hdr[i].cmd_op = cmd_op;
+               hdr[i].seq_id = seq;
+       }
+       return 0;
+}
+
+static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *trans)
+{
+       if (!trans)
+               return;
+       kfree(trans->req_hdr);
+       kfree(trans->rsp_hdr);
+       kfree(trans);
+}
+
+static int qlcnic_sriov_clear_trans(struct qlcnic_vf_info *vf,
+                                   struct qlcnic_bc_trans *trans, u8 type)
+{
+       struct qlcnic_trans_list *t_list;
+       unsigned long flags;
+       int ret = 0;
+
+       if (type == QLC_BC_RESPONSE) {
+               t_list = &vf->rcv_act;
+               spin_lock_irqsave(&t_list->lock, flags);
+               t_list->count--;
+               list_del(&trans->list);
+               if (t_list->count > 0)
+                       ret = 1;
+               spin_unlock_irqrestore(&t_list->lock, flags);
+       }
+       if (type == QLC_BC_COMMAND) {
+               while (test_and_set_bit(QLC_BC_VF_SEND, &vf->state))
+                       msleep(100);
+               vf->send_cmd = NULL;
+               clear_bit(QLC_BC_VF_SEND, &vf->state);
+       }
+       return ret;
+}
+
+static void qlcnic_sriov_schedule_bc_cmd(struct qlcnic_sriov *sriov,
+                                        struct qlcnic_vf_info *vf,
+                                        work_func_t func)
+{
+       if (test_bit(QLC_BC_VF_FLR, &vf->state) ||
+           vf->adapter->need_fw_reset)
+               return;
+
+       INIT_WORK(&vf->trans_work, func);
+       queue_work(sriov->bc.bc_trans_wq, &vf->trans_work);
+}
+
+static inline void qlcnic_sriov_wait_for_resp(struct qlcnic_bc_trans *trans)
+{
+       struct completion *cmpl = &trans->resp_cmpl;
+
+       if (wait_for_completion_timeout(cmpl, QLC_MBOX_RESP_TIMEOUT))
+               trans->trans_state = QLC_END;
+       else
+               trans->trans_state = QLC_ABORT;
+
+       return;
+}
+
+static void qlcnic_sriov_handle_multi_frags(struct qlcnic_bc_trans *trans,
+                                           u8 type)
+{
+       if (type == QLC_BC_RESPONSE) {
+               trans->curr_rsp_frag++;
+               if (trans->curr_rsp_frag < trans->rsp_hdr->num_frags)
+                       trans->trans_state = QLC_INIT;
+               else
+                       trans->trans_state = QLC_END;
+       } else {
+               trans->curr_req_frag++;
+               if (trans->curr_req_frag < trans->req_hdr->num_frags)
+                       trans->trans_state = QLC_INIT;
+               else
+                       trans->trans_state = QLC_WAIT_FOR_RESP;
+       }
+}
+
+static void qlcnic_sriov_wait_for_channel_free(struct qlcnic_bc_trans *trans,
+                                              u8 type)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct completion *cmpl = &vf->ch_free_cmpl;
+
+       if (!wait_for_completion_timeout(cmpl, QLC_MBOX_CH_FREE_TIMEOUT)) {
+               trans->trans_state = QLC_ABORT;
+               return;
+       }
+
+       clear_bit(QLC_BC_VF_CHANNEL, &vf->state);
+       qlcnic_sriov_handle_multi_frags(trans, type);
+}
+
+static void qlcnic_sriov_pull_bc_msg(struct qlcnic_adapter *adapter,
+                                    u32 *hdr, u32 *pay, u32 size)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u32 fw_mbx;
+       u8 i, max = 2, hdr_size, j;
+
+       hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32));
+       max = (size / sizeof(u32)) + hdr_size;
+
+       fw_mbx = readl(QLCNIC_MBX_FW(ahw, 0));
+       for (i = 2, j = 0; j < hdr_size; i++, j++)
+               *(hdr++) = readl(QLCNIC_MBX_FW(ahw, i));
+       for (; j < max; i++, j++)
+               *(pay++) = readl(QLCNIC_MBX_FW(ahw, i));
+}
+
+static int __qlcnic_sriov_issue_bc_post(struct qlcnic_vf_info *vf)
+{
+       int ret = -EBUSY;
+       u32 timeout = 10000;
+
+       do {
+               if (!test_and_set_bit(QLC_BC_VF_CHANNEL, &vf->state)) {
+                       ret = 0;
+                       break;
+               }
+               mdelay(1);
+       } while (--timeout);
+
+       return ret;
+}
+
+static int qlcnic_sriov_issue_bc_post(struct qlcnic_bc_trans *trans, u8 type)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       u32 pay_size, hdr_size;
+       u32 *hdr, *pay;
+       int ret;
+       u8 pci_func = trans->func_id;
+
+       if (__qlcnic_sriov_issue_bc_post(vf))
+               return -EBUSY;
+
+       if (type == QLC_BC_COMMAND) {
+               hdr = (u32 *)(trans->req_hdr + trans->curr_req_frag);
+               pay = (u32 *)(trans->req_pay + trans->curr_req_frag);
+               hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32));
+               pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size,
+                                                      trans->curr_req_frag);
+               pay_size = (pay_size / sizeof(u32));
+       } else {
+               hdr = (u32 *)(trans->rsp_hdr + trans->curr_rsp_frag);
+               pay = (u32 *)(trans->rsp_pay + trans->curr_rsp_frag);
+               hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32));
+               pay_size = qlcnic_sriov_get_bc_paysize(trans->rsp_pay_size,
+                                                      trans->curr_rsp_frag);
+               pay_size = (pay_size / sizeof(u32));
+       }
+
+       ret = qlcnic_sriov_post_bc_msg(vf->adapter, hdr, pay,
+                                      pci_func, pay_size);
+       return ret;
+}
+
+static int __qlcnic_sriov_send_bc_msg(struct qlcnic_bc_trans *trans,
+                                     struct qlcnic_vf_info *vf, u8 type)
+{
+       bool flag = true;
+       int err = -EIO;
+
+       while (flag) {
+               if (test_bit(QLC_BC_VF_FLR, &vf->state) ||
+                   vf->adapter->need_fw_reset)
+                       trans->trans_state = QLC_ABORT;
+
+               switch (trans->trans_state) {
+               case QLC_INIT:
+                       trans->trans_state = QLC_WAIT_FOR_CHANNEL_FREE;
+                       if (qlcnic_sriov_issue_bc_post(trans, type))
+                               trans->trans_state = QLC_ABORT;
+                       break;
+               case QLC_WAIT_FOR_CHANNEL_FREE:
+                       qlcnic_sriov_wait_for_channel_free(trans, type);
+                       break;
+               case QLC_WAIT_FOR_RESP:
+                       qlcnic_sriov_wait_for_resp(trans);
+                       break;
+               case QLC_END:
+                       err = 0;
+                       flag = false;
+                       break;
+               case QLC_ABORT:
+                       err = -EIO;
+                       flag = false;
+                       clear_bit(QLC_BC_VF_CHANNEL, &vf->state);
+                       break;
+               default:
+                       err = -EIO;
+                       flag = false;
+               }
+       }
+       return err;
+}
+
+static int qlcnic_sriov_send_bc_cmd(struct qlcnic_adapter *adapter,
+                                   struct qlcnic_bc_trans *trans, int pci_func)
+{
+       struct qlcnic_vf_info *vf;
+       int err, index = qlcnic_sriov_func_to_index(adapter, pci_func);
+
+       if (index < 0)
+               return -EIO;
+
+       vf = &adapter->ahw->sriov->vf_info[index];
+       trans->vf = vf;
+       trans->func_id = pci_func;
+
+       if (!test_bit(QLC_BC_VF_STATE, &vf->state)) {
+               if (qlcnic_sriov_pf_check(adapter))
+                       return -EIO;
+               if (qlcnic_sriov_vf_check(adapter) &&
+                   trans->cmd_id != QLCNIC_BC_CMD_CHANNEL_INIT)
+                       return -EIO;
+       }
+
+       mutex_lock(&vf->send_cmd_lock);
+       vf->send_cmd = trans;
+       err = __qlcnic_sriov_send_bc_msg(trans, vf, QLC_BC_COMMAND);
+       qlcnic_sriov_clear_trans(vf, trans, QLC_BC_COMMAND);
+       mutex_unlock(&vf->send_cmd_lock);
+       return err;
+}
+
+static void __qlcnic_sriov_process_bc_cmd(struct qlcnic_adapter *adapter,
+                                         struct qlcnic_bc_trans *trans,
+                                         struct qlcnic_cmd_args *cmd)
+{
+#ifdef CONFIG_QLCNIC_SRIOV
+       if (qlcnic_sriov_pf_check(adapter)) {
+               qlcnic_sriov_pf_process_bc_cmd(adapter, trans, cmd);
+               return;
+       }
+#endif
+       cmd->rsp.arg[0] |= (0x9 << 25);
+       return;
+}
+
+static void qlcnic_sriov_process_bc_cmd(struct work_struct *work)
+{
+       struct qlcnic_vf_info *vf = container_of(work, struct qlcnic_vf_info,
+                                                trans_work);
+       struct qlcnic_bc_trans *trans = NULL;
+       struct qlcnic_adapter *adapter  = vf->adapter;
+       struct qlcnic_cmd_args cmd;
+       u8 req;
+
+       if (adapter->need_fw_reset)
+               return;
+
+       if (test_bit(QLC_BC_VF_FLR, &vf->state))
+               return;
+
+       trans = list_first_entry(&vf->rcv_act.wait_list,
+                                struct qlcnic_bc_trans, list);
+       adapter = vf->adapter;
+
+       if (qlcnic_sriov_prepare_bc_hdr(trans, &cmd, trans->req_hdr->seq_id,
+                                       QLC_BC_RESPONSE))
+               goto cleanup_trans;
+
+       __qlcnic_sriov_process_bc_cmd(adapter, trans, &cmd);
+       trans->trans_state = QLC_INIT;
+       __qlcnic_sriov_send_bc_msg(trans, vf, QLC_BC_RESPONSE);
+
+cleanup_trans:
+       qlcnic_free_mbx_args(&cmd);
+       req = qlcnic_sriov_clear_trans(vf, trans, QLC_BC_RESPONSE);
+       qlcnic_sriov_cleanup_transaction(trans);
+       if (req)
+               qlcnic_sriov_schedule_bc_cmd(adapter->ahw->sriov, vf,
+                                            qlcnic_sriov_process_bc_cmd);
+}
+
+static void qlcnic_sriov_handle_bc_resp(struct qlcnic_bc_hdr *hdr,
+                                       struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_bc_trans *trans;
+       u32 pay_size;
+
+       if (test_and_set_bit(QLC_BC_VF_SEND, &vf->state))
+               return;
+
+       trans = vf->send_cmd;
+
+       if (trans == NULL)
+               goto clear_send;
+
+       if (trans->trans_id != hdr->seq_id)
+               goto clear_send;
+
+       pay_size = qlcnic_sriov_get_bc_paysize(trans->rsp_pay_size,
+                                              trans->curr_rsp_frag);
+       qlcnic_sriov_pull_bc_msg(vf->adapter,
+                                (u32 *)(trans->rsp_hdr + trans->curr_rsp_frag),
+                                (u32 *)(trans->rsp_pay + trans->curr_rsp_frag),
+                                pay_size);
+       if (++trans->curr_rsp_frag < trans->rsp_hdr->num_frags)
+               goto clear_send;
+
+       complete(&trans->resp_cmpl);
+
+clear_send:
+       clear_bit(QLC_BC_VF_SEND, &vf->state);
+}
+
+int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *sriov,
+                               struct qlcnic_vf_info *vf,
+                               struct qlcnic_bc_trans *trans)
+{
+       struct qlcnic_trans_list *t_list = &vf->rcv_act;
+
+       t_list->count++;
+       list_add_tail(&trans->list, &t_list->wait_list);
+       if (t_list->count == 1)
+               qlcnic_sriov_schedule_bc_cmd(sriov, vf,
+                                            qlcnic_sriov_process_bc_cmd);
+       return 0;
+}
+
+static int qlcnic_sriov_add_act_list(struct qlcnic_sriov *sriov,
+                                    struct qlcnic_vf_info *vf,
+                                    struct qlcnic_bc_trans *trans)
+{
+       struct qlcnic_trans_list *t_list = &vf->rcv_act;
+
+       spin_lock(&t_list->lock);
+
+       __qlcnic_sriov_add_act_list(sriov, vf, trans);
+
+       spin_unlock(&t_list->lock);
+       return 0;
+}
+
+static void qlcnic_sriov_handle_pending_trans(struct qlcnic_sriov *sriov,
+                                             struct qlcnic_vf_info *vf,
+                                             struct qlcnic_bc_hdr *hdr)
+{
+       struct qlcnic_bc_trans *trans = NULL;
+       struct list_head *node;
+       u32 pay_size, curr_frag;
+       u8 found = 0, active = 0;
+
+       spin_lock(&vf->rcv_pend.lock);
+       if (vf->rcv_pend.count > 0) {
+               list_for_each(node, &vf->rcv_pend.wait_list) {
+                       trans = list_entry(node, struct qlcnic_bc_trans, list);
+                       if (trans->trans_id == hdr->seq_id) {
+                               found = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (found) {
+               curr_frag = trans->curr_req_frag;
+               pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size,
+                                                      curr_frag);
+               qlcnic_sriov_pull_bc_msg(vf->adapter,
+                                        (u32 *)(trans->req_hdr + curr_frag),
+                                        (u32 *)(trans->req_pay + curr_frag),
+                                        pay_size);
+               trans->curr_req_frag++;
+               if (trans->curr_req_frag >= hdr->num_frags) {
+                       vf->rcv_pend.count--;
+                       list_del(&trans->list);
+                       active = 1;
+               }
+       }
+       spin_unlock(&vf->rcv_pend.lock);
+
+       if (active)
+               if (qlcnic_sriov_add_act_list(sriov, vf, trans))
+                       qlcnic_sriov_cleanup_transaction(trans);
+
+       return;
+}
+
+static void qlcnic_sriov_handle_bc_cmd(struct qlcnic_sriov *sriov,
+                                      struct qlcnic_bc_hdr *hdr,
+                                      struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_bc_trans *trans;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       struct qlcnic_cmd_args cmd;
+       u32 pay_size;
+       int err;
+       u8 cmd_op;
+
+       if (adapter->need_fw_reset)
+               return;
+
+       if (!test_bit(QLC_BC_VF_STATE, &vf->state) &&
+           hdr->op_type != QLC_BC_CMD &&
+           hdr->cmd_op != QLCNIC_BC_CMD_CHANNEL_INIT)
+               return;
+
+       if (hdr->frag_num > 1) {
+               qlcnic_sriov_handle_pending_trans(sriov, vf, hdr);
+               return;
+       }
+
+       cmd_op = hdr->cmd_op;
+       if (qlcnic_sriov_alloc_bc_trans(&trans))
+               return;
+
+       if (hdr->op_type == QLC_BC_CMD)
+               err = qlcnic_sriov_alloc_bc_mbx_args(&cmd, cmd_op);
+       else
+               err = qlcnic_alloc_mbx_args(&cmd, adapter, cmd_op);
+
+       if (err) {
+               qlcnic_sriov_cleanup_transaction(trans);
+               return;
+       }
+
+       cmd.op_type = hdr->op_type;
+       if (qlcnic_sriov_prepare_bc_hdr(trans, &cmd, hdr->seq_id,
+                                       QLC_BC_COMMAND)) {
+               qlcnic_free_mbx_args(&cmd);
+               qlcnic_sriov_cleanup_transaction(trans);
+               return;
+       }
+
+       pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size,
+                                        trans->curr_req_frag);
+       qlcnic_sriov_pull_bc_msg(vf->adapter,
+                                (u32 *)(trans->req_hdr + trans->curr_req_frag),
+                                (u32 *)(trans->req_pay + trans->curr_req_frag),
+                                pay_size);
+       trans->func_id = vf->pci_func;
+       trans->vf = vf;
+       trans->trans_id = hdr->seq_id;
+       trans->curr_req_frag++;
+
+       if (qlcnic_sriov_soft_flr_check(adapter, trans, vf))
+               return;
+
+       if (trans->curr_req_frag == trans->req_hdr->num_frags) {
+               if (qlcnic_sriov_add_act_list(sriov, vf, trans)) {
+                       qlcnic_free_mbx_args(&cmd);
+                       qlcnic_sriov_cleanup_transaction(trans);
+               }
+       } else {
+               spin_lock(&vf->rcv_pend.lock);
+               list_add_tail(&trans->list, &vf->rcv_pend.wait_list);
+               vf->rcv_pend.count++;
+               spin_unlock(&vf->rcv_pend.lock);
+       }
+}
+
+static void qlcnic_sriov_handle_msg_event(struct qlcnic_sriov *sriov,
+                                         struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_bc_hdr hdr;
+       u32 *ptr = (u32 *)&hdr;
+       u8 msg_type, i;
+
+       for (i = 2; i < 6; i++)
+               ptr[i - 2] = readl(QLCNIC_MBX_FW(vf->adapter->ahw, i));
+       msg_type = hdr.msg_type;
+
+       switch (msg_type) {
+       case QLC_BC_COMMAND:
+               qlcnic_sriov_handle_bc_cmd(sriov, &hdr, vf);
+               break;
+       case QLC_BC_RESPONSE:
+               qlcnic_sriov_handle_bc_resp(&hdr, vf);
+               break;
+       }
+}
+
+static void qlcnic_sriov_handle_flr_event(struct qlcnic_sriov *sriov,
+                                         struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_adapter *adapter = vf->adapter;
+
+       if (qlcnic_sriov_pf_check(adapter))
+               qlcnic_sriov_pf_handle_flr(sriov, vf);
+       else
+               dev_err(&adapter->pdev->dev,
+                       "Invalid event to VF. VF should not get FLR event\n");
+}
+
+void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *adapter, u32 event)
+{
+       struct qlcnic_vf_info *vf;
+       struct qlcnic_sriov *sriov;
+       int index;
+       u8 pci_func;
+
+       sriov = adapter->ahw->sriov;
+       pci_func = qlcnic_sriov_target_func_id(event);
+       index = qlcnic_sriov_func_to_index(adapter, pci_func);
+
+       if (index < 0)
+               return;
+
+       vf = &sriov->vf_info[index];
+       vf->pci_func = pci_func;
+
+       if (qlcnic_sriov_channel_free_check(event))
+               complete(&vf->ch_free_cmpl);
+
+       if (qlcnic_sriov_flr_check(event)) {
+               qlcnic_sriov_handle_flr_event(sriov, vf);
+               return;
+       }
+
+       if (qlcnic_sriov_bc_msg_check(event))
+               qlcnic_sriov_handle_msg_event(sriov, vf);
+}
+
+int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *adapter, u8 enable)
+{
+       struct qlcnic_cmd_args cmd;
+       int err;
+
+       if (!test_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state))
+               return 0;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_BC_EVENT_SETUP))
+               return -ENOMEM;
+
+       if (enable)
+               cmd.req.arg[1] = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7);
+
+       err = qlcnic_83xx_mbx_op(adapter, &cmd);
+
+       if (err != QLCNIC_RCODE_SUCCESS) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to %s bc events, err=%d\n",
+                       (enable ? "enable" : "disable"), err);
+       }
+
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static int qlcnic_sriov_retry_bc_cmd(struct qlcnic_adapter *adapter,
+                                    struct qlcnic_bc_trans *trans)
+{
+       u8 max = QLC_BC_CMD_MAX_RETRY_CNT;
+       u32 state;
+
+       state = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_STATE);
+       if (state == QLC_83XX_IDC_DEV_READY) {
+               msleep(20);
+               clear_bit(QLC_BC_VF_CHANNEL, &trans->vf->state);
+               trans->trans_state = QLC_INIT;
+               if (++adapter->fw_fail_cnt > max)
+                       return -EIO;
+               else
+                       return 0;
+       }
+
+       return -EIO;
+}
+
+static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *adapter,
+                                 struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct device *dev = &adapter->pdev->dev;
+       struct qlcnic_bc_trans *trans;
+       int err;
+       u32 rsp_data, opcode, mbx_err_code, rsp;
+       u16 seq = ++adapter->ahw->sriov->bc.trans_counter;
+       u8 func = ahw->pci_func;
+
+       rsp = qlcnic_sriov_alloc_bc_trans(&trans);
+       if (rsp)
+               return rsp;
+
+       rsp = qlcnic_sriov_prepare_bc_hdr(trans, cmd, seq, QLC_BC_COMMAND);
+       if (rsp)
+               goto cleanup_transaction;
+
+retry:
+       if (!test_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status)) {
+               rsp = -EIO;
+               QLCDB(adapter, DRV, "MBX not Ready!(cmd 0x%x) for VF 0x%x\n",
+                     QLCNIC_MBX_RSP(cmd->req.arg[0]), func);
+               goto err_out;
+       }
+
+       err = qlcnic_sriov_send_bc_cmd(adapter, trans, func);
+       if (err) {
+               dev_err(dev, "MBX command 0x%x timed out for VF %d\n",
+                       (cmd->req.arg[0] & 0xffff), func);
+               rsp = QLCNIC_RCODE_TIMEOUT;
+
+               /* After adapter reset PF driver may take some time to
+                * respond to VF's request. Retry request till maximum retries.
+                */
+               if ((trans->req_hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) &&
+                   !qlcnic_sriov_retry_bc_cmd(adapter, trans))
+                       goto retry;
+
+               goto err_out;
+       }
+
+       rsp_data = cmd->rsp.arg[0];
+       mbx_err_code = QLCNIC_MBX_STATUS(rsp_data);
+       opcode = QLCNIC_MBX_RSP(cmd->req.arg[0]);
+
+       if ((mbx_err_code == QLCNIC_MBX_RSP_OK) ||
+           (mbx_err_code == QLCNIC_MBX_PORT_RSP_OK)) {
+               rsp = QLCNIC_RCODE_SUCCESS;
+       } else {
+               rsp = mbx_err_code;
+               if (!rsp)
+                       rsp = 1;
+               dev_err(dev,
+                       "MBX command 0x%x failed with err:0x%x for VF %d\n",
+                       opcode, mbx_err_code, func);
+       }
+
+err_out:
+       if (rsp == QLCNIC_RCODE_TIMEOUT) {
+               ahw->reset_context = 1;
+               adapter->need_fw_reset = 1;
+               clear_bit(QLC_83XX_MBX_READY, &ahw->idc.status);
+       }
+
+cleanup_transaction:
+       qlcnic_sriov_cleanup_transaction(trans);
+       return rsp;
+}
+
+int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *adapter, u8 cmd_op)
+{
+       struct qlcnic_cmd_args cmd;
+       struct qlcnic_vf_info *vf = &adapter->ahw->sriov->vf_info[0];
+       int ret;
+
+       if (qlcnic_sriov_alloc_bc_mbx_args(&cmd, cmd_op))
+               return -ENOMEM;
+
+       ret = qlcnic_issue_cmd(adapter, &cmd);
+       if (ret) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed bc channel %s %d\n", cmd_op ? "term" : "init",
+                       ret);
+               goto out;
+       }
+
+       cmd_op = (cmd.rsp.arg[0] & 0xff);
+       if (cmd.rsp.arg[0] >> 25 == 2)
+               return 2;
+       if (cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT)
+               set_bit(QLC_BC_VF_STATE, &vf->state);
+       else
+               clear_bit(QLC_BC_VF_STATE, &vf->state);
+
+out:
+       qlcnic_free_mbx_args(&cmd);
+       return ret;
+}
+
+void qlcnic_vf_add_mc_list(struct net_device *netdev, u16 vlan)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_mac_list_s *cur;
+       struct list_head *head, tmp_list;
+
+       INIT_LIST_HEAD(&tmp_list);
+       head = &adapter->vf_mc_list;
+       netif_addr_lock_bh(netdev);
+
+       while (!list_empty(head)) {
+               cur = list_entry(head->next, struct qlcnic_mac_list_s, list);
+               list_move(&cur->list, &tmp_list);
+       }
+
+       netif_addr_unlock_bh(netdev);
+
+       while (!list_empty(&tmp_list)) {
+               cur = list_entry((&tmp_list)->next,
+                                struct qlcnic_mac_list_s, list);
+               qlcnic_nic_add_mac(adapter, cur->mac_addr, vlan);
+               list_del(&cur->list);
+               kfree(cur);
+       }
+}
+
+void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc)
+{
+       struct list_head *head = &bc->async_list;
+       struct qlcnic_async_work_list *entry;
+
+       while (!list_empty(head)) {
+               entry = list_entry(head->next, struct qlcnic_async_work_list,
+                                  list);
+               cancel_work_sync(&entry->work);
+               list_del(&entry->list);
+               kfree(entry);
+       }
+}
+
+static void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       u16 vlan;
+
+       if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
+               return;
+
+       vlan = adapter->ahw->sriov->vlan;
+       __qlcnic_set_multi(netdev, vlan);
+}
+
+static void qlcnic_sriov_handle_async_multi(struct work_struct *work)
+{
+       struct qlcnic_async_work_list *entry;
+       struct net_device *netdev;
+
+       entry = container_of(work, struct qlcnic_async_work_list, work);
+       netdev = (struct net_device *)entry->ptr;
+
+       qlcnic_sriov_vf_set_multi(netdev);
+       return;
+}
+
+static struct qlcnic_async_work_list *
+qlcnic_sriov_get_free_node_async_work(struct qlcnic_back_channel *bc)
+{
+       struct list_head *node;
+       struct qlcnic_async_work_list *entry = NULL;
+       u8 empty = 0;
+
+       list_for_each(node, &bc->async_list) {
+               entry = list_entry(node, struct qlcnic_async_work_list, list);
+               if (!work_pending(&entry->work)) {
+                       empty = 1;
+                       break;
+               }
+       }
+
+       if (!empty) {
+               entry = kzalloc(sizeof(struct qlcnic_async_work_list),
+                               GFP_ATOMIC);
+               if (entry == NULL)
+                       return NULL;
+               list_add_tail(&entry->list, &bc->async_list);
+       }
+
+       return entry;
+}
+
+static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc,
+                                               work_func_t func, void *data)
+{
+       struct qlcnic_async_work_list *entry = NULL;
+
+       entry = qlcnic_sriov_get_free_node_async_work(bc);
+       if (!entry)
+               return;
+
+       entry->ptr = data;
+       INIT_WORK(&entry->work, func);
+       queue_work(bc->bc_async_wq, &entry->work);
+}
+
+void qlcnic_sriov_vf_schedule_multi(struct net_device *netdev)
+{
+
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc;
+
+       if (adapter->need_fw_reset)
+               return;
+
+       qlcnic_sriov_schedule_bc_async_work(bc, qlcnic_sriov_handle_async_multi,
+                                           netdev);
+}
+
+static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter)
+{
+       int err;
+
+       set_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status);
+       qlcnic_83xx_enable_mbx_intrpt(adapter);
+
+       err = qlcnic_sriov_cfg_bc_intr(adapter, 1);
+       if (err)
+               return err;
+
+       err = qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_INIT);
+       if (err)
+               goto err_out_cleanup_bc_intr;
+
+       err = qlcnic_sriov_vf_init_driver(adapter);
+       if (err)
+               goto err_out_term_channel;
+
+       return 0;
+
+err_out_term_channel:
+       qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM);
+
+err_out_cleanup_bc_intr:
+       qlcnic_sriov_cfg_bc_intr(adapter, 0);
+       return err;
+}
+
+static void qlcnic_sriov_vf_attach(struct qlcnic_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+
+       if (netif_running(netdev)) {
+               if (!qlcnic_up(adapter, netdev))
+                       qlcnic_restore_indev_addr(netdev, NETDEV_UP);
+       }
+
+       netif_device_attach(netdev);
+}
+
+static void qlcnic_sriov_vf_detach(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlcnic_intrpt_config *intr_tbl = ahw->intr_tbl;
+       struct net_device *netdev = adapter->netdev;
+       u8 i, max_ints = ahw->num_msix - 1;
+
+       qlcnic_83xx_disable_mbx_intr(adapter);
+       netif_device_detach(netdev);
+       if (netif_running(netdev))
+               qlcnic_down(adapter, netdev);
+
+       for (i = 0; i < max_ints; i++) {
+               intr_tbl[i].id = i;
+               intr_tbl[i].enabled = 0;
+               intr_tbl[i].src = 0;
+       }
+       ahw->reset_context = 0;
+}
+
+static int qlcnic_sriov_vf_handle_dev_ready(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct device *dev = &adapter->pdev->dev;
+       struct qlc_83xx_idc *idc = &ahw->idc;
+       u8 func = ahw->pci_func;
+       u32 state;
+
+       if ((idc->prev_state == QLC_83XX_IDC_DEV_NEED_RESET) ||
+           (idc->prev_state == QLC_83XX_IDC_DEV_INIT)) {
+               if (!qlcnic_sriov_vf_reinit_driver(adapter)) {
+                       qlcnic_sriov_vf_attach(adapter);
+                       adapter->fw_fail_cnt = 0;
+                       dev_info(dev,
+                                "%s: Reinitalization of VF 0x%x done after FW reset\n",
+                                __func__, func);
+               } else {
+                       dev_err(dev,
+                               "%s: Reinitialization of VF 0x%x failed after FW reset\n",
+                               __func__, func);
+                       state = QLCRDX(ahw, QLC_83XX_IDC_DEV_STATE);
+                       dev_info(dev, "Current state 0x%x after FW reset\n",
+                                state);
+               }
+       }
+
+       return 0;
+}
+
+static int qlcnic_sriov_vf_handle_context_reset(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct device *dev = &adapter->pdev->dev;
+       struct qlc_83xx_idc *idc = &ahw->idc;
+       u8 func = ahw->pci_func;
+       u32 state;
+
+       adapter->reset_ctx_cnt++;
+
+       /* Skip the context reset and check if FW is hung */
+       if (adapter->reset_ctx_cnt < 3) {
+               adapter->need_fw_reset = 1;
+               clear_bit(QLC_83XX_MBX_READY, &idc->status);
+               dev_info(dev,
+                        "Resetting context, wait here to check if FW is in failed state\n");
+               return 0;
+       }
+
+       /* Check if number of resets exceed the threshold.
+        * If it exceeds the threshold just fail the VF.
+        */
+       if (adapter->reset_ctx_cnt > QLC_83XX_VF_RESET_FAIL_THRESH) {
+               clear_bit(QLC_83XX_MODULE_LOADED, &idc->status);
+               adapter->tx_timeo_cnt = 0;
+               adapter->fw_fail_cnt = 0;
+               adapter->reset_ctx_cnt = 0;
+               qlcnic_sriov_vf_detach(adapter);
+               dev_err(dev,
+                       "Device context resets have exceeded the threshold, device interface will be shutdown\n");
+               return -EIO;
+       }
+
+       dev_info(dev, "Resetting context of VF 0x%x\n", func);
+       dev_info(dev, "%s: Context reset count %d for VF 0x%x\n",
+                __func__, adapter->reset_ctx_cnt, func);
+       set_bit(__QLCNIC_RESETTING, &adapter->state);
+       adapter->need_fw_reset = 1;
+       clear_bit(QLC_83XX_MBX_READY, &idc->status);
+       qlcnic_sriov_vf_detach(adapter);
+       adapter->need_fw_reset = 0;
+
+       if (!qlcnic_sriov_vf_reinit_driver(adapter)) {
+               qlcnic_sriov_vf_attach(adapter);
+               adapter->netdev->trans_start = jiffies;
+               adapter->tx_timeo_cnt = 0;
+               adapter->reset_ctx_cnt = 0;
+               adapter->fw_fail_cnt = 0;
+               dev_info(dev, "Done resetting context for VF 0x%x\n", func);
+       } else {
+               dev_err(dev, "%s: Reinitialization of VF 0x%x failed\n",
+                       __func__, func);
+               state = QLCRDX(ahw, QLC_83XX_IDC_DEV_STATE);
+               dev_info(dev, "%s: Current state 0x%x\n", __func__, state);
+       }
+
+       return 0;
+}
+
+static int qlcnic_sriov_vf_idc_ready_state(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       int ret = 0;
+
+       if (ahw->idc.prev_state != QLC_83XX_IDC_DEV_READY)
+               ret = qlcnic_sriov_vf_handle_dev_ready(adapter);
+       else if (ahw->reset_context)
+               ret = qlcnic_sriov_vf_handle_context_reset(adapter);
+
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       return ret;
+}
+
+static int qlcnic_sriov_vf_idc_failed_state(struct qlcnic_adapter *adapter)
+{
+       struct qlc_83xx_idc *idc = &adapter->ahw->idc;
+
+       dev_err(&adapter->pdev->dev, "Device is in failed state\n");
+       if (idc->prev_state == QLC_83XX_IDC_DEV_READY)
+               qlcnic_sriov_vf_detach(adapter);
+
+       clear_bit(QLC_83XX_MODULE_LOADED, &idc->status);
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       return -EIO;
+}
+
+static int
+qlcnic_sriov_vf_idc_need_quiescent_state(struct qlcnic_adapter *adapter)
+{
+       struct qlc_83xx_idc *idc = &adapter->ahw->idc;
+
+       dev_info(&adapter->pdev->dev, "Device is in quiescent state\n");
+       if (idc->prev_state == QLC_83XX_IDC_DEV_READY) {
+               set_bit(__QLCNIC_RESETTING, &adapter->state);
+               adapter->tx_timeo_cnt = 0;
+               adapter->reset_ctx_cnt = 0;
+               clear_bit(QLC_83XX_MBX_READY, &idc->status);
+               qlcnic_sriov_vf_detach(adapter);
+       }
+
+       return 0;
+}
+
+static int qlcnic_sriov_vf_idc_init_reset_state(struct qlcnic_adapter *adapter)
+{
+       struct qlc_83xx_idc *idc = &adapter->ahw->idc;
+       u8 func = adapter->ahw->pci_func;
+
+       if (idc->prev_state == QLC_83XX_IDC_DEV_READY) {
+               dev_err(&adapter->pdev->dev,
+                       "Firmware hang detected by VF 0x%x\n", func);
+               set_bit(__QLCNIC_RESETTING, &adapter->state);
+               adapter->tx_timeo_cnt = 0;
+               adapter->reset_ctx_cnt = 0;
+               clear_bit(QLC_83XX_MBX_READY, &idc->status);
+               qlcnic_sriov_vf_detach(adapter);
+       }
+       return 0;
+}
+
+static int qlcnic_sriov_vf_idc_unknown_state(struct qlcnic_adapter *adapter)
+{
+       dev_err(&adapter->pdev->dev, "%s: Device in unknown state\n", __func__);
+       return 0;
+}
+
+static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *work)
+{
+       struct qlcnic_adapter *adapter;
+       struct qlc_83xx_idc *idc;
+       int ret = 0;
+
+       adapter = container_of(work, struct qlcnic_adapter, fw_work.work);
+       idc = &adapter->ahw->idc;
+       idc->curr_state = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_STATE);
+
+       switch (idc->curr_state) {
+       case QLC_83XX_IDC_DEV_READY:
+               ret = qlcnic_sriov_vf_idc_ready_state(adapter);
+               break;
+       case QLC_83XX_IDC_DEV_NEED_RESET:
+       case QLC_83XX_IDC_DEV_INIT:
+               ret = qlcnic_sriov_vf_idc_init_reset_state(adapter);
+               break;
+       case QLC_83XX_IDC_DEV_NEED_QUISCENT:
+               ret = qlcnic_sriov_vf_idc_need_quiescent_state(adapter);
+               break;
+       case QLC_83XX_IDC_DEV_FAILED:
+               ret = qlcnic_sriov_vf_idc_failed_state(adapter);
+               break;
+       case QLC_83XX_IDC_DEV_QUISCENT:
+               break;
+       default:
+               ret = qlcnic_sriov_vf_idc_unknown_state(adapter);
+       }
+
+       idc->prev_state = idc->curr_state;
+       if (!ret && test_bit(QLC_83XX_MODULE_LOADED, &idc->status))
+               qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
+                                    idc->delay);
+}
+
+static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *adapter)
+{
+       while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
+               msleep(20);
+
+       clear_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status);
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       cancel_delayed_work_sync(&adapter->fw_work);
+}
+
+static int qlcnic_sriov_validate_vlan_cfg(struct qlcnic_sriov *sriov,
+                                         u16 vid, u8 enable)
+{
+       u16 vlan = sriov->vlan;
+       u8 allowed = 0;
+       int i;
+
+       if (sriov->vlan_mode != QLC_GUEST_VLAN_MODE)
+               return -EINVAL;
+
+       if (enable) {
+               if (vlan)
+                       return -EINVAL;
+
+               if (sriov->any_vlan) {
+                       for (i = 0; i < sriov->num_allowed_vlans; i++) {
+                               if (sriov->allowed_vlans[i] == vid)
+                                       allowed = 1;
+                       }
+
+                       if (!allowed)
+                               return -EINVAL;
+               }
+       } else {
+               if (!vlan || vlan != vid)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter,
+                                  u16 vid, u8 enable)
+{
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_cmd_args cmd;
+       int ret;
+
+       if (vid == 0)
+               return 0;
+
+       ret = qlcnic_sriov_validate_vlan_cfg(sriov, vid, enable);
+       if (ret)
+               return ret;
+
+       ret = qlcnic_sriov_alloc_bc_mbx_args(&cmd,
+                                            QLCNIC_BC_CMD_CFG_GUEST_VLAN);
+       if (ret)
+               return ret;
+
+       cmd.req.arg[1] = (enable & 1) | vid << 16;
+
+       qlcnic_sriov_cleanup_async_list(&sriov->bc);
+       ret = qlcnic_issue_cmd(adapter, &cmd);
+       if (ret) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to configure guest VLAN, err=%d\n", ret);
+       } else {
+               qlcnic_free_mac_list(adapter);
+
+               if (enable)
+                       sriov->vlan = vid;
+               else
+                       sriov->vlan = 0;
+
+               qlcnic_sriov_vf_set_multi(adapter->netdev);
+       }
+
+       qlcnic_free_mbx_args(&cmd);
+       return ret;
+}
+
+static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *adapter)
+{
+       struct list_head *head = &adapter->mac_list;
+       struct qlcnic_mac_list_s *cur;
+       u16 vlan;
+
+       vlan = adapter->ahw->sriov->vlan;
+
+       while (!list_empty(head)) {
+               cur = list_entry(head->next, struct qlcnic_mac_list_s, list);
+               qlcnic_sre_macaddr_change(adapter, cur->mac_addr,
+                                         vlan, QLCNIC_MAC_DEL);
+               list_del(&cur->list);
+               kfree(cur);
+       }
+}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
new file mode 100644 (file)
index 0000000..c81be2d
--- /dev/null
@@ -0,0 +1,1780 @@
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c) 2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#include "qlcnic_sriov.h"
+#include "qlcnic.h"
+#include <linux/types.h>
+
+#define QLCNIC_SRIOV_VF_MAX_MAC 1
+#define QLC_VF_MIN_TX_RATE     100
+#define QLC_VF_MAX_TX_RATE     9999
+
+static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8);
+
+struct qlcnic_sriov_cmd_handler {
+       int (*fn) (struct qlcnic_bc_trans *, struct qlcnic_cmd_args *);
+};
+
+struct qlcnic_sriov_fw_cmd_handler {
+       u32 cmd;
+       int (*fn) (struct qlcnic_bc_trans *, struct qlcnic_cmd_args *);
+};
+
+static int qlcnic_sriov_pf_set_vport_info(struct qlcnic_adapter *adapter,
+                                         struct qlcnic_info *npar_info,
+                                         u16 vport_id)
+{
+       struct qlcnic_cmd_args cmd;
+       int err;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO))
+               return -ENOMEM;
+
+       cmd.req.arg[1] = (vport_id << 16) | 0x1;
+       cmd.req.arg[2] = npar_info->bit_offsets;
+       cmd.req.arg[2] |= npar_info->min_tx_bw << 16;
+       cmd.req.arg[3] = npar_info->max_tx_bw | (npar_info->max_tx_ques << 16);
+       cmd.req.arg[4] = npar_info->max_tx_mac_filters;
+       cmd.req.arg[4] |= npar_info->max_rx_mcast_mac_filters << 16;
+       cmd.req.arg[5] = npar_info->max_rx_ucast_mac_filters |
+                        (npar_info->max_rx_ip_addr << 16);
+       cmd.req.arg[6] = npar_info->max_rx_lro_flow |
+                        (npar_info->max_rx_status_rings << 16);
+       cmd.req.arg[7] = npar_info->max_rx_buf_rings |
+                        (npar_info->max_rx_ques << 16);
+       cmd.req.arg[8] = npar_info->max_tx_vlan_keys;
+       cmd.req.arg[8] |= npar_info->max_local_ipv6_addrs << 16;
+       cmd.req.arg[9] = npar_info->max_remote_ipv6_addrs;
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err)
+               dev_err(&adapter->pdev->dev,
+                       "Failed to set vport info, err=%d\n", err);
+
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter,
+                                        struct qlcnic_info *info, u16 func)
+{
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_resources *res = &sriov->ff_max;
+       u32 temp, num_vf_macs, num_vfs, max;
+       int ret = -EIO, vpid, id;
+       struct qlcnic_vport *vp;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func);
+       if (vpid < 0)
+               return -EINVAL;
+
+       num_vfs = sriov->num_vfs;
+       max = num_vfs + 1;
+       info->bit_offsets = 0xffff;
+       info->max_tx_ques = res->num_tx_queues / max;
+       info->max_rx_mcast_mac_filters = res->num_rx_mcast_mac_filters;
+       num_vf_macs = QLCNIC_SRIOV_VF_MAX_MAC;
+
+       if (adapter->ahw->pci_func == func) {
+               temp = res->num_rx_mcast_mac_filters - (num_vfs * num_vf_macs);
+               info->max_rx_ucast_mac_filters = temp;
+               temp = res->num_tx_mac_filters - (num_vfs * num_vf_macs);
+               info->max_tx_mac_filters = temp;
+               info->min_tx_bw = 0;
+               info->max_tx_bw = MAX_BW;
+       } else {
+               id = qlcnic_sriov_func_to_index(adapter, func);
+               if (id < 0)
+                       return id;
+               vp = sriov->vf_info[id].vp;
+               info->min_tx_bw = vp->min_tx_bw;
+               info->max_tx_bw = vp->max_tx_bw;
+               info->max_rx_ucast_mac_filters = num_vf_macs;
+               info->max_tx_mac_filters = num_vf_macs;
+       }
+
+       info->max_rx_ip_addr = res->num_destip / max;
+       info->max_rx_status_rings = res->num_rx_status_rings / max;
+       info->max_rx_buf_rings = res->num_rx_buf_rings / max;
+       info->max_rx_ques = res->num_rx_queues / max;
+       info->max_rx_lro_flow = res->num_lro_flows_supported / max;
+       info->max_tx_vlan_keys = res->num_txvlan_keys;
+       info->max_local_ipv6_addrs = res->max_local_ipv6_addrs;
+       info->max_remote_ipv6_addrs = res->max_remote_ipv6_addrs;
+
+       ret = qlcnic_sriov_pf_set_vport_info(adapter, info, vpid);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void qlcnic_sriov_pf_set_ff_max_res(struct qlcnic_adapter *adapter,
+                                          struct qlcnic_info *info)
+{
+       struct qlcnic_resources *ff_max = &adapter->ahw->sriov->ff_max;
+
+       ff_max->num_tx_mac_filters = info->max_tx_mac_filters;
+       ff_max->num_rx_ucast_mac_filters = info->max_rx_ucast_mac_filters;
+       ff_max->num_rx_mcast_mac_filters = info->max_rx_mcast_mac_filters;
+       ff_max->num_txvlan_keys = info->max_tx_vlan_keys;
+       ff_max->num_rx_queues = info->max_rx_ques;
+       ff_max->num_tx_queues = info->max_tx_ques;
+       ff_max->num_lro_flows_supported = info->max_rx_lro_flow;
+       ff_max->num_destip = info->max_rx_ip_addr;
+       ff_max->num_rx_buf_rings = info->max_rx_buf_rings;
+       ff_max->num_rx_status_rings = info->max_rx_status_rings;
+       ff_max->max_remote_ipv6_addrs = info->max_remote_ipv6_addrs;
+       ff_max->max_local_ipv6_addrs = info->max_local_ipv6_addrs;
+}
+
+static int qlcnic_sriov_get_pf_info(struct qlcnic_adapter *adapter,
+                                   struct qlcnic_info *npar_info)
+{
+       int err;
+       struct qlcnic_cmd_args cmd;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO))
+               return -ENOMEM;
+
+       cmd.req.arg[1] = 0x2;
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to get PF info, err=%d\n", err);
+               goto out;
+       }
+
+       npar_info->total_pf = cmd.rsp.arg[2] & 0xff;
+       npar_info->total_rss_engines = (cmd.rsp.arg[2] >> 8) & 0xff;
+       npar_info->max_vports = MSW(cmd.rsp.arg[2]);
+       npar_info->max_tx_ques =  LSW(cmd.rsp.arg[3]);
+       npar_info->max_tx_mac_filters = MSW(cmd.rsp.arg[3]);
+       npar_info->max_rx_mcast_mac_filters = LSW(cmd.rsp.arg[4]);
+       npar_info->max_rx_ucast_mac_filters = MSW(cmd.rsp.arg[4]);
+       npar_info->max_rx_ip_addr = LSW(cmd.rsp.arg[5]);
+       npar_info->max_rx_lro_flow = MSW(cmd.rsp.arg[5]);
+       npar_info->max_rx_status_rings = LSW(cmd.rsp.arg[6]);
+       npar_info->max_rx_buf_rings = MSW(cmd.rsp.arg[6]);
+       npar_info->max_rx_ques = LSW(cmd.rsp.arg[7]);
+       npar_info->max_tx_vlan_keys = MSW(cmd.rsp.arg[7]);
+       npar_info->max_local_ipv6_addrs = LSW(cmd.rsp.arg[8]);
+       npar_info->max_remote_ipv6_addrs = MSW(cmd.rsp.arg[8]);
+
+       qlcnic_sriov_pf_set_ff_max_res(adapter, npar_info);
+       dev_info(&adapter->pdev->dev,
+                "\n\ttotal_pf: %d,\n"
+                "\n\ttotal_rss_engines: %d max_vports: %d max_tx_ques %d,\n"
+                "\tmax_tx_mac_filters: %d max_rx_mcast_mac_filters: %d,\n"
+                "\tmax_rx_ucast_mac_filters: 0x%x, max_rx_ip_addr: %d,\n"
+                "\tmax_rx_lro_flow: %d max_rx_status_rings: %d,\n"
+                "\tmax_rx_buf_rings: %d, max_rx_ques: %d, max_tx_vlan_keys %d\n"
+                "\tmax_local_ipv6_addrs: %d, max_remote_ipv6_addrs: %d\n",
+                npar_info->total_pf, npar_info->total_rss_engines,
+                npar_info->max_vports, npar_info->max_tx_ques,
+                npar_info->max_tx_mac_filters,
+                npar_info->max_rx_mcast_mac_filters,
+                npar_info->max_rx_ucast_mac_filters, npar_info->max_rx_ip_addr,
+                npar_info->max_rx_lro_flow, npar_info->max_rx_status_rings,
+                npar_info->max_rx_buf_rings, npar_info->max_rx_ques,
+                npar_info->max_tx_vlan_keys, npar_info->max_local_ipv6_addrs,
+                npar_info->max_remote_ipv6_addrs);
+
+out:
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static void qlcnic_sriov_pf_reset_vport_handle(struct qlcnic_adapter *adapter,
+                                              u8 func)
+{
+       struct qlcnic_sriov  *sriov = adapter->ahw->sriov;
+       struct qlcnic_vport *vp;
+       int index;
+
+       if (adapter->ahw->pci_func == func) {
+               sriov->vp_handle = 0;
+       } else {
+               index = qlcnic_sriov_func_to_index(adapter, func);
+               if (index < 0)
+                       return;
+               vp = sriov->vf_info[index].vp;
+               vp->handle = 0;
+       }
+}
+
+static void qlcnic_sriov_pf_set_vport_handle(struct qlcnic_adapter *adapter,
+                                            u16 vport_handle, u8 func)
+{
+       struct qlcnic_sriov  *sriov = adapter->ahw->sriov;
+       struct qlcnic_vport *vp;
+       int index;
+
+       if (adapter->ahw->pci_func == func) {
+               sriov->vp_handle = vport_handle;
+       } else {
+               index = qlcnic_sriov_func_to_index(adapter, func);
+               if (index < 0)
+                       return;
+               vp = sriov->vf_info[index].vp;
+               vp->handle = vport_handle;
+       }
+}
+
+static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *adapter,
+                                           u8 func)
+{
+       struct qlcnic_sriov  *sriov = adapter->ahw->sriov;
+       struct qlcnic_vf_info *vf_info;
+       int index;
+
+       if (adapter->ahw->pci_func == func) {
+               return sriov->vp_handle;
+       } else {
+               index = qlcnic_sriov_func_to_index(adapter, func);
+               if (index >= 0) {
+                       vf_info = &sriov->vf_info[index];
+                       return vf_info->vp->handle;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int qlcnic_sriov_pf_config_vport(struct qlcnic_adapter *adapter,
+                                       u8 flag, u16 func)
+{
+       struct qlcnic_cmd_args cmd;
+       int ret;
+       int vpid;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_VPORT))
+               return -ENOMEM;
+
+       if (flag) {
+               cmd.req.arg[3] = func << 8;
+       } else {
+               vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func);
+               if (vpid < 0) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+               cmd.req.arg[3] = ((vpid & 0xffff) << 8) | 1;
+       }
+
+       ret = qlcnic_issue_cmd(adapter, &cmd);
+       if (ret) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed %s vport, err %d for func 0x%x\n",
+                       (flag ? "enable" : "disable"), ret, func);
+               goto out;
+       }
+
+       if (flag) {
+               vpid = cmd.rsp.arg[2] & 0xffff;
+               qlcnic_sriov_pf_set_vport_handle(adapter, vpid, func);
+       } else {
+               qlcnic_sriov_pf_reset_vport_handle(adapter, func);
+       }
+
+out:
+       qlcnic_free_mbx_args(&cmd);
+       return ret;
+}
+
+static int qlcnic_sriov_pf_cfg_vlan_filtering(struct qlcnic_adapter *adapter,
+                                             u8 enable)
+{
+       struct qlcnic_cmd_args cmd;
+       int err;
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       if (err)
+               return err;
+
+       cmd.req.arg[1] = 0x4;
+       if (enable)
+               cmd.req.arg[1] |= BIT_16;
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err)
+               dev_err(&adapter->pdev->dev,
+                       "Failed to configure VLAN filtering, err=%d\n", err);
+
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static int qlcnic_sriov_pf_cfg_eswitch(struct qlcnic_adapter *adapter,
+                                      u8 func, u8 enable)
+{
+       struct qlcnic_cmd_args cmd;
+       int err = -EIO;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH))
+               return -ENOMEM;
+
+       cmd.req.arg[0] |= (3 << 29);
+       cmd.req.arg[1] = ((func & 0xf) << 2) | BIT_6 | BIT_1;
+       if (enable)
+               cmd.req.arg[1] |= BIT_0;
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+
+       if (err != QLCNIC_RCODE_SUCCESS) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to enable sriov eswitch%d\n", err);
+               err = -EIO;
+       }
+
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static void qlcnic_sriov_pf_del_flr_queue(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_back_channel *bc = &sriov->bc;
+       int i;
+
+       for (i = 0; i < sriov->num_vfs; i++)
+               cancel_work_sync(&sriov->vf_info[i].flr_work);
+
+       destroy_workqueue(bc->bc_flr_wq);
+}
+
+static int qlcnic_sriov_pf_create_flr_queue(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc;
+       struct workqueue_struct *wq;
+
+       wq = create_singlethread_workqueue("qlcnic-flr");
+       if (wq == NULL) {
+               dev_err(&adapter->pdev->dev, "Cannot create FLR workqueue\n");
+               return -ENOMEM;
+       }
+
+       bc->bc_flr_wq =  wq;
+       return 0;
+}
+
+void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter)
+{
+       u8 func = adapter->ahw->pci_func;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return;
+
+       qlcnic_sriov_pf_del_flr_queue(adapter);
+       qlcnic_sriov_cfg_bc_intr(adapter, 0);
+       qlcnic_sriov_pf_config_vport(adapter, 0, func);
+       qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0);
+       qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 0);
+       __qlcnic_sriov_cleanup(adapter);
+       adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
+       clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
+}
+
+void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter)
+{
+       if (!qlcnic_sriov_pf_check(adapter))
+               return;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return;
+
+       pci_disable_sriov(adapter->pdev);
+       netdev_info(adapter->netdev,
+                   "SR-IOV is disabled successfully on port %d\n",
+                   adapter->portnum);
+}
+
+static int qlcnic_pci_sriov_disable(struct qlcnic_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+
+       if (netif_running(netdev))
+               __qlcnic_down(adapter, netdev);
+
+       qlcnic_sriov_pf_disable(adapter);
+
+       qlcnic_sriov_pf_cleanup(adapter);
+
+       /* After disabling SRIOV re-init the driver in default mode
+          configure opmode based on op_mode of function
+        */
+       if (qlcnic_83xx_configure_opmode(adapter))
+               return -EIO;
+
+       if (netif_running(netdev))
+               __qlcnic_up(adapter, netdev);
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_init(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlcnic_info nic_info, pf_info, vp_info;
+       int err;
+       u8 func = ahw->pci_func;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return 0;
+
+       err = qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 1);
+       if (err)
+               return err;
+
+       err = qlcnic_sriov_pf_cfg_eswitch(adapter, func, 1);
+       if (err)
+               goto disable_vlan_filtering;
+
+       err = qlcnic_sriov_pf_config_vport(adapter, 1, func);
+       if (err)
+               goto disable_eswitch;
+
+       err = qlcnic_sriov_get_pf_info(adapter, &pf_info);
+       if (err)
+               goto delete_vport;
+
+       err = qlcnic_get_nic_info(adapter, &nic_info, func);
+       if (err)
+               goto delete_vport;
+
+       err = qlcnic_sriov_pf_cal_res_limit(adapter, &vp_info, func);
+       if (err)
+               goto delete_vport;
+
+       err = qlcnic_sriov_cfg_bc_intr(adapter, 1);
+       if (err)
+               goto delete_vport;
+
+       ahw->physical_port = (u8) nic_info.phys_port;
+       ahw->switch_mode = nic_info.switch_mode;
+       ahw->max_mtu = nic_info.max_mtu;
+       ahw->capabilities = nic_info.capabilities;
+       ahw->nic_mode = QLC_83XX_SRIOV_MODE;
+       return err;
+
+delete_vport:
+       qlcnic_sriov_pf_config_vport(adapter, 0, func);
+
+disable_eswitch:
+       qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0);
+
+disable_vlan_filtering:
+       qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 0);
+
+       return err;
+}
+
+static int qlcnic_sriov_pf_enable(struct qlcnic_adapter *adapter, int num_vfs)
+{
+       int err;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return 0;
+
+       err = pci_enable_sriov(adapter->pdev, num_vfs);
+       if (err)
+               qlcnic_sriov_pf_cleanup(adapter);
+
+       return err;
+}
+
+static int __qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter,
+                                    int num_vfs)
+{
+       int err = 0;
+
+       set_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
+       adapter->ahw->op_mode = QLCNIC_SRIOV_PF_FUNC;
+
+       err = qlcnic_sriov_init(adapter, num_vfs);
+       if (err)
+               goto clear_op_mode;
+
+       err = qlcnic_sriov_pf_create_flr_queue(adapter);
+       if (err)
+               goto sriov_cleanup;
+
+       err = qlcnic_sriov_pf_init(adapter);
+       if (err)
+               goto del_flr_queue;
+
+       err = qlcnic_sriov_pf_enable(adapter, num_vfs);
+       return err;
+
+del_flr_queue:
+       qlcnic_sriov_pf_del_flr_queue(adapter);
+
+sriov_cleanup:
+       __qlcnic_sriov_cleanup(adapter);
+
+clear_op_mode:
+       clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
+       adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
+       return err;
+}
+
+static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs)
+{
+       struct net_device *netdev = adapter->netdev;
+       int err;
+
+       if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
+               netdev_err(netdev,
+                          "SR-IOV cannot be enabled, when legacy interrupts are enabled\n");
+               return -EIO;
+       }
+
+       if (netif_running(netdev))
+               __qlcnic_down(adapter, netdev);
+
+       err = __qlcnic_pci_sriov_enable(adapter, num_vfs);
+       if (err) {
+               netdev_info(netdev, "Failed to enable SR-IOV on port %d\n",
+                           adapter->portnum);
+
+               err = -EIO;
+               if (qlcnic_83xx_configure_opmode(adapter))
+                       goto error;
+       } else {
+               netdev_info(netdev,
+                           "SR-IOV is enabled successfully on port %d\n",
+                           adapter->portnum);
+               /* Return number of vfs enabled */
+               err = num_vfs;
+       }
+       if (netif_running(netdev))
+               __qlcnic_up(adapter, netdev);
+
+error:
+       return err;
+}
+
+int qlcnic_pci_sriov_configure(struct pci_dev *dev, int num_vfs)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(dev);
+       int err;
+
+       if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
+               return -EBUSY;
+
+       if (num_vfs == 0)
+               err = qlcnic_pci_sriov_disable(adapter);
+       else
+               err = qlcnic_pci_sriov_enable(adapter, num_vfs);
+
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       return err;
+}
+
+static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func)
+{
+       struct qlcnic_cmd_args cmd;
+       struct qlcnic_vport *vp;
+       int err, id;
+
+       id = qlcnic_sriov_func_to_index(adapter, func);
+       if (id < 0)
+               return id;
+
+       vp = adapter->ahw->sriov->vf_info[id].vp;
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       if (err)
+               return err;
+
+       cmd.req.arg[1] = 0x3 | func << 16;
+       if (vp->vlan_mode == QLC_PVID_MODE) {
+               cmd.req.arg[2] |= BIT_6;
+               cmd.req.arg[3] |= vp->vlan << 8;
+       }
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err)
+               dev_err(&adapter->pdev->dev, "Failed to set ACL, err=%d\n",
+                       err);
+
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static int qlcnic_sriov_set_vf_vport_info(struct qlcnic_adapter *adapter,
+                                         u16 func)
+{
+       struct qlcnic_info defvp_info;
+       int err;
+
+       err = qlcnic_sriov_pf_cal_res_limit(adapter, &defvp_info, func);
+       if (err)
+               return -EIO;
+
+       err = qlcnic_sriov_set_vf_acl(adapter, func);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_channel_cfg_cmd(struct qlcnic_bc_trans *trans,
+                                          struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+       u16 func = vf->pci_func;
+
+       cmd->rsp.arg[0] = trans->req_hdr->cmd_op;
+       cmd->rsp.arg[0] |= (1 << 16);
+
+       if (trans->req_hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) {
+               err = qlcnic_sriov_pf_config_vport(adapter, 1, func);
+               if (!err) {
+                       err = qlcnic_sriov_set_vf_vport_info(adapter, func);
+                       if (err)
+                               qlcnic_sriov_pf_config_vport(adapter, 0, func);
+               }
+       } else {
+               err = qlcnic_sriov_pf_config_vport(adapter, 0, func);
+       }
+
+       if (err)
+               goto err_out;
+
+       cmd->rsp.arg[0] |= (1 << 25);
+
+       if (trans->req_hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT)
+               set_bit(QLC_BC_VF_STATE, &vf->state);
+       else
+               clear_bit(QLC_BC_VF_STATE, &vf->state);
+
+       return err;
+
+err_out:
+       cmd->rsp.arg[0] |= (2 << 25);
+       return err;
+}
+
+static int qlcnic_sriov_cfg_vf_def_mac(struct qlcnic_adapter *adapter,
+                                      struct qlcnic_vport *vp,
+                                      u16 func, u16 vlan, u8 op)
+{
+       struct qlcnic_cmd_args cmd;
+       struct qlcnic_macvlan_mbx mv;
+       u8 *addr;
+       int err;
+       u32 *buf;
+       int vpid;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN))
+               return -ENOMEM;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func);
+       if (vpid < 0) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (vlan)
+               op = ((op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ?
+                     QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL);
+
+       cmd.req.arg[1] = op | (1 << 8) | (3 << 6);
+       cmd.req.arg[1] |= ((vpid & 0xffff) << 16) | BIT_31;
+
+       addr = vp->mac;
+       mv.vlan = vlan;
+       mv.mac_addr0 = addr[0];
+       mv.mac_addr1 = addr[1];
+       mv.mac_addr2 = addr[2];
+       mv.mac_addr3 = addr[3];
+       mv.mac_addr4 = addr[4];
+       mv.mac_addr5 = addr[5];
+       buf = &cmd.req.arg[2];
+       memcpy(buf, &mv, sizeof(struct qlcnic_macvlan_mbx));
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+
+       if (err)
+               dev_err(&adapter->pdev->dev,
+                       "MAC-VLAN %s to CAM failed, err=%d.\n",
+                       ((op == 1) ? "add " : "delete "), err);
+
+out:
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static int qlcnic_sriov_validate_create_rx_ctx(struct qlcnic_cmd_args *cmd)
+{
+       if ((cmd->req.arg[0] >> 29) != 0x3)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_create_rx_ctx_cmd(struct qlcnic_bc_trans *tran,
+                                            struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = tran->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       struct qlcnic_rcv_mbx_out *mbx_out;
+       int err;
+       u16 vlan;
+
+       err = qlcnic_sriov_validate_create_rx_ctx(cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       cmd->req.arg[6] = vf->vp->handle;
+       err = qlcnic_issue_cmd(adapter, cmd);
+
+       vlan = vf->vp->vlan;
+       if (!err) {
+               mbx_out = (struct qlcnic_rcv_mbx_out *)&cmd->rsp.arg[1];
+               vf->rx_ctx_id = mbx_out->ctx_id;
+               qlcnic_sriov_cfg_vf_def_mac(adapter, vf->vp, vf->pci_func,
+                                           vlan, QLCNIC_MAC_ADD);
+       } else {
+               vf->rx_ctx_id = 0;
+       }
+
+       return err;
+}
+
+static int qlcnic_sriov_pf_mac_address_cmd(struct qlcnic_bc_trans *trans,
+                                          struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       u8 type, *mac;
+
+       type = cmd->req.arg[1];
+       switch (type) {
+       case QLCNIC_SET_STATION_MAC:
+       case QLCNIC_SET_FAC_DEF_MAC:
+               cmd->rsp.arg[0] = (2 << 25);
+               break;
+       case QLCNIC_GET_CURRENT_MAC:
+               cmd->rsp.arg[0] = (1 << 25);
+               mac = vf->vp->mac;
+               cmd->rsp.arg[2] = mac[1] | ((mac[0] << 8) & 0xff00);
+               cmd->rsp.arg[1] = mac[5] | ((mac[4] << 8) & 0xff00) |
+                                 ((mac[3]) << 16 & 0xff0000) |
+                                 ((mac[2]) << 24 & 0xff000000);
+       }
+
+       return 0;
+}
+
+static int qlcnic_sriov_validate_create_tx_ctx(struct qlcnic_cmd_args *cmd)
+{
+       if ((cmd->req.arg[0] >> 29) != 0x3)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_create_tx_ctx_cmd(struct qlcnic_bc_trans *trans,
+                                            struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       struct qlcnic_tx_mbx_out *mbx_out;
+       int err;
+
+       err = qlcnic_sriov_validate_create_tx_ctx(cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       cmd->req.arg[5] |= vf->vp->handle << 16;
+       err = qlcnic_issue_cmd(adapter, cmd);
+       if (!err) {
+               mbx_out = (struct qlcnic_tx_mbx_out *)&cmd->rsp.arg[2];
+               vf->tx_ctx_id = mbx_out->ctx_id;
+       } else {
+               vf->tx_ctx_id = 0;
+       }
+
+       return err;
+}
+
+static int qlcnic_sriov_validate_del_rx_ctx(struct qlcnic_vf_info *vf,
+                                           struct qlcnic_cmd_args *cmd)
+{
+       if ((cmd->req.arg[0] >> 29) != 0x3)
+               return -EINVAL;
+
+       if ((cmd->req.arg[1] & 0xffff) != vf->rx_ctx_id)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_del_rx_ctx_cmd(struct qlcnic_bc_trans *trans,
+                                         struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+       u16 vlan;
+
+       err = qlcnic_sriov_validate_del_rx_ctx(vf, cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       vlan = vf->vp->vlan;
+       qlcnic_sriov_cfg_vf_def_mac(adapter, vf->vp, vf->pci_func,
+                                   vlan, QLCNIC_MAC_DEL);
+       cmd->req.arg[1] |= vf->vp->handle << 16;
+       err = qlcnic_issue_cmd(adapter, cmd);
+
+       if (!err)
+               vf->rx_ctx_id = 0;
+
+       return err;
+}
+
+static int qlcnic_sriov_validate_del_tx_ctx(struct qlcnic_vf_info *vf,
+                                           struct qlcnic_cmd_args *cmd)
+{
+       if ((cmd->req.arg[0] >> 29) != 0x3)
+               return -EINVAL;
+
+       if ((cmd->req.arg[1] & 0xffff) != vf->tx_ctx_id)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_del_tx_ctx_cmd(struct qlcnic_bc_trans *trans,
+                                         struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_del_tx_ctx(vf, cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       cmd->req.arg[1] |= vf->vp->handle << 16;
+       err = qlcnic_issue_cmd(adapter, cmd);
+
+       if (!err)
+               vf->tx_ctx_id = 0;
+
+       return err;
+}
+
+static int qlcnic_sriov_validate_cfg_lro(struct qlcnic_vf_info *vf,
+                                        struct qlcnic_cmd_args *cmd)
+{
+       if ((cmd->req.arg[1] >> 16) != vf->rx_ctx_id)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_cfg_lro_cmd(struct qlcnic_bc_trans *trans,
+                                      struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_cfg_lro(vf, cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       err = qlcnic_issue_cmd(adapter, cmd);
+       return err;
+}
+
+static int qlcnic_sriov_pf_cfg_ip_cmd(struct qlcnic_bc_trans *trans,
+                                     struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err = -EIO;
+       u8 op;
+
+       op =  cmd->req.arg[1] & 0xff;
+
+       cmd->req.arg[1] |= vf->vp->handle << 16;
+       cmd->req.arg[1] |= BIT_31;
+
+       err = qlcnic_issue_cmd(adapter, cmd);
+       return err;
+}
+
+static int qlcnic_sriov_validate_cfg_intrpt(struct qlcnic_vf_info *vf,
+                                           struct qlcnic_cmd_args *cmd)
+{
+       if (((cmd->req.arg[1] >> 8) & 0xff) != vf->pci_func)
+               return -EINVAL;
+
+       if (!(cmd->req.arg[1] & BIT_16))
+               return -EINVAL;
+
+       if ((cmd->req.arg[1] & 0xff) != 0x1)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_cfg_intrpt_cmd(struct qlcnic_bc_trans *trans,
+                                         struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_cfg_intrpt(vf, cmd);
+       if (err)
+               cmd->rsp.arg[0] |= (0x6 << 25);
+       else
+               err = qlcnic_issue_cmd(adapter, cmd);
+
+       return err;
+}
+
+static int qlcnic_sriov_validate_mtu(struct qlcnic_adapter *adapter,
+                                    struct qlcnic_vf_info *vf,
+                                    struct qlcnic_cmd_args *cmd)
+{
+       if (cmd->req.arg[1] != vf->rx_ctx_id)
+               return -EINVAL;
+
+       if (cmd->req.arg[2] > adapter->ahw->max_mtu)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_set_mtu_cmd(struct qlcnic_bc_trans *trans,
+                                      struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_mtu(adapter, vf, cmd);
+       if (err)
+               cmd->rsp.arg[0] |= (0x6 << 25);
+       else
+               err = qlcnic_issue_cmd(adapter, cmd);
+
+       return err;
+}
+
+static int qlcnic_sriov_validate_get_nic_info(struct qlcnic_vf_info *vf,
+                                             struct qlcnic_cmd_args *cmd)
+{
+       if (cmd->req.arg[1] & BIT_31) {
+               if (((cmd->req.arg[1] >> 16) & 0x7fff) != vf->pci_func)
+                       return -EINVAL;
+       } else {
+               cmd->req.arg[1] |= vf->vp->handle << 16;
+       }
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_get_nic_info_cmd(struct qlcnic_bc_trans *trans,
+                                           struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_get_nic_info(vf, cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       err = qlcnic_issue_cmd(adapter, cmd);
+       return err;
+}
+
+static int qlcnic_sriov_validate_cfg_rss(struct qlcnic_vf_info *vf,
+                                        struct qlcnic_cmd_args *cmd)
+{
+       if (cmd->req.arg[1] != vf->rx_ctx_id)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_cfg_rss_cmd(struct qlcnic_bc_trans *trans,
+                                      struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_cfg_rss(vf, cmd);
+       if (err)
+               cmd->rsp.arg[0] |= (0x6 << 25);
+       else
+               err = qlcnic_issue_cmd(adapter, cmd);
+
+       return err;
+}
+
+static int qlcnic_sriov_validate_cfg_intrcoal(struct qlcnic_adapter *adapter,
+                                             struct qlcnic_vf_info *vf,
+                                             struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal;
+       u16 ctx_id, pkts, time;
+
+       ctx_id = cmd->req.arg[1] >> 16;
+       pkts = cmd->req.arg[2] & 0xffff;
+       time = cmd->req.arg[2] >> 16;
+
+       if (ctx_id != vf->rx_ctx_id)
+               return -EINVAL;
+       if (pkts > coal->rx_packets)
+               return -EINVAL;
+       if (time < coal->rx_time_us)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_cfg_intrcoal_cmd(struct qlcnic_bc_trans *tran,
+                                           struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = tran->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_cfg_intrcoal(adapter, vf, cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       err = qlcnic_issue_cmd(adapter, cmd);
+       return err;
+}
+
+static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter,
+                                            struct qlcnic_vf_info *vf,
+                                            struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_macvlan_mbx *macvlan;
+       struct qlcnic_vport *vp = vf->vp;
+       u8 op, new_op;
+
+       if (!(cmd->req.arg[1] & BIT_8))
+               return -EINVAL;
+
+       cmd->req.arg[1] |= (vf->vp->handle << 16);
+       cmd->req.arg[1] |= BIT_31;
+
+       macvlan = (struct qlcnic_macvlan_mbx *)&cmd->req.arg[2];
+       if (!(macvlan->mac_addr0 & BIT_0)) {
+               dev_err(&adapter->pdev->dev,
+                       "MAC address change is not allowed from VF %d",
+                       vf->pci_func);
+               return -EINVAL;
+       }
+
+       if (vp->vlan_mode == QLC_PVID_MODE) {
+               op = cmd->req.arg[1] & 0x7;
+               cmd->req.arg[1] &= ~0x7;
+               new_op = (op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ?
+                        QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL;
+               cmd->req.arg[3] |= vp->vlan << 16;
+               cmd->req.arg[1] |= new_op;
+       }
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_cfg_macvlan_cmd(struct qlcnic_bc_trans *trans,
+                                          struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_cfg_macvlan(adapter, vf, cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       err = qlcnic_issue_cmd(adapter, cmd);
+       return err;
+}
+
+static int qlcnic_sriov_validate_linkevent(struct qlcnic_vf_info *vf,
+                                          struct qlcnic_cmd_args *cmd)
+{
+       if ((cmd->req.arg[1] >> 16) != vf->rx_ctx_id)
+               return -EINVAL;
+
+       if (!(cmd->req.arg[1] & BIT_8))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_linkevent_cmd(struct qlcnic_bc_trans *trans,
+                                        struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       err = qlcnic_sriov_validate_linkevent(vf, cmd);
+       if (err) {
+               cmd->rsp.arg[0] |= (0x6 << 25);
+               return err;
+       }
+
+       err = qlcnic_issue_cmd(adapter, cmd);
+       return err;
+}
+
+static int qlcnic_sriov_pf_cfg_promisc_cmd(struct qlcnic_bc_trans *trans,
+                                          struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_adapter *adapter = vf->adapter;
+       int err;
+
+       cmd->req.arg[1] |= vf->vp->handle << 16;
+       cmd->req.arg[1] |= BIT_31;
+       err = qlcnic_issue_cmd(adapter, cmd);
+       return err;
+}
+
+static int qlcnic_sriov_pf_get_acl_cmd(struct qlcnic_bc_trans *trans,
+                                      struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info *vf = trans->vf;
+       struct qlcnic_vport *vp = vf->vp;
+       u8 cmd_op, mode = vp->vlan_mode;
+
+       cmd_op = trans->req_hdr->cmd_op;
+       cmd->rsp.arg[0] = (cmd_op & 0xffff) | 14 << 16 | 1 << 25;
+
+       switch (mode) {
+       case QLC_GUEST_VLAN_MODE:
+               cmd->rsp.arg[1] = mode | 1 << 8;
+               cmd->rsp.arg[2] = 1 << 16;
+               break;
+       case QLC_PVID_MODE:
+               cmd->rsp.arg[1] = mode | 1 << 8 | vp->vlan << 16;
+               break;
+       }
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_del_guest_vlan(struct qlcnic_adapter *adapter,
+                                         struct qlcnic_vf_info *vf)
+
+{
+       struct qlcnic_vport *vp = vf->vp;
+
+       if (!vp->vlan)
+               return -EINVAL;
+
+       if (!vf->rx_ctx_id) {
+               vp->vlan = 0;
+               return 0;
+       }
+
+       qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func,
+                                   vp->vlan, QLCNIC_MAC_DEL);
+       vp->vlan = 0;
+       qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func,
+                                   0, QLCNIC_MAC_ADD);
+       return 0;
+}
+
+static int qlcnic_sriov_pf_add_guest_vlan(struct qlcnic_adapter *adapter,
+                                         struct qlcnic_vf_info *vf,
+                                         struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vport *vp = vf->vp;
+       int err = -EIO;
+
+       if (vp->vlan)
+               return err;
+
+       if (!vf->rx_ctx_id) {
+               vp->vlan = cmd->req.arg[1] >> 16;
+               return 0;
+       }
+
+       err = qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func,
+                                         0, QLCNIC_MAC_DEL);
+       if (err)
+               return err;
+
+       vp->vlan = cmd->req.arg[1] >> 16;
+       err = qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func,
+                                         vp->vlan, QLCNIC_MAC_ADD);
+
+       if (err) {
+               qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func,
+                                           0, QLCNIC_MAC_ADD);
+               vp->vlan = 0;
+       }
+
+       return err;
+}
+
+static int qlcnic_sriov_pf_cfg_guest_vlan_cmd(struct qlcnic_bc_trans *tran,
+                                             struct qlcnic_cmd_args *cmd)
+{
+       struct qlcnic_vf_info  *vf = tran->vf;
+       struct qlcnic_adapter *adapter =  vf->adapter;
+       struct qlcnic_vport *vp = vf->vp;
+       int err = -EIO;
+       u8 op;
+
+       if (vp->vlan_mode != QLC_GUEST_VLAN_MODE) {
+               cmd->rsp.arg[0] |= 2 << 25;
+               return err;
+       }
+
+       op = cmd->req.arg[1] & 0xf;
+
+       if (op)
+               err = qlcnic_sriov_pf_add_guest_vlan(adapter, vf, cmd);
+       else
+               err = qlcnic_sriov_pf_del_guest_vlan(adapter, vf);
+
+       cmd->rsp.arg[0] |= err ? 2 << 25 : 1 << 25;
+       return err;
+}
+
+static const int qlcnic_pf_passthru_supp_cmds[] = {
+       QLCNIC_CMD_GET_STATISTICS,
+       QLCNIC_CMD_GET_PORT_CONFIG,
+       QLCNIC_CMD_GET_LINK_STATUS,
+};
+
+static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = {
+       [QLCNIC_BC_CMD_CHANNEL_INIT] = {&qlcnic_sriov_pf_channel_cfg_cmd},
+       [QLCNIC_BC_CMD_CHANNEL_TERM] = {&qlcnic_sriov_pf_channel_cfg_cmd},
+       [QLCNIC_BC_CMD_GET_ACL] = {&qlcnic_sriov_pf_get_acl_cmd},
+       [QLCNIC_BC_CMD_CFG_GUEST_VLAN]  = {&qlcnic_sriov_pf_cfg_guest_vlan_cmd},
+};
+
+static const struct qlcnic_sriov_fw_cmd_handler qlcnic_pf_fw_cmd_hdlr[] = {
+       {QLCNIC_CMD_CREATE_RX_CTX, qlcnic_sriov_pf_create_rx_ctx_cmd},
+       {QLCNIC_CMD_CREATE_TX_CTX, qlcnic_sriov_pf_create_tx_ctx_cmd},
+       {QLCNIC_CMD_MAC_ADDRESS, qlcnic_sriov_pf_mac_address_cmd},
+       {QLCNIC_CMD_DESTROY_RX_CTX, qlcnic_sriov_pf_del_rx_ctx_cmd},
+       {QLCNIC_CMD_DESTROY_TX_CTX, qlcnic_sriov_pf_del_tx_ctx_cmd},
+       {QLCNIC_CMD_CONFIGURE_HW_LRO, qlcnic_sriov_pf_cfg_lro_cmd},
+       {QLCNIC_CMD_CONFIGURE_IP_ADDR, qlcnic_sriov_pf_cfg_ip_cmd},
+       {QLCNIC_CMD_CONFIG_INTRPT, qlcnic_sriov_pf_cfg_intrpt_cmd},
+       {QLCNIC_CMD_SET_MTU, qlcnic_sriov_pf_set_mtu_cmd},
+       {QLCNIC_CMD_GET_NIC_INFO, qlcnic_sriov_pf_get_nic_info_cmd},
+       {QLCNIC_CMD_CONFIGURE_RSS, qlcnic_sriov_pf_cfg_rss_cmd},
+       {QLCNIC_CMD_CONFIG_INTR_COAL, qlcnic_sriov_pf_cfg_intrcoal_cmd},
+       {QLCNIC_CMD_CONFIG_MAC_VLAN, qlcnic_sriov_pf_cfg_macvlan_cmd},
+       {QLCNIC_CMD_GET_LINK_EVENT, qlcnic_sriov_pf_linkevent_cmd},
+       {QLCNIC_CMD_CONFIGURE_MAC_RX_MODE, qlcnic_sriov_pf_cfg_promisc_cmd},
+};
+
+void qlcnic_sriov_pf_process_bc_cmd(struct qlcnic_adapter *adapter,
+                                   struct qlcnic_bc_trans *trans,
+                                   struct qlcnic_cmd_args *cmd)
+{
+       u8 size, cmd_op;
+
+       cmd_op = trans->req_hdr->cmd_op;
+
+       if (trans->req_hdr->op_type == QLC_BC_CMD) {
+               size = ARRAY_SIZE(qlcnic_pf_bc_cmd_hdlr);
+               if (cmd_op < size) {
+                       qlcnic_pf_bc_cmd_hdlr[cmd_op].fn(trans, cmd);
+                       return;
+               }
+       } else {
+               int i;
+               size = ARRAY_SIZE(qlcnic_pf_fw_cmd_hdlr);
+               for (i = 0; i < size; i++) {
+                       if (cmd_op == qlcnic_pf_fw_cmd_hdlr[i].cmd) {
+                               qlcnic_pf_fw_cmd_hdlr[i].fn(trans, cmd);
+                               return;
+                       }
+               }
+
+               size = ARRAY_SIZE(qlcnic_pf_passthru_supp_cmds);
+               for (i = 0; i < size; i++) {
+                       if (cmd_op == qlcnic_pf_passthru_supp_cmds[i]) {
+                               qlcnic_issue_cmd(adapter, cmd);
+                               return;
+                       }
+               }
+       }
+
+       cmd->rsp.arg[0] |= (0x9 << 25);
+}
+
+void qlcnic_pf_set_interface_id_create_rx_ctx(struct qlcnic_adapter *adapter,
+                                            u32 *int_id)
+{
+       u16 vpid;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter,
+                                               adapter->ahw->pci_func);
+       *int_id |= vpid;
+}
+
+void qlcnic_pf_set_interface_id_del_rx_ctx(struct qlcnic_adapter *adapter,
+                                          u32 *int_id)
+{
+       u16 vpid;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter,
+                                               adapter->ahw->pci_func);
+       *int_id |= vpid << 16;
+}
+
+void qlcnic_pf_set_interface_id_create_tx_ctx(struct qlcnic_adapter *adapter,
+                                             u32 *int_id)
+{
+       int vpid;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter,
+                                               adapter->ahw->pci_func);
+       *int_id |= vpid << 16;
+}
+
+void qlcnic_pf_set_interface_id_del_tx_ctx(struct qlcnic_adapter *adapter,
+                                          u32 *int_id)
+{
+       u16 vpid;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter,
+                                               adapter->ahw->pci_func);
+       *int_id |= vpid << 16;
+}
+
+void qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *adapter,
+                                       u32 *int_id)
+{
+       u16 vpid;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter,
+                                               adapter->ahw->pci_func);
+       *int_id |= (vpid << 16) | BIT_31;
+}
+
+void qlcnic_pf_set_interface_id_ipaddr(struct qlcnic_adapter *adapter,
+                                      u32 *int_id)
+{
+       u16 vpid;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter,
+                                               adapter->ahw->pci_func);
+       *int_id |= (vpid << 16) | BIT_31;
+}
+
+void qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *adapter,
+                                       u32 *int_id)
+{
+       u16 vpid;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter,
+                                               adapter->ahw->pci_func);
+       *int_id |= (vpid << 16) | BIT_31;
+}
+
+static void qlcnic_sriov_del_rx_ctx(struct qlcnic_adapter *adapter,
+                                   struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_cmd_args cmd;
+       int vpid;
+
+       if (!vf->rx_ctx_id)
+               return;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX))
+               return;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter, vf->pci_func);
+       if (vpid >= 0) {
+               cmd.req.arg[1] = vf->rx_ctx_id | (vpid & 0xffff) << 16;
+               if (qlcnic_issue_cmd(adapter, &cmd))
+                       dev_err(&adapter->pdev->dev,
+                               "Failed to delete Tx ctx in firmware for func 0x%x\n",
+                               vf->pci_func);
+               else
+                       vf->rx_ctx_id = 0;
+       }
+
+       qlcnic_free_mbx_args(&cmd);
+}
+
+static void qlcnic_sriov_del_tx_ctx(struct qlcnic_adapter *adapter,
+                                   struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_cmd_args cmd;
+       int vpid;
+
+       if (!vf->tx_ctx_id)
+               return;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX))
+               return;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter, vf->pci_func);
+       if (vpid >= 0) {
+               cmd.req.arg[1] |= vf->tx_ctx_id | (vpid & 0xffff) << 16;
+               if (qlcnic_issue_cmd(adapter, &cmd))
+                       dev_err(&adapter->pdev->dev,
+                               "Failed to delete Tx ctx in firmware for func 0x%x\n",
+                               vf->pci_func);
+               else
+                       vf->tx_ctx_id = 0;
+       }
+
+       qlcnic_free_mbx_args(&cmd);
+}
+
+static int qlcnic_sriov_add_act_list_irqsave(struct qlcnic_sriov *sriov,
+                                            struct qlcnic_vf_info *vf,
+                                            struct qlcnic_bc_trans *trans)
+{
+       struct qlcnic_trans_list *t_list = &vf->rcv_act;
+       unsigned long flag;
+
+       spin_lock_irqsave(&t_list->lock, flag);
+
+       __qlcnic_sriov_add_act_list(sriov, vf, trans);
+
+       spin_unlock_irqrestore(&t_list->lock, flag);
+       return 0;
+}
+
+static void __qlcnic_sriov_process_flr(struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_adapter *adapter = vf->adapter;
+
+       qlcnic_sriov_cleanup_list(&vf->rcv_pend);
+       cancel_work_sync(&vf->trans_work);
+       qlcnic_sriov_cleanup_list(&vf->rcv_act);
+
+       if (test_bit(QLC_BC_VF_SOFT_FLR, &vf->state)) {
+               qlcnic_sriov_del_tx_ctx(adapter, vf);
+               qlcnic_sriov_del_rx_ctx(adapter, vf);
+       }
+
+       qlcnic_sriov_pf_config_vport(adapter, 0, vf->pci_func);
+
+       clear_bit(QLC_BC_VF_FLR, &vf->state);
+       if (test_bit(QLC_BC_VF_SOFT_FLR, &vf->state)) {
+               qlcnic_sriov_add_act_list_irqsave(adapter->ahw->sriov, vf,
+                                                 vf->flr_trans);
+               clear_bit(QLC_BC_VF_SOFT_FLR, &vf->state);
+               vf->flr_trans = NULL;
+       }
+}
+
+static void qlcnic_sriov_pf_process_flr(struct work_struct *work)
+{
+       struct qlcnic_vf_info *vf;
+
+       vf = container_of(work, struct qlcnic_vf_info, flr_work);
+       __qlcnic_sriov_process_flr(vf);
+       return;
+}
+
+static void qlcnic_sriov_schedule_flr(struct qlcnic_sriov *sriov,
+                                     struct qlcnic_vf_info *vf,
+                                     work_func_t func)
+{
+       if (test_bit(__QLCNIC_RESETTING, &vf->adapter->state))
+               return;
+
+       INIT_WORK(&vf->flr_work, func);
+       queue_work(sriov->bc.bc_flr_wq, &vf->flr_work);
+}
+
+static void qlcnic_sriov_handle_soft_flr(struct qlcnic_adapter *adapter,
+                                        struct qlcnic_bc_trans *trans,
+                                        struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+
+       set_bit(QLC_BC_VF_FLR, &vf->state);
+       clear_bit(QLC_BC_VF_STATE, &vf->state);
+       set_bit(QLC_BC_VF_SOFT_FLR, &vf->state);
+       vf->flr_trans = trans;
+       qlcnic_sriov_schedule_flr(sriov, vf, qlcnic_sriov_pf_process_flr);
+       netdev_info(adapter->netdev, "Software FLR for PCI func %d\n",
+                   vf->pci_func);
+}
+
+bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *adapter,
+                                struct qlcnic_bc_trans *trans,
+                                struct qlcnic_vf_info *vf)
+{
+       struct qlcnic_bc_hdr *hdr = trans->req_hdr;
+
+       if ((hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) &&
+           (hdr->op_type == QLC_BC_CMD) &&
+            test_bit(QLC_BC_VF_STATE, &vf->state)) {
+               qlcnic_sriov_handle_soft_flr(adapter, trans, vf);
+               return true;
+       }
+
+       return false;
+}
+
+void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *sriov,
+                               struct qlcnic_vf_info *vf)
+{
+       struct net_device *dev = vf->adapter->netdev;
+
+       if (!test_and_clear_bit(QLC_BC_VF_STATE, &vf->state)) {
+               clear_bit(QLC_BC_VF_FLR, &vf->state);
+               return;
+       }
+
+       if (test_and_set_bit(QLC_BC_VF_FLR, &vf->state)) {
+               netdev_info(dev, "FLR for PCI func %d in progress\n",
+                           vf->pci_func);
+               return;
+       }
+
+       qlcnic_sriov_schedule_flr(sriov, vf, qlcnic_sriov_pf_process_flr);
+       netdev_info(dev, "FLR received for PCI func %d\n", vf->pci_func);
+}
+
+void qlcnic_sriov_pf_reset(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlcnic_sriov *sriov = ahw->sriov;
+       struct qlcnic_vf_info *vf;
+       u16 num_vfs = sriov->num_vfs;
+       int i;
+
+       for (i = 0; i < num_vfs; i++) {
+               vf = &sriov->vf_info[i];
+               vf->rx_ctx_id = 0;
+               vf->tx_ctx_id = 0;
+               cancel_work_sync(&vf->flr_work);
+               __qlcnic_sriov_process_flr(vf);
+               clear_bit(QLC_BC_VF_STATE, &vf->state);
+       }
+
+       qlcnic_sriov_pf_reset_vport_handle(adapter, ahw->pci_func);
+       QLCWRX(ahw, QLCNIC_MBX_INTR_ENBL, (ahw->num_msix - 1) << 8);
+}
+
+int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       int err;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return 0;
+
+       ahw->op_mode = QLCNIC_SRIOV_PF_FUNC;
+
+       err = qlcnic_sriov_pf_init(adapter);
+       if (err)
+               return err;
+
+       dev_info(&adapter->pdev->dev, "%s: op_mode %d\n",
+                __func__, ahw->op_mode);
+       return err;
+}
+
+int qlcnic_sriov_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       int i, num_vfs = sriov->num_vfs;
+       struct qlcnic_vf_info *vf_info;
+       u8 *curr_mac;
+
+       if (!qlcnic_sriov_pf_check(adapter))
+               return -EOPNOTSUPP;
+
+       if (!is_valid_ether_addr(mac) || vf >= num_vfs)
+               return -EINVAL;
+
+       if (!compare_ether_addr(adapter->mac_addr, mac)) {
+               netdev_err(netdev, "MAC address is already in use by the PF\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < num_vfs; i++) {
+               vf_info = &sriov->vf_info[i];
+               if (!compare_ether_addr(vf_info->vp->mac, mac)) {
+                       netdev_err(netdev,
+                                  "MAC address is already in use by VF %d\n",
+                                  i);
+                       return -EINVAL;
+               }
+       }
+
+       vf_info = &sriov->vf_info[vf];
+       curr_mac = vf_info->vp->mac;
+
+       if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) {
+               netdev_err(netdev,
+                          "MAC address change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n",
+                          vf);
+               return -EOPNOTSUPP;
+       }
+
+       memcpy(curr_mac, mac, netdev->addr_len);
+       netdev_info(netdev, "MAC Address %pM  is configured for VF %d\n",
+                   mac, vf);
+       return 0;
+}
+
+int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_vf_info *vf_info;
+       struct qlcnic_info nic_info;
+       struct qlcnic_vport *vp;
+       u16 vpid;
+
+       if (!qlcnic_sriov_pf_check(adapter))
+               return -EOPNOTSUPP;
+
+       if (vf >= sriov->num_vfs)
+               return -EINVAL;
+
+       if (tx_rate >= 10000 || tx_rate < 100) {
+               netdev_err(netdev,
+                          "Invalid Tx rate, allowed range is [%d - %d]",
+                          QLC_VF_MIN_TX_RATE, QLC_VF_MAX_TX_RATE);
+               return -EINVAL;
+       }
+
+       if (tx_rate == 0)
+               tx_rate = 10000;
+
+       vf_info = &sriov->vf_info[vf];
+       vp = vf_info->vp;
+       vpid = vp->handle;
+
+       if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) {
+               if (qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, vpid))
+                       return -EIO;
+
+               nic_info.max_tx_bw = tx_rate / 100;
+               nic_info.bit_offsets = BIT_0;
+
+               if (qlcnic_sriov_pf_set_vport_info(adapter, &nic_info, vpid))
+                       return -EIO;
+       }
+
+       vp->max_tx_bw = tx_rate / 100;
+       netdev_info(netdev,
+                   "Setting Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n",
+                   tx_rate, vp->max_tx_bw, vf);
+       return 0;
+}
+
+int qlcnic_sriov_set_vf_vlan(struct net_device *netdev, int vf,
+                            u16 vlan, u8 qos)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_vf_info *vf_info;
+       struct qlcnic_vport *vp;
+
+       if (!qlcnic_sriov_pf_check(adapter))
+               return -EOPNOTSUPP;
+
+       if (vf >= sriov->num_vfs || qos > 7)
+               return -EINVAL;
+
+       if (vlan > MAX_VLAN_ID) {
+               netdev_err(netdev,
+                          "Invalid VLAN ID, allowed range is [0 - %d]\n",
+                          MAX_VLAN_ID);
+               return -EINVAL;
+       }
+
+       vf_info = &sriov->vf_info[vf];
+       vp = vf_info->vp;
+       if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) {
+               netdev_err(netdev,
+                          "VLAN change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n",
+                          vf);
+               return -EOPNOTSUPP;
+       }
+
+       switch (vlan) {
+       case 4095:
+               vp->vlan_mode = QLC_GUEST_VLAN_MODE;
+               break;
+       case 0:
+               vp->vlan_mode = QLC_NO_VLAN_MODE;
+               vp->vlan = 0;
+               vp->qos = 0;
+               break;
+       default:
+               vp->vlan_mode = QLC_PVID_MODE;
+               vp->vlan = vlan;
+               vp->qos = qos;
+       }
+
+       netdev_info(netdev, "Setting VLAN %d, QoS %d, for VF %d\n",
+                   vlan, qos, vf);
+       return 0;
+}
+
+int qlcnic_sriov_get_vf_config(struct net_device *netdev,
+                              int vf, struct ifla_vf_info *ivi)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_vport *vp;
+
+       if (!qlcnic_sriov_pf_check(adapter))
+               return -EOPNOTSUPP;
+
+       if (vf >= sriov->num_vfs)
+               return -EINVAL;
+
+       vp = sriov->vf_info[vf].vp;
+       memcpy(&ivi->mac, vp->mac, ETH_ALEN);
+       ivi->vlan = vp->vlan;
+       ivi->qos = vp->qos;
+       if (vp->max_tx_bw == MAX_BW)
+               ivi->tx_rate = 0;
+       else
+               ivi->tx_rate = vp->max_tx_bw * 100;
+
+       ivi->vf = vf;
+       return 0;
+}
index 5ef328af61d0ab9fcbc1c098b323f444fbd63854..4e22e794a1863ea1abec985d91abe154afcf8b49 100644 (file)
@@ -21,8 +21,6 @@
 #include <linux/aer.h>
 #include <linux/log2.h>
 
-#include <linux/sysfs.h>
-
 #define QLC_STATUS_UNSUPPORTED_CMD     -2
 
 int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable)
@@ -886,6 +884,244 @@ static ssize_t qlcnic_sysfs_read_pci_config(struct file *file,
        return size;
 }
 
+static ssize_t qlcnic_83xx_sysfs_flash_read_handler(struct file *filp,
+                                                   struct kobject *kobj,
+                                                   struct bin_attribute *attr,
+                                                   char *buf, loff_t offset,
+                                                   size_t size)
+{
+       unsigned char *p_read_buf;
+       int  ret, count;
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+
+       if (!size)
+               return QL_STATUS_INVALID_PARAM;
+       if (!buf)
+               return QL_STATUS_INVALID_PARAM;
+
+       count = size / sizeof(u32);
+
+       if (size % sizeof(u32))
+               count++;
+
+       p_read_buf = kcalloc(size, sizeof(unsigned char), GFP_KERNEL);
+       if (!p_read_buf)
+               return -ENOMEM;
+       if (qlcnic_83xx_lock_flash(adapter) != 0) {
+               kfree(p_read_buf);
+               return -EIO;
+       }
+
+       ret = qlcnic_83xx_lockless_flash_read32(adapter, offset, p_read_buf,
+                                               count);
+
+       if (ret) {
+               qlcnic_83xx_unlock_flash(adapter);
+               kfree(p_read_buf);
+               return ret;
+       }
+
+       qlcnic_83xx_unlock_flash(adapter);
+       memcpy(buf, p_read_buf, size);
+       kfree(p_read_buf);
+
+       return size;
+}
+
+static int qlcnic_83xx_sysfs_flash_bulk_write(struct qlcnic_adapter *adapter,
+                                             char *buf, loff_t offset,
+                                             size_t size)
+{
+       int  i, ret, count;
+       unsigned char *p_cache, *p_src;
+
+       p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL);
+       if (!p_cache)
+               return -ENOMEM;
+
+       memcpy(p_cache, buf, size);
+       p_src = p_cache;
+       count = size / sizeof(u32);
+
+       if (qlcnic_83xx_lock_flash(adapter) != 0) {
+               kfree(p_cache);
+               return -EIO;
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_enable_flash_write(adapter);
+               if (ret) {
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+       }
+
+       for (i = 0; i < count / QLC_83XX_FLASH_WRITE_MAX; i++) {
+               ret = qlcnic_83xx_flash_bulk_write(adapter, offset,
+                                                  (u32 *)p_src,
+                                                  QLC_83XX_FLASH_WRITE_MAX);
+
+               if (ret) {
+                       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+                               ret = qlcnic_83xx_disable_flash_write(adapter);
+                               if (ret) {
+                                       kfree(p_cache);
+                                       qlcnic_83xx_unlock_flash(adapter);
+                                       return -EIO;
+                               }
+                       }
+
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+
+               p_src = p_src + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX;
+               offset = offset + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX;
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_disable_flash_write(adapter);
+               if (ret) {
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+       }
+
+       kfree(p_cache);
+       qlcnic_83xx_unlock_flash(adapter);
+
+       return 0;
+}
+
+static int qlcnic_83xx_sysfs_flash_write(struct qlcnic_adapter *adapter,
+                                        char *buf, loff_t offset, size_t size)
+{
+       int  i, ret, count;
+       unsigned char *p_cache, *p_src;
+
+       p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL);
+       if (!p_cache)
+               return -ENOMEM;
+
+       memcpy(p_cache, buf, size);
+       p_src = p_cache;
+       count = size / sizeof(u32);
+
+       if (qlcnic_83xx_lock_flash(adapter) != 0) {
+               kfree(p_cache);
+               return -EIO;
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_enable_flash_write(adapter);
+               if (ret) {
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               ret = qlcnic_83xx_flash_write32(adapter, offset, (u32 *)p_src);
+               if (ret) {
+                       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+                               ret = qlcnic_83xx_disable_flash_write(adapter);
+                               if (ret) {
+                                       kfree(p_cache);
+                                       qlcnic_83xx_unlock_flash(adapter);
+                                       return -EIO;
+                               }
+                       }
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+
+               p_src = p_src + sizeof(u32);
+               offset = offset + sizeof(u32);
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_disable_flash_write(adapter);
+               if (ret) {
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+       }
+
+       kfree(p_cache);
+       qlcnic_83xx_unlock_flash(adapter);
+
+       return 0;
+}
+
+static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp,
+                                                    struct kobject *kobj,
+                                                    struct bin_attribute *attr,
+                                                    char *buf, loff_t offset,
+                                                    size_t size)
+{
+       int  ret;
+       static int flash_mode;
+       unsigned long data;
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+
+       if (!buf)
+               return QL_STATUS_INVALID_PARAM;
+
+       ret = kstrtoul(buf, 16, &data);
+
+       switch (data) {
+       case QLC_83XX_FLASH_SECTOR_ERASE_CMD:
+               flash_mode = QLC_83XX_ERASE_MODE;
+               ret = qlcnic_83xx_erase_flash_sector(adapter, offset);
+               if (ret) {
+                       dev_err(&adapter->pdev->dev,
+                               "%s failed at %d\n", __func__, __LINE__);
+                       return -EIO;
+               }
+               break;
+
+       case QLC_83XX_FLASH_BULK_WRITE_CMD:
+               flash_mode = QLC_83XX_BULK_WRITE_MODE;
+               break;
+
+       case QLC_83XX_FLASH_WRITE_CMD:
+               flash_mode = QLC_83XX_WRITE_MODE;
+               break;
+       default:
+               if (flash_mode == QLC_83XX_BULK_WRITE_MODE) {
+                       ret = qlcnic_83xx_sysfs_flash_bulk_write(adapter, buf,
+                                                                offset, size);
+                       if (ret) {
+                               dev_err(&adapter->pdev->dev,
+                                       "%s failed at %d\n",
+                                       __func__, __LINE__);
+                               return -EIO;
+                       }
+               }
+
+               if (flash_mode == QLC_83XX_WRITE_MODE) {
+                       ret = qlcnic_83xx_sysfs_flash_write(adapter, buf,
+                                                           offset, size);
+                       if (ret) {
+                               dev_err(&adapter->pdev->dev,
+                                       "%s failed at %d\n", __func__,
+                                       __LINE__);
+                               return -EIO;
+                       }
+               }
+       }
+
+       return size;
+}
+
 static struct device_attribute dev_attr_bridged_mode = {
        .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)},
        .show = qlcnic_show_bridged_mode,
@@ -960,6 +1196,13 @@ static struct bin_attribute bin_attr_pm_config = {
        .write = qlcnic_sysfs_write_pm_config,
 };
 
+static struct bin_attribute bin_attr_flash = {
+       .attr = {.name = "flash", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = qlcnic_83xx_sysfs_flash_read_handler,
+       .write = qlcnic_83xx_sysfs_flash_write_handler,
+};
+
 void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter)
 {
        struct device *dev = &adapter->pdev->dev;
@@ -1048,10 +1291,18 @@ void qlcnic_82xx_remove_sysfs(struct qlcnic_adapter *adapter)
 
 void qlcnic_83xx_add_sysfs(struct qlcnic_adapter *adapter)
 {
+       struct device *dev = &adapter->pdev->dev;
+
        qlcnic_create_diag_entries(adapter);
+
+       if (sysfs_create_bin_file(&dev->kobj, &bin_attr_flash))
+               dev_info(dev, "failed to create flash sysfs entry\n");
 }
 
 void qlcnic_83xx_remove_sysfs(struct qlcnic_adapter *adapter)
 {
+       struct device *dev = &adapter->pdev->dev;
+
        qlcnic_remove_diag_entries(adapter);
+       sysfs_remove_bin_file(&dev->kobj, &bin_attr_flash);
 }
index 8033555e53c2f6f524211b4d848dcc13a0e14f78..87463bc701a653781371feb05599aad2b45d224a 100644 (file)
@@ -409,7 +409,7 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type,
                                      (qdev->
                                       func << CAM_OUT_FUNC_SHIFT) |
                                        (0 << CAM_OUT_CQ_ID_SHIFT));
-                       if (qdev->ndev->features & NETIF_F_HW_VLAN_RX)
+                       if (qdev->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)
                                cam_output |= CAM_OUT_RV;
                        /* route to NIC core */
                        ql_write32(qdev, MAC_ADDR_DATA, cam_output);
@@ -1211,8 +1211,6 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
                                    netdev_alloc_skb(qdev->ndev,
                                                     SMALL_BUFFER_SIZE);
                                if (sbq_desc->p.skb == NULL) {
-                                       netif_err(qdev, probe, qdev->ndev,
-                                                 "Couldn't get an skb.\n");
                                        rx_ring->sbq_clean_idx = clean_idx;
                                        return;
                                }
@@ -1508,7 +1506,7 @@ static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev,
        skb->ip_summed = CHECKSUM_UNNECESSARY;
        skb_record_rx_queue(skb, rx_ring->cq_id);
        if (vlan_id != 0xffff)
-               __vlan_hwaccel_put_tag(skb, vlan_id);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);
        napi_gro_frags(napi);
 }
 
@@ -1527,8 +1525,6 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev,
 
        skb = netdev_alloc_skb(ndev, length);
        if (!skb) {
-               netif_err(qdev, drv, qdev->ndev,
-                         "Couldn't get an skb, need to unwind!.\n");
                rx_ring->rx_dropped++;
                put_page(lbq_desc->p.pg_chunk.page);
                return;
@@ -1592,7 +1588,7 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev,
 
        skb_record_rx_queue(skb, rx_ring->cq_id);
        if (vlan_id != 0xffff)
-               __vlan_hwaccel_put_tag(skb, vlan_id);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);
        if (skb->ip_summed == CHECKSUM_UNNECESSARY)
                napi_gro_receive(napi, skb);
        else
@@ -1619,8 +1615,6 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev,
        /* Allocate new_skb and copy */
        new_skb = netdev_alloc_skb(qdev->ndev, length + NET_IP_ALIGN);
        if (new_skb == NULL) {
-               netif_err(qdev, probe, qdev->ndev,
-                         "No skb available, drop the packet.\n");
                rx_ring->rx_dropped++;
                return;
        }
@@ -1697,7 +1691,7 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev,
 
        skb_record_rx_queue(skb, rx_ring->cq_id);
        if (vlan_id != 0xffff)
-               __vlan_hwaccel_put_tag(skb, vlan_id);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);
        if (skb->ip_summed == CHECKSUM_UNNECESSARY)
                napi_gro_receive(&rx_ring->napi, skb);
        else
@@ -2009,7 +2003,7 @@ static void ql_process_mac_split_rx_intr(struct ql_adapter *qdev,
        rx_ring->rx_bytes += skb->len;
        skb_record_rx_queue(skb, rx_ring->cq_id);
        if ((ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) && (vlan_id != 0))
-               __vlan_hwaccel_put_tag(skb, vlan_id);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);
        if (skb->ip_summed == CHECKSUM_UNNECESSARY)
                napi_gro_receive(&rx_ring->napi, skb);
        else
@@ -2307,7 +2301,7 @@ static void qlge_vlan_mode(struct net_device *ndev, netdev_features_t features)
 {
        struct ql_adapter *qdev = netdev_priv(ndev);
 
-       if (features & NETIF_F_HW_VLAN_RX) {
+       if (features & NETIF_F_HW_VLAN_CTAG_RX) {
                ql_write32(qdev, NIC_RCV_CFG, NIC_RCV_CFG_VLAN_MASK |
                                 NIC_RCV_CFG_VLAN_MATCH_AND_NON);
        } else {
@@ -2322,10 +2316,10 @@ static netdev_features_t qlge_fix_features(struct net_device *ndev,
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
         */
-       if (features & NETIF_F_HW_VLAN_RX)
-               features |= NETIF_F_HW_VLAN_TX;
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
        else
-               features &= ~NETIF_F_HW_VLAN_TX;
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
        return features;
 }
@@ -2335,7 +2329,7 @@ static int qlge_set_features(struct net_device *ndev,
 {
        netdev_features_t changed = ndev->features ^ features;
 
-       if (changed & NETIF_F_HW_VLAN_RX)
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                qlge_vlan_mode(ndev, features);
 
        return 0;
@@ -2354,7 +2348,7 @@ static int __qlge_vlan_rx_add_vid(struct ql_adapter *qdev, u16 vid)
        return err;
 }
 
-static int qlge_vlan_rx_add_vid(struct net_device *ndev, u16 vid)
+static int qlge_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)
 {
        struct ql_adapter *qdev = netdev_priv(ndev);
        int status;
@@ -2385,7 +2379,7 @@ static int __qlge_vlan_rx_kill_vid(struct ql_adapter *qdev, u16 vid)
        return err;
 }
 
-static int qlge_vlan_rx_kill_vid(struct net_device *ndev, u16 vid)
+static int qlge_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid)
 {
        struct ql_adapter *qdev = netdev_priv(ndev);
        int status;
@@ -4693,9 +4687,9 @@ static int qlge_probe(struct pci_dev *pdev,
        SET_NETDEV_DEV(ndev, &pdev->dev);
        ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
                NETIF_F_TSO | NETIF_F_TSO_ECN |
-               NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM;
+               NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_RXCSUM;
        ndev->features = ndev->hw_features |
-               NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
+               NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
        ndev->vlan_features = ndev->hw_features;
 
        if (test_bit(QL_DMA64, &qdev->flags))
index 5b4103db70f5c25025241672c19e48be6c583eaa..e9dc84943cfcb5fa39a8031e5a28051f5963da90 100644 (file)
@@ -224,11 +224,14 @@ static int r6040_phy_read(void __iomem *ioaddr, int phy_addr, int reg)
                        break;
        }
 
+       if (limit < 0)
+               return -ETIMEDOUT;
+
        return ioread16(ioaddr + MMRD);
 }
 
 /* Write a word data from PHY Chip */
-static void r6040_phy_write(void __iomem *ioaddr,
+static int r6040_phy_write(void __iomem *ioaddr,
                                        int phy_addr, int reg, u16 val)
 {
        int limit = MAC_DEF_TIMEOUT;
@@ -243,6 +246,8 @@ static void r6040_phy_write(void __iomem *ioaddr,
                if (!(cmd & MDIO_WRITE))
                        break;
        }
+
+       return (limit < 0) ? -ETIMEDOUT : 0;
 }
 
 static int r6040_mdiobus_read(struct mii_bus *bus, int phy_addr, int reg)
@@ -261,9 +266,7 @@ static int r6040_mdiobus_write(struct mii_bus *bus, int phy_addr,
        struct r6040_private *lp = netdev_priv(dev);
        void __iomem *ioaddr = lp->base;
 
-       r6040_phy_write(ioaddr, phy_addr, reg, value);
-
-       return 0;
+       return r6040_phy_write(ioaddr, phy_addr, reg, value);
 }
 
 static int r6040_mdiobus_reset(struct mii_bus *bus)
@@ -347,7 +350,6 @@ static int r6040_alloc_rxbufs(struct net_device *dev)
        do {
                skb = netdev_alloc_skb(dev, MAX_BUF_SIZE);
                if (!skb) {
-                       netdev_err(dev, "failed to alloc skb for rx\n");
                        rc = -ENOMEM;
                        goto err_exit;
                }
index b62a32484f6a629874413e045eb364b75d38d596..7d1fb9ad1296e5825da2d845129ef38b51728cd7 100644 (file)
@@ -431,7 +431,7 @@ static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb,
        cp->dev->stats.rx_bytes += skb->len;
 
        if (opts2 & RxVlanTagged)
-               __vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff));
 
        napi_gro_receive(&cp->napi, skb);
 }
@@ -1438,7 +1438,7 @@ static int cp_set_features(struct net_device *dev, netdev_features_t features)
        else
                cp->cpcmd &= ~RxChkSum;
 
-       if (features & NETIF_F_HW_VLAN_RX)
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
                cp->cpcmd |= RxVlanOn;
        else
                cp->cpcmd &= ~RxVlanOn;
@@ -1955,14 +1955,14 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->ethtool_ops = &cp_ethtool_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
-       dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 
        if (pci_using_dac)
                dev->features |= NETIF_F_HIGHDMA;
 
        /* disabled by default until verified */
        dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
-               NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+               NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
        dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
                NETIF_F_HIGHDMA;
 
index 1276ac71353ae1c85d74da90ef83e2f2c31b9d8b..3ccedeb8aba03703bcdff318dff2ff888eec4bfa 100644 (file)
@@ -2041,8 +2041,6 @@ keep_pkt:
 
                        netif_receive_skb (skb);
                } else {
-                       if (net_ratelimit())
-                               netdev_warn(dev, "Memory squeeze, dropping packet\n");
                        dev->stats.rx_dropped++;
                }
                received++;
index 9f2d416de750882485ec5b4c5a60f59c2a55e98d..d77d60ea820255cd41bc6d37606dfdcd7c4f0395 100644 (file)
@@ -782,8 +782,6 @@ static void net_rx(struct net_device *dev)
 
                skb = netdev_alloc_skb(dev, pkt_len + 2);
                if (skb == NULL) {
-                       printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n",
-                                  dev->name);
                        dev->stats.rx_dropped++;
                        goto done;
                }
index 4ecbe64a758d8b05dd5a016a9659b20bfdfb90c5..c6dac38fd9ccf0a56a8cfa45316e0b2c020659d5 100644 (file)
@@ -47,7 +47,9 @@
 #define FIRMWARE_8402_1                "rtl_nic/rtl8402-1.fw"
 #define FIRMWARE_8411_1                "rtl_nic/rtl8411-1.fw"
 #define FIRMWARE_8106E_1       "rtl_nic/rtl8106e-1.fw"
-#define FIRMWARE_8168G_1       "rtl_nic/rtl8168g-1.fw"
+#define FIRMWARE_8106E_2       "rtl_nic/rtl8106e-2.fw"
+#define FIRMWARE_8168G_2       "rtl_nic/rtl8168g-2.fw"
+#define FIRMWARE_8168G_3       "rtl_nic/rtl8168g-3.fw"
 
 #ifdef RTL8169_DEBUG
 #define assert(expr) \
@@ -140,6 +142,8 @@ enum mac_version {
        RTL_GIGA_MAC_VER_39,
        RTL_GIGA_MAC_VER_40,
        RTL_GIGA_MAC_VER_41,
+       RTL_GIGA_MAC_VER_42,
+       RTL_GIGA_MAC_VER_43,
        RTL_GIGA_MAC_NONE   = 0xff,
 };
 
@@ -262,10 +266,16 @@ static const struct {
                _R("RTL8106e",          RTL_TD_1, FIRMWARE_8106E_1,
                                                        JUMBO_1K, true),
        [RTL_GIGA_MAC_VER_40] =
-               _R("RTL8168g/8111g",    RTL_TD_1, FIRMWARE_8168G_1,
+               _R("RTL8168g/8111g",    RTL_TD_1, FIRMWARE_8168G_2,
                                                        JUMBO_9K, false),
        [RTL_GIGA_MAC_VER_41] =
                _R("RTL8168g/8111g",    RTL_TD_1, NULL, JUMBO_9K, false),
+       [RTL_GIGA_MAC_VER_42] =
+               _R("RTL8168g/8111g",    RTL_TD_1, FIRMWARE_8168G_3,
+                                                       JUMBO_9K, false),
+       [RTL_GIGA_MAC_VER_43] =
+               _R("RTL8106e",          RTL_TD_1, FIRMWARE_8106E_2,
+                                                       JUMBO_1K, true),
 };
 #undef _R
 
@@ -329,6 +339,7 @@ enum rtl_registers {
 #define        RXCFG_FIFO_SHIFT                13
                                        /* No threshold before first PCI xfer */
 #define        RX_FIFO_THRESH                  (7 << RXCFG_FIFO_SHIFT)
+#define        RX_EARLY_OFF                    (1 << 11)
 #define        RXCFG_DMA_SHIFT                 8
                                        /* Unlimited maximum PCI burst. */
 #define        RX_DMA_BURST                    (7 << RXCFG_DMA_SHIFT)
@@ -513,6 +524,7 @@ enum rtl_register_content {
        PMEnable        = (1 << 0),     /* Power Management Enable */
 
        /* Config2 register p. 25 */
+       ClkReqEn        = (1 << 7),     /* Clock Request Enable */
        MSIEnable       = (1 << 5),     /* 8169 only. Reserved in the 8168. */
        PCI_Clock_66MHz = 0x01,
        PCI_Clock_33MHz = 0x00,
@@ -533,6 +545,7 @@ enum rtl_register_content {
        Spi_en          = (1 << 3),
        LanWake         = (1 << 1),     /* LanWake enable/disable */
        PMEStatus       = (1 << 0),     /* PME status can be reset by PCI RST# */
+       ASPM_en         = (1 << 0),     /* ASPM enable */
 
        /* TBICSR p.28 */
        TBIReset        = 0x80000000,
@@ -814,7 +827,9 @@ MODULE_FIRMWARE(FIRMWARE_8168F_2);
 MODULE_FIRMWARE(FIRMWARE_8402_1);
 MODULE_FIRMWARE(FIRMWARE_8411_1);
 MODULE_FIRMWARE(FIRMWARE_8106E_1);
-MODULE_FIRMWARE(FIRMWARE_8168G_1);
+MODULE_FIRMWARE(FIRMWARE_8106E_2);
+MODULE_FIRMWARE(FIRMWARE_8168G_2);
+MODULE_FIRMWARE(FIRMWARE_8168G_3);
 
 static void rtl_lock_work(struct rtl8169_private *tp)
 {
@@ -1024,14 +1039,6 @@ static u16 r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
                (RTL_R32(GPHY_OCP) & 0xffff) : ~0;
 }
 
-static void rtl_w1w0_phy_ocp(struct rtl8169_private *tp, int reg, int p, int m)
-{
-       int val;
-
-       val = r8168_phy_ocp_read(tp, reg);
-       r8168_phy_ocp_write(tp, reg, (val | p) & ~m);
-}
-
 static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
 {
        void __iomem *ioaddr = tp->mmio_addr;
@@ -1077,6 +1084,21 @@ static int r8168g_mdio_read(struct rtl8169_private *tp, int reg)
        return r8168_phy_ocp_read(tp, tp->ocp_base + reg * 2);
 }
 
+static void mac_mcu_write(struct rtl8169_private *tp, int reg, int value)
+{
+       if (reg == 0x1f) {
+               tp->ocp_base = value << 4;
+               return;
+       }
+
+       r8168_mac_ocp_write(tp, tp->ocp_base + reg, value);
+}
+
+static int mac_mcu_read(struct rtl8169_private *tp, int reg)
+{
+       return r8168_mac_ocp_read(tp, tp->ocp_base + reg);
+}
+
 DECLARE_RTL_COND(rtl_phyar_cond)
 {
        void __iomem *ioaddr = tp->mmio_addr;
@@ -1771,16 +1793,17 @@ static void __rtl8169_set_features(struct net_device *dev,
        netdev_features_t changed = features ^ dev->features;
        void __iomem *ioaddr = tp->mmio_addr;
 
-       if (!(changed & (NETIF_F_RXALL | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX)))
+       if (!(changed & (NETIF_F_RXALL | NETIF_F_RXCSUM |
+                        NETIF_F_HW_VLAN_CTAG_RX)))
                return;
 
-       if (changed & (NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX)) {
+       if (changed & (NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX)) {
                if (features & NETIF_F_RXCSUM)
                        tp->cp_cmd |= RxChkSum;
                else
                        tp->cp_cmd &= ~RxChkSum;
 
-               if (dev->features & NETIF_F_HW_VLAN_RX)
+               if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
                        tp->cp_cmd |= RxVlan;
                else
                        tp->cp_cmd &= ~RxVlan;
@@ -1820,7 +1843,7 @@ static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb)
        u32 opts2 = le32_to_cpu(desc->opts2);
 
        if (opts2 & RxVlanTag)
-               __vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff));
 }
 
 static int rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd)
@@ -2028,6 +2051,7 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
                int mac_version;
        } mac_info[] = {
                /* 8168G family. */
+               { 0x7cf00000, 0x50900000,       RTL_GIGA_MAC_VER_42 },
                { 0x7cf00000, 0x4c100000,       RTL_GIGA_MAC_VER_41 },
                { 0x7cf00000, 0x4c000000,       RTL_GIGA_MAC_VER_40 },
 
@@ -2116,6 +2140,10 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
                netif_notice(tp, probe, dev,
                             "unknown MAC, using family default\n");
                tp->mac_version = default_version;
+       } else if (tp->mac_version == RTL_GIGA_MAC_VER_42) {
+               tp->mac_version = tp->mii.supports_gmii ?
+                                 RTL_GIGA_MAC_VER_42 :
+                                 RTL_GIGA_MAC_VER_43;
        }
 }
 
@@ -2142,9 +2170,7 @@ static void rtl_writephy_batch(struct rtl8169_private *tp,
 #define PHY_DATA_OR            0x10000000
 #define PHY_DATA_AND           0x20000000
 #define PHY_BJMPN              0x30000000
-#define PHY_READ_EFUSE         0x40000000
-#define PHY_READ_MAC_BYTE      0x50000000
-#define PHY_WRITE_MAC_BYTE     0x60000000
+#define PHY_MDIO_CHG           0x40000000
 #define PHY_CLEAR_READCOUNT    0x70000000
 #define PHY_WRITE              0x80000000
 #define PHY_READCOUNT_EQ_SKIP  0x90000000
@@ -2153,7 +2179,6 @@ static void rtl_writephy_batch(struct rtl8169_private *tp,
 #define PHY_WRITE_PREVIOUS     0xc0000000
 #define PHY_SKIPN              0xd0000000
 #define PHY_DELAY_MS           0xe0000000
-#define PHY_WRITE_ERI_WORD     0xf0000000
 
 struct fw_info {
        u32     magic;
@@ -2230,7 +2255,7 @@ static bool rtl_fw_data_ok(struct rtl8169_private *tp, struct net_device *dev,
                case PHY_READ:
                case PHY_DATA_OR:
                case PHY_DATA_AND:
-               case PHY_READ_EFUSE:
+               case PHY_MDIO_CHG:
                case PHY_CLEAR_READCOUNT:
                case PHY_WRITE:
                case PHY_WRITE_PREVIOUS:
@@ -2261,9 +2286,6 @@ static bool rtl_fw_data_ok(struct rtl8169_private *tp, struct net_device *dev,
                        }
                        break;
 
-               case PHY_READ_MAC_BYTE:
-               case PHY_WRITE_MAC_BYTE:
-               case PHY_WRITE_ERI_WORD:
                default:
                        netif_err(tp, ifup, tp->dev,
                                  "Invalid action 0x%08x\n", action);
@@ -2294,10 +2316,13 @@ out:
 static void rtl_phy_write_fw(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
 {
        struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
+       struct mdio_ops org, *ops = &tp->mdio_ops;
        u32 predata, count;
        size_t index;
 
        predata = count = 0;
+       org.write = ops->write;
+       org.read = ops->read;
 
        for (index = 0; index < pa->size; ) {
                u32 action = le32_to_cpu(pa->code[index]);
@@ -2324,8 +2349,15 @@ static void rtl_phy_write_fw(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
                case PHY_BJMPN:
                        index -= regno;
                        break;
-               case PHY_READ_EFUSE:
-                       predata = rtl8168d_efuse_read(tp, regno);
+               case PHY_MDIO_CHG:
+                       if (data == 0) {
+                               ops->write = org.write;
+                               ops->read = org.read;
+                       } else if (data == 1) {
+                               ops->write = mac_mcu_write;
+                               ops->read = mac_mcu_read;
+                       }
+
                        index++;
                        break;
                case PHY_CLEAR_READCOUNT:
@@ -2361,13 +2393,13 @@ static void rtl_phy_write_fw(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
                        index++;
                        break;
 
-               case PHY_READ_MAC_BYTE:
-               case PHY_WRITE_MAC_BYTE:
-               case PHY_WRITE_ERI_WORD:
                default:
                        BUG();
                }
        }
+
+       ops->write = org.write;
+       ops->read = org.read;
 }
 
 static void rtl_release_firmware(struct rtl8169_private *tp)
@@ -3368,51 +3400,68 @@ static void rtl8411_hw_phy_config(struct rtl8169_private *tp)
 
 static void rtl8168g_1_hw_phy_config(struct rtl8169_private *tp)
 {
-       static const u16 mac_ocp_patch[] = {
-               0xe008, 0xe01b, 0xe01d, 0xe01f,
-               0xe021, 0xe023, 0xe025, 0xe027,
-               0x49d2, 0xf10d, 0x766c, 0x49e2,
-               0xf00a, 0x1ec0, 0x8ee1, 0xc60a,
-
-               0x77c0, 0x4870, 0x9fc0, 0x1ea0,
-               0xc707, 0x8ee1, 0x9d6c, 0xc603,
-               0xbe00, 0xb416, 0x0076, 0xe86c,
-               0xc602, 0xbe00, 0x0000, 0xc602,
-
-               0xbe00, 0x0000, 0xc602, 0xbe00,
-               0x0000, 0xc602, 0xbe00, 0x0000,
-               0xc602, 0xbe00, 0x0000, 0xc602,
-               0xbe00, 0x0000, 0xc602, 0xbe00,
-
-               0x0000, 0x0000, 0x0000, 0x0000
-       };
-       u32 i;
+       rtl_apply_firmware(tp);
 
-       /* Patch code for GPHY reset */
-       for (i = 0; i < ARRAY_SIZE(mac_ocp_patch); i++)
-               r8168_mac_ocp_write(tp, 0xf800 + 2*i, mac_ocp_patch[i]);
-       r8168_mac_ocp_write(tp, 0xfc26, 0x8000);
-       r8168_mac_ocp_write(tp, 0xfc28, 0x0075);
+       rtl_writephy(tp, 0x1f, 0x0a46);
+       if (rtl_readphy(tp, 0x10) & 0x0100) {
+               rtl_writephy(tp, 0x1f, 0x0bcc);
+               rtl_w1w0_phy(tp, 0x12, 0x0000, 0x8000);
+       } else {
+               rtl_writephy(tp, 0x1f, 0x0bcc);
+               rtl_w1w0_phy(tp, 0x12, 0x8000, 0x0000);
+       }
 
-       rtl_apply_firmware(tp);
+       rtl_writephy(tp, 0x1f, 0x0a46);
+       if (rtl_readphy(tp, 0x13) & 0x0100) {
+               rtl_writephy(tp, 0x1f, 0x0c41);
+               rtl_w1w0_phy(tp, 0x15, 0x0002, 0x0000);
+       } else {
+               rtl_writephy(tp, 0x1f, 0x0c41);
+               rtl_w1w0_phy(tp, 0x15, 0x0000, 0x0002);
+       }
 
-       if (r8168_phy_ocp_read(tp, 0xa460) & 0x0100)
-               rtl_w1w0_phy_ocp(tp, 0xbcc4, 0x0000, 0x8000);
-       else
-               rtl_w1w0_phy_ocp(tp, 0xbcc4, 0x8000, 0x0000);
+       /* Enable PHY auto speed down */
+       rtl_writephy(tp, 0x1f, 0x0a44);
+       rtl_w1w0_phy(tp, 0x11, 0x000c, 0x0000);
+
+       rtl_writephy(tp, 0x1f, 0x0bcc);
+       rtl_w1w0_phy(tp, 0x14, 0x0100, 0x0000);
+       rtl_writephy(tp, 0x1f, 0x0a44);
+       rtl_w1w0_phy(tp, 0x11, 0x00c0, 0x0000);
+       rtl_writephy(tp, 0x1f, 0x0a43);
+       rtl_writephy(tp, 0x13, 0x8084);
+       rtl_w1w0_phy(tp, 0x14, 0x0000, 0x6000);
+       rtl_w1w0_phy(tp, 0x10, 0x1003, 0x0000);
+
+       /* EEE auto-fallback function */
+       rtl_writephy(tp, 0x1f, 0x0a4b);
+       rtl_w1w0_phy(tp, 0x11, 0x0004, 0x0000);
+
+       /* Enable UC LPF tune function */
+       rtl_writephy(tp, 0x1f, 0x0a43);
+       rtl_writephy(tp, 0x13, 0x8012);
+       rtl_w1w0_phy(tp, 0x14, 0x8000, 0x0000);
 
-       if (r8168_phy_ocp_read(tp, 0xa466) & 0x0100)
-               rtl_w1w0_phy_ocp(tp, 0xc41a, 0x0002, 0x0000);
-       else
-               rtl_w1w0_phy_ocp(tp, 0xbcc4, 0x0000, 0x0002);
+       rtl_writephy(tp, 0x1f, 0x0c42);
+       rtl_w1w0_phy(tp, 0x11, 0x4000, 0x2000);
 
-       rtl_w1w0_phy_ocp(tp, 0xa442, 0x000c, 0x0000);
-       rtl_w1w0_phy_ocp(tp, 0xa4b2, 0x0004, 0x0000);
+       /* Improve SWR Efficiency */
+       rtl_writephy(tp, 0x1f, 0x0bcd);
+       rtl_writephy(tp, 0x14, 0x5065);
+       rtl_writephy(tp, 0x14, 0xd065);
+       rtl_writephy(tp, 0x1f, 0x0bc8);
+       rtl_writephy(tp, 0x11, 0x5655);
+       rtl_writephy(tp, 0x1f, 0x0bcd);
+       rtl_writephy(tp, 0x14, 0x1065);
+       rtl_writephy(tp, 0x14, 0x9065);
+       rtl_writephy(tp, 0x14, 0x1065);
 
-       r8168_phy_ocp_write(tp, 0xa436, 0x8012);
-       rtl_w1w0_phy_ocp(tp, 0xa438, 0x8000, 0x0000);
+       rtl_writephy(tp, 0x1f, 0x0000);
+}
 
-       rtl_w1w0_phy_ocp(tp, 0xc422, 0x4000, 0x2000);
+static void rtl8168g_2_hw_phy_config(struct rtl8169_private *tp)
+{
+       rtl_apply_firmware(tp);
 }
 
 static void rtl8102e_hw_phy_config(struct rtl8169_private *tp)
@@ -3600,6 +3649,10 @@ static void rtl_hw_phy_config(struct net_device *dev)
        case RTL_GIGA_MAC_VER_40:
                rtl8168g_1_hw_phy_config(tp);
                break;
+       case RTL_GIGA_MAC_VER_42:
+       case RTL_GIGA_MAC_VER_43:
+               rtl8168g_2_hw_phy_config(tp);
+               break;
 
        case RTL_GIGA_MAC_VER_41:
        default:
@@ -3808,6 +3861,8 @@ static void rtl_init_mdio_ops(struct rtl8169_private *tp)
                break;
        case RTL_GIGA_MAC_VER_40:
        case RTL_GIGA_MAC_VER_41:
+       case RTL_GIGA_MAC_VER_42:
+       case RTL_GIGA_MAC_VER_43:
                ops->write      = r8168g_mdio_write;
                ops->read       = r8168g_mdio_read;
                break;
@@ -3859,6 +3914,8 @@ static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
        case RTL_GIGA_MAC_VER_39:
        case RTL_GIGA_MAC_VER_40:
        case RTL_GIGA_MAC_VER_41:
+       case RTL_GIGA_MAC_VER_42:
+       case RTL_GIGA_MAC_VER_43:
                RTL_W32(RxConfig, RTL_R32(RxConfig) |
                        AcceptBroadcast | AcceptMulticast | AcceptMyPhys);
                break;
@@ -3966,6 +4023,8 @@ static void r8168_phy_power_down(struct rtl8169_private *tp)
        switch (tp->mac_version) {
        case RTL_GIGA_MAC_VER_32:
        case RTL_GIGA_MAC_VER_33:
+       case RTL_GIGA_MAC_VER_40:
+       case RTL_GIGA_MAC_VER_41:
                rtl_writephy(tp, MII_BMCR, BMCR_ANENABLE | BMCR_PDOWN);
                break;
 
@@ -4027,6 +4086,11 @@ static void r8168_pll_power_down(struct rtl8169_private *tp)
        case RTL_GIGA_MAC_VER_33:
                RTL_W8(PMCH, RTL_R8(PMCH) & ~0x80);
                break;
+       case RTL_GIGA_MAC_VER_40:
+       case RTL_GIGA_MAC_VER_41:
+               rtl_w1w0_eri(tp, 0x1a8, ERIAR_MASK_1111, 0x00000000,
+                            0xfc000000, ERIAR_EXGMAC);
+               break;
        }
 }
 
@@ -4044,6 +4108,11 @@ static void r8168_pll_power_up(struct rtl8169_private *tp)
        case RTL_GIGA_MAC_VER_33:
                RTL_W8(PMCH, RTL_R8(PMCH) | 0x80);
                break;
+       case RTL_GIGA_MAC_VER_40:
+       case RTL_GIGA_MAC_VER_41:
+               rtl_w1w0_eri(tp, 0x1a8, ERIAR_MASK_1111, 0xfc000000,
+                            0x00000000, ERIAR_EXGMAC);
+               break;
        }
 
        r8168_phy_power_up(tp);
@@ -4080,6 +4149,7 @@ static void rtl_init_pll_power_ops(struct rtl8169_private *tp)
        case RTL_GIGA_MAC_VER_30:
        case RTL_GIGA_MAC_VER_37:
        case RTL_GIGA_MAC_VER_39:
+       case RTL_GIGA_MAC_VER_43:
                ops->down       = r810x_pll_power_down;
                ops->up         = r810x_pll_power_up;
                break;
@@ -4107,6 +4177,7 @@ static void rtl_init_pll_power_ops(struct rtl8169_private *tp)
        case RTL_GIGA_MAC_VER_38:
        case RTL_GIGA_MAC_VER_40:
        case RTL_GIGA_MAC_VER_41:
+       case RTL_GIGA_MAC_VER_42:
                ops->down       = r8168_pll_power_down;
                ops->up         = r8168_pll_power_up;
                break;
@@ -4149,6 +4220,12 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
        case RTL_GIGA_MAC_VER_34:
                RTL_W32(RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST);
                break;
+       case RTL_GIGA_MAC_VER_40:
+       case RTL_GIGA_MAC_VER_41:
+       case RTL_GIGA_MAC_VER_42:
+       case RTL_GIGA_MAC_VER_43:
+               RTL_W32(RxConfig, RX128_INT_EN | RX_DMA_BURST | RX_EARLY_OFF);
+               break;
        default:
                RTL_W32(RxConfig, RX128_INT_EN | RX_DMA_BURST);
                break;
@@ -4305,6 +4382,8 @@ static void rtl_init_jumbo_ops(struct rtl8169_private *tp)
         */
        case RTL_GIGA_MAC_VER_40:
        case RTL_GIGA_MAC_VER_41:
+       case RTL_GIGA_MAC_VER_42:
+       case RTL_GIGA_MAC_VER_43:
        default:
                ops->disable    = NULL;
                ops->enable     = NULL;
@@ -4412,6 +4491,8 @@ static void rtl8169_hw_reset(struct rtl8169_private *tp)
                   tp->mac_version == RTL_GIGA_MAC_VER_37 ||
                   tp->mac_version == RTL_GIGA_MAC_VER_40 ||
                   tp->mac_version == RTL_GIGA_MAC_VER_41 ||
+                  tp->mac_version == RTL_GIGA_MAC_VER_42 ||
+                  tp->mac_version == RTL_GIGA_MAC_VER_43 ||
                   tp->mac_version == RTL_GIGA_MAC_VER_38) {
                RTL_W8(ChipCmd, RTL_R8(ChipCmd) | StopReq);
                rtl_udelay_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
@@ -5127,6 +5208,8 @@ static void rtl_hw_start_8168g_1(struct rtl8169_private *tp)
        void __iomem *ioaddr = tp->mmio_addr;
        struct pci_dev *pdev = tp->pci_dev;
 
+       RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
+
        rtl_eri_write(tp, 0xc8, ERIAR_MASK_0101, 0x080002, ERIAR_EXGMAC);
        rtl_eri_write(tp, 0xcc, ERIAR_MASK_0001, 0x38, ERIAR_EXGMAC);
        rtl_eri_write(tp, 0xd0, ERIAR_MASK_0001, 0x48, ERIAR_EXGMAC);
@@ -5138,6 +5221,7 @@ static void rtl_hw_start_8168g_1(struct rtl8169_private *tp)
 
        rtl_w1w0_eri(tp, 0xdc, ERIAR_MASK_0001, 0x00, 0x01, ERIAR_EXGMAC);
        rtl_w1w0_eri(tp, 0xdc, ERIAR_MASK_0001, 0x01, 0x00, ERIAR_EXGMAC);
+       rtl_eri_write(tp, 0x2f8, ERIAR_MASK_0011, 0x1d8f, ERIAR_EXGMAC);
 
        RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
        RTL_W32(MISC, RTL_R32(MISC) & ~RXDV_GATED_EN);
@@ -5149,7 +5233,26 @@ static void rtl_hw_start_8168g_1(struct rtl8169_private *tp)
        /* Adjust EEE LED frequency */
        RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
 
-       rtl_w1w0_eri(tp, 0x2fc, ERIAR_MASK_0001, 0x01, 0x02, ERIAR_EXGMAC);
+       rtl_w1w0_eri(tp, 0x2fc, ERIAR_MASK_0001, 0x01, 0x06, ERIAR_EXGMAC);
+       rtl_w1w0_eri(tp, 0x1b0, ERIAR_MASK_0011, 0x0000, 0x1000, ERIAR_EXGMAC);
+}
+
+static void rtl_hw_start_8168g_2(struct rtl8169_private *tp)
+{
+       void __iomem *ioaddr = tp->mmio_addr;
+       static const struct ephy_info e_info_8168g_2[] = {
+               { 0x00, 0x0000, 0x0008 },
+               { 0x0c, 0x3df0, 0x0200 },
+               { 0x19, 0xffff, 0xfc00 },
+               { 0x1e, 0xffff, 0x20eb }
+       };
+
+       rtl_hw_start_8168g_1(tp);
+
+       /* disable aspm and clock request before access ephy */
+       RTL_W8(Config2, RTL_R8(Config2) & ~ClkReqEn);
+       RTL_W8(Config5, RTL_R8(Config5) & ~ASPM_en);
+       rtl_ephy_init(tp, e_info_8168g_2, ARRAY_SIZE(e_info_8168g_2));
 }
 
 static void rtl_hw_start_8168(struct net_device *dev)
@@ -5177,10 +5280,7 @@ static void rtl_hw_start_8168(struct net_device *dev)
 
        rtl_set_rx_tx_desc_registers(tp, ioaddr);
 
-       rtl_set_rx_mode(dev);
-
-       RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) |
-               (InterFrameGap << TxInterFrameGapShift));
+       rtl_set_rx_tx_config_registers(tp);
 
        RTL_R8(IntrMask);
 
@@ -5257,6 +5357,9 @@ static void rtl_hw_start_8168(struct net_device *dev)
        case RTL_GIGA_MAC_VER_41:
                rtl_hw_start_8168g_1(tp);
                break;
+       case RTL_GIGA_MAC_VER_42:
+               rtl_hw_start_8168g_2(tp);
+               break;
 
        default:
                printk(KERN_ERR PFX "%s: unknown chipset (mac_version = %d).\n",
@@ -5264,9 +5367,11 @@ static void rtl_hw_start_8168(struct net_device *dev)
                break;
        }
 
+       RTL_W8(Cfg9346, Cfg9346_Lock);
+
        RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
 
-       RTL_W8(Cfg9346, Cfg9346_Lock);
+       rtl_set_rx_mode(dev);
 
        RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
 }
@@ -5424,6 +5529,17 @@ static void rtl_hw_start_8101(struct net_device *dev)
 
        RTL_W8(Cfg9346, Cfg9346_Unlock);
 
+       RTL_W8(MaxTxPacketSize, TxPacketMax);
+
+       rtl_set_rx_max_size(ioaddr, rx_buf_sz);
+
+       tp->cp_cmd &= ~R810X_CPCMD_QUIRK_MASK;
+       RTL_W16(CPlusCmd, tp->cp_cmd);
+
+       rtl_set_rx_tx_desc_registers(tp, ioaddr);
+
+       rtl_set_rx_tx_config_registers(tp);
+
        switch (tp->mac_version) {
        case RTL_GIGA_MAC_VER_07:
                rtl_hw_start_8102e_1(tp);
@@ -5451,28 +5567,21 @@ static void rtl_hw_start_8101(struct net_device *dev)
        case RTL_GIGA_MAC_VER_39:
                rtl_hw_start_8106(tp);
                break;
+       case RTL_GIGA_MAC_VER_43:
+               rtl_hw_start_8168g_2(tp);
+               break;
        }
 
        RTL_W8(Cfg9346, Cfg9346_Lock);
 
-       RTL_W8(MaxTxPacketSize, TxPacketMax);
-
-       rtl_set_rx_max_size(ioaddr, rx_buf_sz);
-
-       tp->cp_cmd &= ~R810X_CPCMD_QUIRK_MASK;
-       RTL_W16(CPlusCmd, tp->cp_cmd);
-
        RTL_W16(IntrMitigate, 0x0000);
 
-       rtl_set_rx_tx_desc_registers(tp, ioaddr);
-
        RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
-       rtl_set_rx_tx_config_registers(tp);
-
-       RTL_R8(IntrMask);
 
        rtl_set_rx_mode(dev);
 
+       RTL_R8(IntrMask);
+
        RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000);
 }
 
@@ -6744,6 +6853,8 @@ static void rtl_hw_initialize(struct rtl8169_private *tp)
        switch (tp->mac_version) {
        case RTL_GIGA_MAC_VER_40:
        case RTL_GIGA_MAC_VER_41:
+       case RTL_GIGA_MAC_VER_42:
+       case RTL_GIGA_MAC_VER_43:
                rtl_hw_init_8168g(tp);
                break;
 
@@ -6926,16 +7037,17 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* don't enable SG, IP_CSUM and TSO by default - it might not work
         * properly for all devices */
        dev->features |= NETIF_F_RXCSUM |
-               NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+               NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 
        dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
-               NETIF_F_RXCSUM | NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+               NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX |
+               NETIF_F_HW_VLAN_CTAG_RX;
        dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
                NETIF_F_HIGHDMA;
 
        if (tp->mac_version == RTL_GIGA_MAC_VER_05)
                /* 8110SCd requires hardware Rx VLAN - disallow toggling */
-               dev->hw_features &= ~NETIF_F_HW_VLAN_RX;
+               dev->hw_features &= ~NETIF_F_HW_VLAN_CTAG_RX;
 
        dev->hw_features |= NETIF_F_RXALL;
        dev->hw_features |= NETIF_F_RXFCS;
index 6ed333fe5c04cd36618c465c0a3bec8dafbe8fcc..b8e52cd1a6985cf1c0fd00fe940907fd1bb92e6a 100644 (file)
@@ -2,7 +2,8 @@
  *  SuperH Ethernet device driver
  *
  *  Copyright (C) 2006-2012 Nobuhiro Iwamatsu
- *  Copyright (C) 2008-2012 Renesas Solutions Corp.
+ *  Copyright (C) 2008-2013 Renesas Solutions Corp.
+ *  Copyright (C) 2013 Cogent Embedded, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify it
  *  under the terms and conditions of the GNU General Public License,
                NETIF_MSG_RX_ERR| \
                NETIF_MSG_TX_ERR)
 
+static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = {
+       [EDSR]          = 0x0000,
+       [EDMR]          = 0x0400,
+       [EDTRR]         = 0x0408,
+       [EDRRR]         = 0x0410,
+       [EESR]          = 0x0428,
+       [EESIPR]        = 0x0430,
+       [TDLAR]         = 0x0010,
+       [TDFAR]         = 0x0014,
+       [TDFXR]         = 0x0018,
+       [TDFFR]         = 0x001c,
+       [RDLAR]         = 0x0030,
+       [RDFAR]         = 0x0034,
+       [RDFXR]         = 0x0038,
+       [RDFFR]         = 0x003c,
+       [TRSCER]        = 0x0438,
+       [RMFCR]         = 0x0440,
+       [TFTR]          = 0x0448,
+       [FDR]           = 0x0450,
+       [RMCR]          = 0x0458,
+       [RPADIR]        = 0x0460,
+       [FCFTR]         = 0x0468,
+       [CSMR]          = 0x04E4,
+
+       [ECMR]          = 0x0500,
+       [ECSR]          = 0x0510,
+       [ECSIPR]        = 0x0518,
+       [PIR]           = 0x0520,
+       [PSR]           = 0x0528,
+       [PIPR]          = 0x052c,
+       [RFLR]          = 0x0508,
+       [APR]           = 0x0554,
+       [MPR]           = 0x0558,
+       [PFTCR]         = 0x055c,
+       [PFRCR]         = 0x0560,
+       [TPAUSER]       = 0x0564,
+       [GECMR]         = 0x05b0,
+       [BCULR]         = 0x05b4,
+       [MAHR]          = 0x05c0,
+       [MALR]          = 0x05c8,
+       [TROCR]         = 0x0700,
+       [CDCR]          = 0x0708,
+       [LCCR]          = 0x0710,
+       [CEFCR]         = 0x0740,
+       [FRECR]         = 0x0748,
+       [TSFRCR]        = 0x0750,
+       [TLFRCR]        = 0x0758,
+       [RFCR]          = 0x0760,
+       [CERCR]         = 0x0768,
+       [CEECR]         = 0x0770,
+       [MAFCR]         = 0x0778,
+       [RMII_MII]      = 0x0790,
+
+       [ARSTR]         = 0x0000,
+       [TSU_CTRST]     = 0x0004,
+       [TSU_FWEN0]     = 0x0010,
+       [TSU_FWEN1]     = 0x0014,
+       [TSU_FCM]       = 0x0018,
+       [TSU_BSYSL0]    = 0x0020,
+       [TSU_BSYSL1]    = 0x0024,
+       [TSU_PRISL0]    = 0x0028,
+       [TSU_PRISL1]    = 0x002c,
+       [TSU_FWSL0]     = 0x0030,
+       [TSU_FWSL1]     = 0x0034,
+       [TSU_FWSLC]     = 0x0038,
+       [TSU_QTAG0]     = 0x0040,
+       [TSU_QTAG1]     = 0x0044,
+       [TSU_FWSR]      = 0x0050,
+       [TSU_FWINMK]    = 0x0054,
+       [TSU_ADQT0]     = 0x0048,
+       [TSU_ADQT1]     = 0x004c,
+       [TSU_VTAG0]     = 0x0058,
+       [TSU_VTAG1]     = 0x005c,
+       [TSU_ADSBSY]    = 0x0060,
+       [TSU_TEN]       = 0x0064,
+       [TSU_POST1]     = 0x0070,
+       [TSU_POST2]     = 0x0074,
+       [TSU_POST3]     = 0x0078,
+       [TSU_POST4]     = 0x007c,
+       [TSU_ADRH0]     = 0x0100,
+       [TSU_ADRL0]     = 0x0104,
+       [TSU_ADRH31]    = 0x01f8,
+       [TSU_ADRL31]    = 0x01fc,
+
+       [TXNLCR0]       = 0x0080,
+       [TXALCR0]       = 0x0084,
+       [RXNLCR0]       = 0x0088,
+       [RXALCR0]       = 0x008c,
+       [FWNLCR0]       = 0x0090,
+       [FWALCR0]       = 0x0094,
+       [TXNLCR1]       = 0x00a0,
+       [TXALCR1]       = 0x00a0,
+       [RXNLCR1]       = 0x00a8,
+       [RXALCR1]       = 0x00ac,
+       [FWNLCR1]       = 0x00b0,
+       [FWALCR1]       = 0x00b4,
+};
+
+static const u16 sh_eth_offset_fast_rcar[SH_ETH_MAX_REGISTER_OFFSET] = {
+       [ECMR]          = 0x0300,
+       [RFLR]          = 0x0308,
+       [ECSR]          = 0x0310,
+       [ECSIPR]        = 0x0318,
+       [PIR]           = 0x0320,
+       [PSR]           = 0x0328,
+       [RDMLR]         = 0x0340,
+       [IPGR]          = 0x0350,
+       [APR]           = 0x0354,
+       [MPR]           = 0x0358,
+       [RFCF]          = 0x0360,
+       [TPAUSER]       = 0x0364,
+       [TPAUSECR]      = 0x0368,
+       [MAHR]          = 0x03c0,
+       [MALR]          = 0x03c8,
+       [TROCR]         = 0x03d0,
+       [CDCR]          = 0x03d4,
+       [LCCR]          = 0x03d8,
+       [CNDCR]         = 0x03dc,
+       [CEFCR]         = 0x03e4,
+       [FRECR]         = 0x03e8,
+       [TSFRCR]        = 0x03ec,
+       [TLFRCR]        = 0x03f0,
+       [RFCR]          = 0x03f4,
+       [MAFCR]         = 0x03f8,
+
+       [EDMR]          = 0x0200,
+       [EDTRR]         = 0x0208,
+       [EDRRR]         = 0x0210,
+       [TDLAR]         = 0x0218,
+       [RDLAR]         = 0x0220,
+       [EESR]          = 0x0228,
+       [EESIPR]        = 0x0230,
+       [TRSCER]        = 0x0238,
+       [RMFCR]         = 0x0240,
+       [TFTR]          = 0x0248,
+       [FDR]           = 0x0250,
+       [RMCR]          = 0x0258,
+       [TFUCR]         = 0x0264,
+       [RFOCR]         = 0x0268,
+       [FCFTR]         = 0x0270,
+       [TRIMD]         = 0x027c,
+};
+
+static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = {
+       [ECMR]          = 0x0100,
+       [RFLR]          = 0x0108,
+       [ECSR]          = 0x0110,
+       [ECSIPR]        = 0x0118,
+       [PIR]           = 0x0120,
+       [PSR]           = 0x0128,
+       [RDMLR]         = 0x0140,
+       [IPGR]          = 0x0150,
+       [APR]           = 0x0154,
+       [MPR]           = 0x0158,
+       [TPAUSER]       = 0x0164,
+       [RFCF]          = 0x0160,
+       [TPAUSECR]      = 0x0168,
+       [BCFRR]         = 0x016c,
+       [MAHR]          = 0x01c0,
+       [MALR]          = 0x01c8,
+       [TROCR]         = 0x01d0,
+       [CDCR]          = 0x01d4,
+       [LCCR]          = 0x01d8,
+       [CNDCR]         = 0x01dc,
+       [CEFCR]         = 0x01e4,
+       [FRECR]         = 0x01e8,
+       [TSFRCR]        = 0x01ec,
+       [TLFRCR]        = 0x01f0,
+       [RFCR]          = 0x01f4,
+       [MAFCR]         = 0x01f8,
+       [RTRATE]        = 0x01fc,
+
+       [EDMR]          = 0x0000,
+       [EDTRR]         = 0x0008,
+       [EDRRR]         = 0x0010,
+       [TDLAR]         = 0x0018,
+       [RDLAR]         = 0x0020,
+       [EESR]          = 0x0028,
+       [EESIPR]        = 0x0030,
+       [TRSCER]        = 0x0038,
+       [RMFCR]         = 0x0040,
+       [TFTR]          = 0x0048,
+       [FDR]           = 0x0050,
+       [RMCR]          = 0x0058,
+       [TFUCR]         = 0x0064,
+       [RFOCR]         = 0x0068,
+       [FCFTR]         = 0x0070,
+       [RPADIR]        = 0x0078,
+       [TRIMD]         = 0x007c,
+       [RBWAR]         = 0x00c8,
+       [RDFAR]         = 0x00cc,
+       [TBRAR]         = 0x00d4,
+       [TDFAR]         = 0x00d8,
+};
+
+static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = {
+       [ECMR]          = 0x0160,
+       [ECSR]          = 0x0164,
+       [ECSIPR]        = 0x0168,
+       [PIR]           = 0x016c,
+       [MAHR]          = 0x0170,
+       [MALR]          = 0x0174,
+       [RFLR]          = 0x0178,
+       [PSR]           = 0x017c,
+       [TROCR]         = 0x0180,
+       [CDCR]          = 0x0184,
+       [LCCR]          = 0x0188,
+       [CNDCR]         = 0x018c,
+       [CEFCR]         = 0x0194,
+       [FRECR]         = 0x0198,
+       [TSFRCR]        = 0x019c,
+       [TLFRCR]        = 0x01a0,
+       [RFCR]          = 0x01a4,
+       [MAFCR]         = 0x01a8,
+       [IPGR]          = 0x01b4,
+       [APR]           = 0x01b8,
+       [MPR]           = 0x01bc,
+       [TPAUSER]       = 0x01c4,
+       [BCFR]          = 0x01cc,
+
+       [ARSTR]         = 0x0000,
+       [TSU_CTRST]     = 0x0004,
+       [TSU_FWEN0]     = 0x0010,
+       [TSU_FWEN1]     = 0x0014,
+       [TSU_FCM]       = 0x0018,
+       [TSU_BSYSL0]    = 0x0020,
+       [TSU_BSYSL1]    = 0x0024,
+       [TSU_PRISL0]    = 0x0028,
+       [TSU_PRISL1]    = 0x002c,
+       [TSU_FWSL0]     = 0x0030,
+       [TSU_FWSL1]     = 0x0034,
+       [TSU_FWSLC]     = 0x0038,
+       [TSU_QTAGM0]    = 0x0040,
+       [TSU_QTAGM1]    = 0x0044,
+       [TSU_ADQT0]     = 0x0048,
+       [TSU_ADQT1]     = 0x004c,
+       [TSU_FWSR]      = 0x0050,
+       [TSU_FWINMK]    = 0x0054,
+       [TSU_ADSBSY]    = 0x0060,
+       [TSU_TEN]       = 0x0064,
+       [TSU_POST1]     = 0x0070,
+       [TSU_POST2]     = 0x0074,
+       [TSU_POST3]     = 0x0078,
+       [TSU_POST4]     = 0x007c,
+
+       [TXNLCR0]       = 0x0080,
+       [TXALCR0]       = 0x0084,
+       [RXNLCR0]       = 0x0088,
+       [RXALCR0]       = 0x008c,
+       [FWNLCR0]       = 0x0090,
+       [FWALCR0]       = 0x0094,
+       [TXNLCR1]       = 0x00a0,
+       [TXALCR1]       = 0x00a0,
+       [RXNLCR1]       = 0x00a8,
+       [RXALCR1]       = 0x00ac,
+       [FWNLCR1]       = 0x00b0,
+       [FWALCR1]       = 0x00b4,
+
+       [TSU_ADRH0]     = 0x0100,
+       [TSU_ADRL0]     = 0x0104,
+       [TSU_ADRL31]    = 0x01fc,
+};
+
 #if defined(CONFIG_CPU_SUBTYPE_SH7734) || \
        defined(CONFIG_CPU_SUBTYPE_SH7763) || \
        defined(CONFIG_ARCH_R8A7740)
@@ -78,7 +342,7 @@ static void sh_eth_select_mii(struct net_device *ndev)
 #endif
 
 /* There is CPU dependent code */
-#if defined(CONFIG_CPU_SUBTYPE_SH7724) || defined(CONFIG_ARCH_R8A7779)
+#if defined(CONFIG_ARCH_R8A7779)
 #define SH_ETH_RESET_DEFAULT   1
 static void sh_eth_set_duplex(struct net_device *ndev)
 {
@@ -93,18 +357,60 @@ static void sh_eth_set_duplex(struct net_device *ndev)
 static void sh_eth_set_rate(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
-       unsigned int bits = ECMR_RTM;
 
-#if defined(CONFIG_ARCH_R8A7779)
-       bits |= ECMR_ELB;
-#endif
+       switch (mdp->speed) {
+       case 10: /* 10BASE */
+               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_ELB, ECMR);
+               break;
+       case 100:/* 100BASE */
+               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_ELB, ECMR);
+               break;
+       default:
+               break;
+       }
+}
+
+/* R8A7779 */
+static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
+       .set_duplex     = sh_eth_set_duplex,
+       .set_rate       = sh_eth_set_rate,
+
+       .ecsr_value     = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD,
+       .ecsipr_value   = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP,
+       .eesipr_value   = 0x01ff009f,
+
+       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO,
+       .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RDE |
+                         EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI,
+       .tx_error_check = EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE,
+
+       .apr            = 1,
+       .mpr            = 1,
+       .tpauser        = 1,
+       .hw_swap        = 1,
+};
+#elif defined(CONFIG_CPU_SUBTYPE_SH7724)
+#define SH_ETH_RESET_DEFAULT   1
+static void sh_eth_set_duplex(struct net_device *ndev)
+{
+       struct sh_eth_private *mdp = netdev_priv(ndev);
+
+       if (mdp->duplex) /* Full */
+               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR);
+       else            /* Half */
+               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
+}
+
+static void sh_eth_set_rate(struct net_device *ndev)
+{
+       struct sh_eth_private *mdp = netdev_priv(ndev);
 
        switch (mdp->speed) {
        case 10: /* 10BASE */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~bits, ECMR);
+               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_RTM, ECMR);
                break;
        case 100:/* 100BASE */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | bits, ECMR);
+               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_RTM, ECMR);
                break;
        default:
                break;
@@ -592,7 +898,7 @@ static int sh_eth_check_reset(struct net_device *ndev)
                cnt--;
        }
        if (cnt < 0) {
-               printk(KERN_ERR "Device reset fail\n");
+               pr_err("Device reset fail\n");
                ret = -ETIMEDOUT;
        }
        return ret;
@@ -908,11 +1214,8 @@ static int sh_eth_ring_init(struct net_device *ndev)
        /* Allocate all Rx descriptors. */
        rx_ringsize = sizeof(struct sh_eth_rxdesc) * mdp->num_rx_ring;
        mdp->rx_ring = dma_alloc_coherent(NULL, rx_ringsize, &mdp->rx_desc_dma,
-                       GFP_KERNEL);
-
+                                         GFP_KERNEL);
        if (!mdp->rx_ring) {
-               dev_err(&ndev->dev, "Cannot allocate Rx Ring (size %d bytes)\n",
-                       rx_ringsize);
                ret = -ENOMEM;
                goto desc_ring_free;
        }
@@ -922,10 +1225,8 @@ static int sh_eth_ring_init(struct net_device *ndev)
        /* Allocate all Tx descriptors. */
        tx_ringsize = sizeof(struct sh_eth_txdesc) * mdp->num_tx_ring;
        mdp->tx_ring = dma_alloc_coherent(NULL, tx_ringsize, &mdp->tx_desc_dma,
-                       GFP_KERNEL);
+                                         GFP_KERNEL);
        if (!mdp->tx_ring) {
-               dev_err(&ndev->dev, "Cannot allocate Tx Ring (size %d bytes)\n",
-                       tx_ringsize);
                ret = -ENOMEM;
                goto desc_ring_free;
        }
@@ -2147,7 +2448,8 @@ static int sh_eth_get_vtag_index(struct sh_eth_private *mdp)
                return TSU_VTAG1;
 }
 
-static int sh_eth_vlan_rx_add_vid(struct net_device *ndev, u16 vid)
+static int sh_eth_vlan_rx_add_vid(struct net_device *ndev,
+                                 __be16 proto, u16 vid)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
        int vtag_reg_index = sh_eth_get_vtag_index(mdp);
@@ -2177,7 +2479,8 @@ static int sh_eth_vlan_rx_add_vid(struct net_device *ndev, u16 vid)
        return 0;
 }
 
-static int sh_eth_vlan_rx_kill_vid(struct net_device *ndev, u16 vid)
+static int sh_eth_vlan_rx_kill_vid(struct net_device *ndev,
+                                  __be16 proto, u16 vid)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
        int vtag_reg_index = sh_eth_get_vtag_index(mdp);
@@ -2228,7 +2531,6 @@ static void sh_eth_tsu_init(struct sh_eth_private *mdp)
 /* MDIO bus release function */
 static int sh_mdio_release(struct net_device *ndev)
 {
-       struct sh_eth_private *mdp = netdev_priv(ndev);
        struct mii_bus *bus = dev_get_drvdata(&ndev->dev);
 
        /* unregister mdio bus */
@@ -2237,15 +2539,9 @@ static int sh_mdio_release(struct net_device *ndev)
        /* remove mdio bus info from net_device */
        dev_set_drvdata(&ndev->dev, NULL);
 
-       /* free interrupts memory */
-       kfree(bus->irq);
-
        /* free bitbang info */
        free_mdio_bitbang(bus);
 
-       /* free bitbang memory */
-       kfree(mdp->bitbang);
-
        return 0;
 }
 
@@ -2258,7 +2554,8 @@ static int sh_mdio_init(struct net_device *ndev, int id,
        struct sh_eth_private *mdp = netdev_priv(ndev);
 
        /* create bit control struct for PHY */
-       bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL);
+       bitbang = devm_kzalloc(&ndev->dev, sizeof(struct bb_info),
+                              GFP_KERNEL);
        if (!bitbang) {
                ret = -ENOMEM;
                goto out;
@@ -2267,18 +2564,17 @@ static int sh_mdio_init(struct net_device *ndev, int id,
        /* bitbang init */
        bitbang->addr = mdp->addr + mdp->reg_offset[PIR];
        bitbang->set_gate = pd->set_mdio_gate;
-       bitbang->mdi_msk = 0x08;
-       bitbang->mdo_msk = 0x04;
-       bitbang->mmd_msk = 0x02;/* MMD */
-       bitbang->mdc_msk = 0x01;
+       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 */
-       mdp->bitbang = bitbang;
        mdp->mii_bus = alloc_mdio_bitbang(&bitbang->ctrl);
        if (!mdp->mii_bus) {
                ret = -ENOMEM;
-               goto out_free_bitbang;
+               goto out;
        }
 
        /* Hook up MII support for ethtool */
@@ -2288,7 +2584,9 @@ static int sh_mdio_init(struct net_device *ndev, int id,
                mdp->pdev->name, id);
 
        /* PHY IRQ */
-       mdp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
+       mdp->mii_bus->irq = devm_kzalloc(&ndev->dev,
+                                        sizeof(int) * PHY_MAX_ADDR,
+                                        GFP_KERNEL);
        if (!mdp->mii_bus->irq) {
                ret = -ENOMEM;
                goto out_free_bus;
@@ -2300,21 +2598,15 @@ static int sh_mdio_init(struct net_device *ndev, int id,
        /* register mdio bus */
        ret = mdiobus_register(mdp->mii_bus);
        if (ret)
-               goto out_free_irq;
+               goto out_free_bus;
 
        dev_set_drvdata(&ndev->dev, mdp->mii_bus);
 
        return 0;
 
-out_free_irq:
-       kfree(mdp->mii_bus->irq);
-
 out_free_bus:
        free_mdio_bitbang(mdp->mii_bus);
 
-out_free_bitbang:
-       kfree(bitbang);
-
 out:
        return ret;
 }
@@ -2327,6 +2619,9 @@ static const u16 *sh_eth_get_register_offset(int register_type)
        case SH_ETH_REG_GIGABIT:
                reg_offset = sh_eth_offset_gigabit;
                break;
+       case SH_ETH_REG_FAST_RCAR:
+               reg_offset = sh_eth_offset_fast_rcar;
+               break;
        case SH_ETH_REG_FAST_SH4:
                reg_offset = sh_eth_offset_fast_sh4;
                break;
@@ -2334,7 +2629,7 @@ static const u16 *sh_eth_get_register_offset(int register_type)
                reg_offset = sh_eth_offset_fast_sh3_sh2;
                break;
        default:
-               printk(KERN_ERR "Unknown register type (%d)\n", register_type);
+               pr_err("Unknown register type (%d)\n", register_type);
                break;
        }
 
@@ -2364,7 +2659,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
        struct resource *res;
        struct net_device *ndev = NULL;
        struct sh_eth_private *mdp = NULL;
-       struct sh_eth_plat_data *pd;
+       struct sh_eth_plat_data *pd = pdev->dev.platform_data;
 
        /* get base addr */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2402,10 +2697,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
        mdp = netdev_priv(ndev);
        mdp->num_tx_ring = TX_RING_SIZE;
        mdp->num_rx_ring = RX_RING_SIZE;
-       mdp->addr = ioremap(res->start, resource_size(res));
-       if (mdp->addr == NULL) {
-               ret = -ENOMEM;
-               dev_err(&pdev->dev, "ioremap failed.\n");
+       mdp->addr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(mdp->addr)) {
+               ret = PTR_ERR(mdp->addr);
                goto out_release;
        }
 
@@ -2414,7 +2708,6 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        pm_runtime_resume(&pdev->dev);
 
-       pd = (struct sh_eth_plat_data *)(pdev->dev.platform_data);
        /* get PHY ID */
        mdp->phy_id = pd->phy;
        mdp->phy_interface = pd->phy_interface;
@@ -2452,15 +2745,13 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
                        ret = -ENODEV;
                        goto out_release;
                }
-               mdp->tsu_addr = ioremap(rtsu->start,
-                                       resource_size(rtsu));
-               if (mdp->tsu_addr == NULL) {
-                       ret = -ENOMEM;
-                       dev_err(&pdev->dev, "TSU ioremap failed.\n");
+               mdp->tsu_addr = devm_ioremap_resource(&pdev->dev, rtsu);
+               if (IS_ERR(mdp->tsu_addr)) {
+                       ret = PTR_ERR(mdp->tsu_addr);
                        goto out_release;
                }
                mdp->port = devno % 2;
-               ndev->features = NETIF_F_HW_VLAN_FILTER;
+               ndev->features = NETIF_F_HW_VLAN_CTAG_FILTER;
        }
 
        /* initialize first or needed device */
@@ -2497,10 +2788,6 @@ out_unregister:
 
 out_release:
        /* net_dev free */
-       if (mdp && mdp->addr)
-               iounmap(mdp->addr);
-       if (mdp && mdp->tsu_addr)
-               iounmap(mdp->tsu_addr);
        if (ndev)
                free_netdev(ndev);
 
@@ -2511,14 +2798,10 @@ out:
 static int sh_eth_drv_remove(struct platform_device *pdev)
 {
        struct net_device *ndev = platform_get_drvdata(pdev);
-       struct sh_eth_private *mdp = netdev_priv(ndev);
 
-       if (mdp->cd->tsu)
-               iounmap(mdp->tsu_addr);
        sh_mdio_release(ndev);
        unregister_netdev(ndev);
        pm_runtime_disable(&pdev->dev);
-       iounmap(mdp->addr);
        free_netdev(ndev);
        platform_set_drvdata(pdev, NULL);
 
index 828be4515008145507f59a5bbe34ac64eb28090b..1ddc9f235bcb393cd915764764eb3ab53f313e61 100644 (file)
@@ -156,225 +156,6 @@ enum {
        SH_ETH_MAX_REGISTER_OFFSET,
 };
 
-static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = {
-       [EDSR]  = 0x0000,
-       [EDMR]  = 0x0400,
-       [EDTRR] = 0x0408,
-       [EDRRR] = 0x0410,
-       [EESR]  = 0x0428,
-       [EESIPR]        = 0x0430,
-       [TDLAR] = 0x0010,
-       [TDFAR] = 0x0014,
-       [TDFXR] = 0x0018,
-       [TDFFR] = 0x001c,
-       [RDLAR] = 0x0030,
-       [RDFAR] = 0x0034,
-       [RDFXR] = 0x0038,
-       [RDFFR] = 0x003c,
-       [TRSCER]        = 0x0438,
-       [RMFCR] = 0x0440,
-       [TFTR]  = 0x0448,
-       [FDR]   = 0x0450,
-       [RMCR]  = 0x0458,
-       [RPADIR]        = 0x0460,
-       [FCFTR] = 0x0468,
-       [CSMR] = 0x04E4,
-
-       [ECMR]  = 0x0500,
-       [ECSR]  = 0x0510,
-       [ECSIPR]        = 0x0518,
-       [PIR]   = 0x0520,
-       [PSR]   = 0x0528,
-       [PIPR]  = 0x052c,
-       [RFLR]  = 0x0508,
-       [APR]   = 0x0554,
-       [MPR]   = 0x0558,
-       [PFTCR] = 0x055c,
-       [PFRCR] = 0x0560,
-       [TPAUSER]       = 0x0564,
-       [GECMR] = 0x05b0,
-       [BCULR] = 0x05b4,
-       [MAHR]  = 0x05c0,
-       [MALR]  = 0x05c8,
-       [TROCR] = 0x0700,
-       [CDCR]  = 0x0708,
-       [LCCR]  = 0x0710,
-       [CEFCR] = 0x0740,
-       [FRECR] = 0x0748,
-       [TSFRCR]        = 0x0750,
-       [TLFRCR]        = 0x0758,
-       [RFCR]  = 0x0760,
-       [CERCR] = 0x0768,
-       [CEECR] = 0x0770,
-       [MAFCR] = 0x0778,
-       [RMII_MII] =  0x0790,
-
-       [ARSTR] = 0x0000,
-       [TSU_CTRST]     = 0x0004,
-       [TSU_FWEN0]     = 0x0010,
-       [TSU_FWEN1]     = 0x0014,
-       [TSU_FCM]       = 0x0018,
-       [TSU_BSYSL0]    = 0x0020,
-       [TSU_BSYSL1]    = 0x0024,
-       [TSU_PRISL0]    = 0x0028,
-       [TSU_PRISL1]    = 0x002c,
-       [TSU_FWSL0]     = 0x0030,
-       [TSU_FWSL1]     = 0x0034,
-       [TSU_FWSLC]     = 0x0038,
-       [TSU_QTAG0]     = 0x0040,
-       [TSU_QTAG1]     = 0x0044,
-       [TSU_FWSR]      = 0x0050,
-       [TSU_FWINMK]    = 0x0054,
-       [TSU_ADQT0]     = 0x0048,
-       [TSU_ADQT1]     = 0x004c,
-       [TSU_VTAG0]     = 0x0058,
-       [TSU_VTAG1]     = 0x005c,
-       [TSU_ADSBSY]    = 0x0060,
-       [TSU_TEN]       = 0x0064,
-       [TSU_POST1]     = 0x0070,
-       [TSU_POST2]     = 0x0074,
-       [TSU_POST3]     = 0x0078,
-       [TSU_POST4]     = 0x007c,
-       [TSU_ADRH0]     = 0x0100,
-       [TSU_ADRL0]     = 0x0104,
-       [TSU_ADRH31]    = 0x01f8,
-       [TSU_ADRL31]    = 0x01fc,
-
-       [TXNLCR0]       = 0x0080,
-       [TXALCR0]       = 0x0084,
-       [RXNLCR0]       = 0x0088,
-       [RXALCR0]       = 0x008c,
-       [FWNLCR0]       = 0x0090,
-       [FWALCR0]       = 0x0094,
-       [TXNLCR1]       = 0x00a0,
-       [TXALCR1]       = 0x00a0,
-       [RXNLCR1]       = 0x00a8,
-       [RXALCR1]       = 0x00ac,
-       [FWNLCR1]       = 0x00b0,
-       [FWALCR1]       = 0x00b4,
-};
-
-static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = {
-       [ECMR]  = 0x0100,
-       [RFLR]  = 0x0108,
-       [ECSR]  = 0x0110,
-       [ECSIPR]        = 0x0118,
-       [PIR]   = 0x0120,
-       [PSR]   = 0x0128,
-       [RDMLR] = 0x0140,
-       [IPGR]  = 0x0150,
-       [APR]   = 0x0154,
-       [MPR]   = 0x0158,
-       [TPAUSER]       = 0x0164,
-       [RFCF]  = 0x0160,
-       [TPAUSECR]      = 0x0168,
-       [BCFRR] = 0x016c,
-       [MAHR]  = 0x01c0,
-       [MALR]  = 0x01c8,
-       [TROCR] = 0x01d0,
-       [CDCR]  = 0x01d4,
-       [LCCR]  = 0x01d8,
-       [CNDCR] = 0x01dc,
-       [CEFCR] = 0x01e4,
-       [FRECR] = 0x01e8,
-       [TSFRCR]        = 0x01ec,
-       [TLFRCR]        = 0x01f0,
-       [RFCR]  = 0x01f4,
-       [MAFCR] = 0x01f8,
-       [RTRATE]        = 0x01fc,
-
-       [EDMR]  = 0x0000,
-       [EDTRR] = 0x0008,
-       [EDRRR] = 0x0010,
-       [TDLAR] = 0x0018,
-       [RDLAR] = 0x0020,
-       [EESR]  = 0x0028,
-       [EESIPR]        = 0x0030,
-       [TRSCER]        = 0x0038,
-       [RMFCR] = 0x0040,
-       [TFTR]  = 0x0048,
-       [FDR]   = 0x0050,
-       [RMCR]  = 0x0058,
-       [TFUCR] = 0x0064,
-       [RFOCR] = 0x0068,
-       [FCFTR] = 0x0070,
-       [RPADIR]        = 0x0078,
-       [TRIMD] = 0x007c,
-       [RBWAR] = 0x00c8,
-       [RDFAR] = 0x00cc,
-       [TBRAR] = 0x00d4,
-       [TDFAR] = 0x00d8,
-};
-
-static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = {
-       [ECMR]  = 0x0160,
-       [ECSR]  = 0x0164,
-       [ECSIPR]        = 0x0168,
-       [PIR]   = 0x016c,
-       [MAHR]  = 0x0170,
-       [MALR]  = 0x0174,
-       [RFLR]  = 0x0178,
-       [PSR]   = 0x017c,
-       [TROCR] = 0x0180,
-       [CDCR]  = 0x0184,
-       [LCCR]  = 0x0188,
-       [CNDCR] = 0x018c,
-       [CEFCR] = 0x0194,
-       [FRECR] = 0x0198,
-       [TSFRCR]        = 0x019c,
-       [TLFRCR]        = 0x01a0,
-       [RFCR]  = 0x01a4,
-       [MAFCR] = 0x01a8,
-       [IPGR]  = 0x01b4,
-       [APR]   = 0x01b8,
-       [MPR]   = 0x01bc,
-       [TPAUSER]       = 0x01c4,
-       [BCFR]  = 0x01cc,
-
-       [ARSTR] = 0x0000,
-       [TSU_CTRST]     = 0x0004,
-       [TSU_FWEN0]     = 0x0010,
-       [TSU_FWEN1]     = 0x0014,
-       [TSU_FCM]       = 0x0018,
-       [TSU_BSYSL0]    = 0x0020,
-       [TSU_BSYSL1]    = 0x0024,
-       [TSU_PRISL0]    = 0x0028,
-       [TSU_PRISL1]    = 0x002c,
-       [TSU_FWSL0]     = 0x0030,
-       [TSU_FWSL1]     = 0x0034,
-       [TSU_FWSLC]     = 0x0038,
-       [TSU_QTAGM0]    = 0x0040,
-       [TSU_QTAGM1]    = 0x0044,
-       [TSU_ADQT0]     = 0x0048,
-       [TSU_ADQT1]     = 0x004c,
-       [TSU_FWSR]      = 0x0050,
-       [TSU_FWINMK]    = 0x0054,
-       [TSU_ADSBSY]    = 0x0060,
-       [TSU_TEN]       = 0x0064,
-       [TSU_POST1]     = 0x0070,
-       [TSU_POST2]     = 0x0074,
-       [TSU_POST3]     = 0x0078,
-       [TSU_POST4]     = 0x007c,
-
-       [TXNLCR0]       = 0x0080,
-       [TXALCR0]       = 0x0084,
-       [RXNLCR0]       = 0x0088,
-       [RXALCR0]       = 0x008c,
-       [FWNLCR0]       = 0x0090,
-       [FWALCR0]       = 0x0094,
-       [TXNLCR1]       = 0x00a0,
-       [TXALCR1]       = 0x00a0,
-       [RXNLCR1]       = 0x00a8,
-       [RXALCR1]       = 0x00ac,
-       [FWNLCR1]       = 0x00b0,
-       [FWALCR1]       = 0x00b4,
-
-       [TSU_ADRH0]     = 0x0100,
-       [TSU_ADRL0]     = 0x0104,
-       [TSU_ADRL31]    = 0x01fc,
-
-};
-
 /* Driver's parameters */
 #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
 #define SH4_SKB_RX_ALIGN       32
@@ -705,7 +486,6 @@ struct sh_eth_private {
        const u16 *reg_offset;
        void __iomem *addr;
        void __iomem *tsu_addr;
-       struct bb_info *bitbang;
        u32 num_rx_ring;
        u32 num_tx_ring;
        dma_addr_t rx_desc_dma;
index 21683e2b1ff412817213fa206416a6813235367d..b6739afeaca1517bf3351dae9e3a33469cccc46e 100644 (file)
@@ -998,6 +998,7 @@ static int s6gmac_probe(struct platform_device *pdev)
        mb = mdiobus_alloc();
        if (!mb) {
                printk(KERN_ERR DRV_PRMT "error allocating mii bus\n");
+               res = -ENOMEM;
                goto errmii;
        }
        mb->name = "s6gmac_mii";
@@ -1053,20 +1054,7 @@ static struct platform_driver s6gmac_driver = {
        },
 };
 
-static int __init s6gmac_init(void)
-{
-       printk(KERN_INFO DRV_PRMT "S6 GMAC ethernet driver\n");
-       return platform_driver_register(&s6gmac_driver);
-}
-
-
-static void __exit s6gmac_exit(void)
-{
-       platform_driver_unregister(&s6gmac_driver);
-}
-
-module_init(s6gmac_init);
-module_exit(s6gmac_exit);
+module_platform_driver(s6gmac_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("S6105 on chip Ethernet driver");
index 3aca57853ed470034d46e9eb01a848b761fbec28..bdac936a68bc2b12d0baf302551337c79702b265 100644 (file)
@@ -651,8 +651,11 @@ if (next_ptr < RX_START || next_ptr >= RX_END) {
                                skb->protocol = eth_type_trans(skb, dev);
                                netif_rx(skb);
                                received ++;
-                       } else
-                               goto dropping;
+                       } else {
+                               ether3_outw(next_ptr >> 8, REG_RECVEND);
+                               dev->stats.rx_dropped++;
+                               goto done;
+                       }
                } else {
                        struct net_device_stats *stats = &dev->stats;
                        ether3_outw(next_ptr >> 8, REG_RECVEND);
@@ -679,21 +682,6 @@ done:
        }
 
        return maxcnt;
-
-dropping:{
-       static unsigned long last_warned;
-
-       ether3_outw(next_ptr >> 8, REG_RECVEND);
-       /*
-        * Don't print this message too many times...
-        */
-       if (time_after(jiffies, last_warned + 10 * HZ)) {
-               last_warned = jiffies;
-               printk("%s: memory squeeze, dropping packet.\n", dev->name);
-       }
-       dev->stats.rx_dropped++;
-       goto done;
-       }
 }
 
 /*
index 0fde9ca282696f53af4f32b8df32fed0cc0398e9..0ad5694b41f8eb12d43d2e518b89607ef373bbea 100644 (file)
@@ -381,8 +381,6 @@ memory_squeeze:
                                        dev->stats.rx_packets++;
                                        dev->stats.rx_bytes += len;
                                } else {
-                                       printk(KERN_NOTICE "%s: Memory squeeze, deferring packet.\n",
-                                               dev->name);
                                        dev->stats.rx_dropped++;
                                }
                        } else {
index 0bc00991d31010cf1ec4efd95ec95afba8aa5d53..01b99206139a0acefeb9452ad62b55346c6278f6 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/topology.h>
 #include <linux/gfp.h>
 #include <linux/cpu_rmap.h>
+#include <linux/aer.h>
 #include "net_driver.h"
 #include "efx.h"
 #include "nic.h"
@@ -71,21 +72,21 @@ const char *const efx_loopback_mode_names[] = {
 
 const unsigned int efx_reset_type_max = RESET_TYPE_MAX;
 const char *const efx_reset_type_names[] = {
-       [RESET_TYPE_INVISIBLE]     = "INVISIBLE",
-       [RESET_TYPE_ALL]           = "ALL",
-       [RESET_TYPE_WORLD]         = "WORLD",
-       [RESET_TYPE_DISABLE]       = "DISABLE",
-       [RESET_TYPE_TX_WATCHDOG]   = "TX_WATCHDOG",
-       [RESET_TYPE_INT_ERROR]     = "INT_ERROR",
-       [RESET_TYPE_RX_RECOVERY]   = "RX_RECOVERY",
-       [RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH",
-       [RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH",
-       [RESET_TYPE_TX_SKIP]       = "TX_SKIP",
-       [RESET_TYPE_MC_FAILURE]    = "MC_FAILURE",
+       [RESET_TYPE_INVISIBLE]          = "INVISIBLE",
+       [RESET_TYPE_ALL]                = "ALL",
+       [RESET_TYPE_RECOVER_OR_ALL]     = "RECOVER_OR_ALL",
+       [RESET_TYPE_WORLD]              = "WORLD",
+       [RESET_TYPE_RECOVER_OR_DISABLE] = "RECOVER_OR_DISABLE",
+       [RESET_TYPE_DISABLE]            = "DISABLE",
+       [RESET_TYPE_TX_WATCHDOG]        = "TX_WATCHDOG",
+       [RESET_TYPE_INT_ERROR]          = "INT_ERROR",
+       [RESET_TYPE_RX_RECOVERY]        = "RX_RECOVERY",
+       [RESET_TYPE_RX_DESC_FETCH]      = "RX_DESC_FETCH",
+       [RESET_TYPE_TX_DESC_FETCH]      = "TX_DESC_FETCH",
+       [RESET_TYPE_TX_SKIP]            = "TX_SKIP",
+       [RESET_TYPE_MC_FAILURE]         = "MC_FAILURE",
 };
 
-#define EFX_MAX_MTU (9 * 1024)
-
 /* Reset workqueue. If any NIC has a hardware failure then a reset will be
  * queued onto this work queue. This is not a per-nic work queue, because
  * efx_reset_work() acquires the rtnl lock, so resets are naturally serialised.
@@ -117,9 +118,12 @@ MODULE_PARM_DESC(separate_tx_channels,
 static int napi_weight = 64;
 
 /* This is the time (in jiffies) between invocations of the hardware
- * monitor.  On Falcon-based NICs, this will:
+ * monitor.
+ * On Falcon-based NICs, this will:
  * - Check the on-board hardware monitor;
  * - Poll the link state and reconfigure the hardware as necessary.
+ * On Siena-based NICs for power systems with EEH support, this will give EEH a
+ * chance to start.
  */
 static unsigned int efx_monitor_interval = 1 * HZ;
 
@@ -203,13 +207,14 @@ static void efx_stop_all(struct efx_nic *efx);
 #define EFX_ASSERT_RESET_SERIALISED(efx)               \
        do {                                            \
                if ((efx->state == STATE_READY) ||      \
+                   (efx->state == STATE_RECOVERY) ||   \
                    (efx->state == STATE_DISABLED))     \
                        ASSERT_RTNL();                  \
        } while (0)
 
 static int efx_check_disabled(struct efx_nic *efx)
 {
-       if (efx->state == STATE_DISABLED) {
+       if (efx->state == STATE_DISABLED || efx->state == STATE_RECOVERY) {
                netif_err(efx, drv, efx->net_dev,
                          "device is disabled due to earlier errors\n");
                return -EIO;
@@ -242,15 +247,9 @@ static int efx_process_channel(struct efx_channel *channel, int budget)
                struct efx_rx_queue *rx_queue =
                        efx_channel_get_rx_queue(channel);
 
-               /* Deliver last RX packet. */
-               if (channel->rx_pkt) {
-                       __efx_rx_packet(channel, channel->rx_pkt);
-                       channel->rx_pkt = NULL;
-               }
-               if (rx_queue->enabled) {
-                       efx_rx_strategy(channel);
+               efx_rx_flush_packet(channel);
+               if (rx_queue->enabled)
                        efx_fast_push_rx_descriptors(rx_queue);
-               }
        }
 
        return spent;
@@ -625,20 +624,51 @@ fail:
  */
 static void efx_start_datapath(struct efx_nic *efx)
 {
+       bool old_rx_scatter = efx->rx_scatter;
        struct efx_tx_queue *tx_queue;
        struct efx_rx_queue *rx_queue;
        struct efx_channel *channel;
+       size_t rx_buf_len;
 
        /* Calculate the rx buffer allocation parameters required to
         * support the current MTU, including padding for header
         * alignment and overruns.
         */
-       efx->rx_buffer_len = (max(EFX_PAGE_IP_ALIGN, NET_IP_ALIGN) +
-                             EFX_MAX_FRAME_LEN(efx->net_dev->mtu) +
-                             efx->type->rx_buffer_hash_size +
-                             efx->type->rx_buffer_padding);
-       efx->rx_buffer_order = get_order(efx->rx_buffer_len +
-                                        sizeof(struct efx_rx_page_state));
+       efx->rx_dma_len = (efx->type->rx_buffer_hash_size +
+                          EFX_MAX_FRAME_LEN(efx->net_dev->mtu) +
+                          efx->type->rx_buffer_padding);
+       rx_buf_len = (sizeof(struct efx_rx_page_state) +
+                     EFX_PAGE_IP_ALIGN + efx->rx_dma_len);
+       if (rx_buf_len <= PAGE_SIZE) {
+               efx->rx_scatter = false;
+               efx->rx_buffer_order = 0;
+       } else if (efx->type->can_rx_scatter) {
+               BUILD_BUG_ON(sizeof(struct efx_rx_page_state) +
+                            EFX_PAGE_IP_ALIGN + EFX_RX_USR_BUF_SIZE >
+                            PAGE_SIZE / 2);
+               efx->rx_scatter = true;
+               efx->rx_dma_len = EFX_RX_USR_BUF_SIZE;
+               efx->rx_buffer_order = 0;
+       } else {
+               efx->rx_scatter = false;
+               efx->rx_buffer_order = get_order(rx_buf_len);
+       }
+
+       efx_rx_config_page_split(efx);
+       if (efx->rx_buffer_order)
+               netif_dbg(efx, drv, efx->net_dev,
+                         "RX buf len=%u; page order=%u batch=%u\n",
+                         efx->rx_dma_len, efx->rx_buffer_order,
+                         efx->rx_pages_per_batch);
+       else
+               netif_dbg(efx, drv, efx->net_dev,
+                         "RX buf len=%u step=%u bpp=%u; page batch=%u\n",
+                         efx->rx_dma_len, efx->rx_page_buf_step,
+                         efx->rx_bufs_per_page, efx->rx_pages_per_batch);
+
+       /* RX filters also have scatter-enabled flags */
+       if (efx->rx_scatter != old_rx_scatter)
+               efx_filter_update_rx_scatter(efx);
 
        /* We must keep at least one descriptor in a TX ring empty.
         * We could avoid this when the queue size does not exactly
@@ -655,16 +685,12 @@ static void efx_start_datapath(struct efx_nic *efx)
                efx_for_each_channel_tx_queue(tx_queue, channel)
                        efx_init_tx_queue(tx_queue);
 
-               /* The rx buffer allocation strategy is MTU dependent */
-               efx_rx_strategy(channel);
-
                efx_for_each_channel_rx_queue(rx_queue, channel) {
                        efx_init_rx_queue(rx_queue);
                        efx_nic_generate_fill_event(rx_queue);
                }
 
-               WARN_ON(channel->rx_pkt != NULL);
-               efx_rx_strategy(channel);
+               WARN_ON(channel->rx_pkt_n_frags);
        }
 
        if (netif_device_present(efx->net_dev))
@@ -683,7 +709,7 @@ static void efx_stop_datapath(struct efx_nic *efx)
        BUG_ON(efx->port_enabled);
 
        /* Only perform flush if dma is enabled */
-       if (dev->is_busmaster) {
+       if (dev->is_busmaster && efx->state != STATE_RECOVERY) {
                rc = efx_nic_flush_queues(efx);
 
                if (rc && EFX_WORKAROUND_7803(efx)) {
@@ -1596,13 +1622,15 @@ static void efx_start_all(struct efx_nic *efx)
        efx_start_port(efx);
        efx_start_datapath(efx);
 
-       /* Start the hardware monitor if there is one. Otherwise (we're link
-        * event driven), we have to poll the PHY because after an event queue
-        * flush, we could have a missed a link state change */
-       if (efx->type->monitor != NULL) {
+       /* Start the hardware monitor if there is one */
+       if (efx->type->monitor != NULL)
                queue_delayed_work(efx->workqueue, &efx->monitor_work,
                                   efx_monitor_interval);
-       } else {
+
+       /* If link state detection is normally event-driven, we have
+        * to poll now because we could have missed a change
+        */
+       if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) {
                mutex_lock(&efx->mac_lock);
                if (efx->phy_op->poll(efx))
                        efx_link_status_changed(efx);
@@ -2309,7 +2337,9 @@ int efx_reset(struct efx_nic *efx, enum reset_type method)
 
 out:
        /* Leave device stopped if necessary */
-       disabled = rc || method == RESET_TYPE_DISABLE;
+       disabled = rc ||
+               method == RESET_TYPE_DISABLE ||
+               method == RESET_TYPE_RECOVER_OR_DISABLE;
        rc2 = efx_reset_up(efx, method, !disabled);
        if (rc2) {
                disabled = true;
@@ -2328,13 +2358,48 @@ out:
        return rc;
 }
 
+/* Try recovery mechanisms.
+ * For now only EEH is supported.
+ * Returns 0 if the recovery mechanisms are unsuccessful.
+ * Returns a non-zero value otherwise.
+ */
+static int efx_try_recovery(struct efx_nic *efx)
+{
+#ifdef CONFIG_EEH
+       /* A PCI error can occur and not be seen by EEH because nothing
+        * happens on the PCI bus. In this case the driver may fail and
+        * schedule a 'recover or reset', leading to this recovery handler.
+        * Manually call the eeh failure check function.
+        */
+       struct eeh_dev *eehdev =
+               of_node_to_eeh_dev(pci_device_to_OF_node(efx->pci_dev));
+
+       if (eeh_dev_check_failure(eehdev)) {
+               /* The EEH mechanisms will handle the error and reset the
+                * device if necessary.
+                */
+               return 1;
+       }
+#endif
+       return 0;
+}
+
 /* The worker thread exists so that code that cannot sleep can
  * schedule a reset for later.
  */
 static void efx_reset_work(struct work_struct *data)
 {
        struct efx_nic *efx = container_of(data, struct efx_nic, reset_work);
-       unsigned long pending = ACCESS_ONCE(efx->reset_pending);
+       unsigned long pending;
+       enum reset_type method;
+
+       pending = ACCESS_ONCE(efx->reset_pending);
+       method = fls(pending) - 1;
+
+       if ((method == RESET_TYPE_RECOVER_OR_DISABLE ||
+            method == RESET_TYPE_RECOVER_OR_ALL) &&
+           efx_try_recovery(efx))
+               return;
 
        if (!pending)
                return;
@@ -2346,7 +2411,7 @@ static void efx_reset_work(struct work_struct *data)
         * it cannot change again.
         */
        if (efx->state == STATE_READY)
-               (void)efx_reset(efx, fls(pending) - 1);
+               (void)efx_reset(efx, method);
 
        rtnl_unlock();
 }
@@ -2355,11 +2420,20 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
 {
        enum reset_type method;
 
+       if (efx->state == STATE_RECOVERY) {
+               netif_dbg(efx, drv, efx->net_dev,
+                         "recovering: skip scheduling %s reset\n",
+                         RESET_TYPE(type));
+               return;
+       }
+
        switch (type) {
        case RESET_TYPE_INVISIBLE:
        case RESET_TYPE_ALL:
+       case RESET_TYPE_RECOVER_OR_ALL:
        case RESET_TYPE_WORLD:
        case RESET_TYPE_DISABLE:
+       case RESET_TYPE_RECOVER_OR_DISABLE:
                method = type;
                netif_dbg(efx, drv, efx->net_dev, "scheduling %s reset\n",
                          RESET_TYPE(method));
@@ -2569,6 +2643,8 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
        efx_fini_struct(efx);
        pci_set_drvdata(pci_dev, NULL);
        free_netdev(efx->net_dev);
+
+       pci_disable_pcie_error_reporting(pci_dev);
 };
 
 /* NIC VPD information
@@ -2741,6 +2817,11 @@ static int efx_pci_probe(struct pci_dev *pci_dev,
                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);
+
        return 0;
 
  fail4:
@@ -2865,12 +2946,112 @@ static const struct dev_pm_ops efx_pm_ops = {
        .restore        = efx_pm_resume,
 };
 
+/* A PCI error affecting this device was detected.
+ * At this point MMIO and DMA may be disabled.
+ * Stop the software path and request a slot reset.
+ */
+static pci_ers_result_t efx_io_error_detected(struct pci_dev *pdev,
+                                             enum pci_channel_state state)
+{
+       pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
+       struct efx_nic *efx = pci_get_drvdata(pdev);
+
+       if (state == pci_channel_io_perm_failure)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       rtnl_lock();
+
+       if (efx->state != STATE_DISABLED) {
+               efx->state = STATE_RECOVERY;
+               efx->reset_pending = 0;
+
+               efx_device_detach_sync(efx);
+
+               efx_stop_all(efx);
+               efx_stop_interrupts(efx, false);
+
+               status = PCI_ERS_RESULT_NEED_RESET;
+       } else {
+               /* If the interface is disabled we don't want to do anything
+                * with it.
+                */
+               status = PCI_ERS_RESULT_RECOVERED;
+       }
+
+       rtnl_unlock();
+
+       pci_disable_device(pdev);
+
+       return status;
+}
+
+/* Fake a successfull reset, which will be performed later in efx_io_resume. */
+static pci_ers_result_t efx_io_slot_reset(struct pci_dev *pdev)
+{
+       struct efx_nic *efx = pci_get_drvdata(pdev);
+       pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
+       int rc;
+
+       if (pci_enable_device(pdev)) {
+               netif_err(efx, hw, efx->net_dev,
+                         "Cannot re-enable PCI device after reset.\n");
+               status =  PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       rc = pci_cleanup_aer_uncorrect_error_status(pdev);
+       if (rc) {
+               netif_err(efx, hw, efx->net_dev,
+               "pci_cleanup_aer_uncorrect_error_status failed (%d)\n", rc);
+               /* Non-fatal error. Continue. */
+       }
+
+       return status;
+}
+
+/* Perform the actual reset and resume I/O operations. */
+static void efx_io_resume(struct pci_dev *pdev)
+{
+       struct efx_nic *efx = pci_get_drvdata(pdev);
+       int rc;
+
+       rtnl_lock();
+
+       if (efx->state == STATE_DISABLED)
+               goto out;
+
+       rc = efx_reset(efx, RESET_TYPE_ALL);
+       if (rc) {
+               netif_err(efx, hw, efx->net_dev,
+                         "efx_reset failed after PCI error (%d)\n", rc);
+       } else {
+               efx->state = STATE_READY;
+               netif_dbg(efx, hw, efx->net_dev,
+                         "Done resetting and resuming IO after PCI error.\n");
+       }
+
+out:
+       rtnl_unlock();
+}
+
+/* For simplicity and reliability, we always require a slot reset and try to
+ * reset the hardware when a pci error affecting the device is detected.
+ * We leave both the link_reset and mmio_enabled callback unimplemented:
+ * with our request for slot reset the mmio_enabled callback will never be
+ * called, and the link_reset callback is not used by AER or EEH mechanisms.
+ */
+static struct pci_error_handlers efx_err_handlers = {
+       .error_detected = efx_io_error_detected,
+       .slot_reset     = efx_io_slot_reset,
+       .resume         = efx_io_resume,
+};
+
 static struct pci_driver efx_pci_driver = {
        .name           = KBUILD_MODNAME,
        .id_table       = efx_pci_table,
        .probe          = efx_pci_probe,
        .remove         = efx_pci_remove,
        .driver.pm      = &efx_pm_ops,
+       .err_handler    = &efx_err_handlers,
 };
 
 /**************************************************************************
index d2f790df6dcbf6872084c49d74a8a63efb58f569..8372da239b43eef9928fa6d207b60a774a83aa5c 100644 (file)
@@ -33,17 +33,22 @@ extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc);
 extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx);
 
 /* RX */
+extern void efx_rx_config_page_split(struct efx_nic *efx);
 extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
 extern void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
 extern void efx_init_rx_queue(struct efx_rx_queue *rx_queue);
 extern void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
-extern void efx_rx_strategy(struct efx_channel *channel);
 extern void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue);
 extern void efx_rx_slow_fill(unsigned long context);
-extern void __efx_rx_packet(struct efx_channel *channel,
-                           struct efx_rx_buffer *rx_buf);
-extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
+extern void __efx_rx_packet(struct efx_channel *channel);
+extern void efx_rx_packet(struct efx_rx_queue *rx_queue,
+                         unsigned int index, unsigned int n_frags,
                          unsigned int len, u16 flags);
+static inline void efx_rx_flush_packet(struct efx_channel *channel)
+{
+       if (channel->rx_pkt_n_frags)
+               __efx_rx_packet(channel);
+}
 extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
 
 #define EFX_MAX_DMAQ_SIZE 4096UL
@@ -67,6 +72,7 @@ extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
 extern int efx_probe_filters(struct efx_nic *efx);
 extern void efx_restore_filters(struct efx_nic *efx);
 extern void efx_remove_filters(struct efx_nic *efx);
+extern void efx_filter_update_rx_scatter(struct efx_nic *efx);
 extern s32 efx_filter_insert_filter(struct efx_nic *efx,
                                    struct efx_filter_spec *spec,
                                    bool replace);
index 182dbe2cc6e444c410e2209f1cc0e8fdf7360e49..ab8fb5889e55829df9bedbb39cbcc0104bd48242 100644 (file)
@@ -137,8 +137,12 @@ enum efx_loopback_mode {
  * Reset methods are numbered in order of increasing scope.
  *
  * @RESET_TYPE_INVISIBLE: Reset datapath and MAC (Falcon only)
+ * @RESET_TYPE_RECOVER_OR_ALL: Try to recover. Apply RESET_TYPE_ALL
+ * if unsuccessful.
  * @RESET_TYPE_ALL: Reset datapath, MAC and PHY
  * @RESET_TYPE_WORLD: Reset as much as possible
+ * @RESET_TYPE_RECOVER_OR_DISABLE: Try to recover. Apply RESET_TYPE_DISABLE if
+ * unsuccessful.
  * @RESET_TYPE_DISABLE: Reset datapath, MAC and PHY; leave NIC disabled
  * @RESET_TYPE_TX_WATCHDOG: reset due to TX watchdog
  * @RESET_TYPE_INT_ERROR: reset due to internal error
@@ -150,9 +154,11 @@ enum efx_loopback_mode {
  */
 enum reset_type {
        RESET_TYPE_INVISIBLE = 0,
-       RESET_TYPE_ALL = 1,
-       RESET_TYPE_WORLD = 2,
-       RESET_TYPE_DISABLE = 3,
+       RESET_TYPE_RECOVER_OR_ALL = 1,
+       RESET_TYPE_ALL = 2,
+       RESET_TYPE_WORLD = 3,
+       RESET_TYPE_RECOVER_OR_DISABLE = 4,
+       RESET_TYPE_DISABLE = 5,
        RESET_TYPE_MAX_METHOD,
        RESET_TYPE_TX_WATCHDOG,
        RESET_TYPE_INT_ERROR,
index 8e61cd06f66af16a693e5ba47e3b4ef29bf17ddd..6e768175e7e00b6b4864088b094cd45a167782a9 100644 (file)
@@ -154,6 +154,7 @@ static const struct efx_ethtool_stat efx_ethtool_stats[] = {
        EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err),
        EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch),
        EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc),
+       EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_nodesc_trunc),
 };
 
 /* Number of ethtool statistics */
@@ -978,7 +979,8 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
             rule->m_ext.data[1]))
                return -EINVAL;
 
-       efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL, 0,
+       efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL,
+                          efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
                           (rule->ring_cookie == RX_CLS_FLOW_DISC) ?
                           0xfff : rule->ring_cookie);
 
index 49bcd196e10d04b4bac011ba20e652756833c543..4486102fa9b3fd7a06fea65b987f7c6081449c7b 100644 (file)
@@ -1546,10 +1546,6 @@ static int falcon_probe_nic(struct efx_nic *efx)
 
 static void falcon_init_rx_cfg(struct efx_nic *efx)
 {
-       /* Prior to Siena the RX DMA engine will split each frame at
-        * intervals of RX_USR_BUF_SIZE (32-byte units). We set it to
-        * be so large that that never happens. */
-       const unsigned huge_buf_size = (3 * 4096) >> 5;
        /* RX control FIFO thresholds (32 entries) */
        const unsigned ctrl_xon_thr = 20;
        const unsigned ctrl_xoff_thr = 25;
@@ -1557,10 +1553,15 @@ static void falcon_init_rx_cfg(struct efx_nic *efx)
 
        efx_reado(efx, &reg, FR_AZ_RX_CFG);
        if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) {
-               /* Data FIFO size is 5.5K */
+               /* Data FIFO size is 5.5K.  The RX DMA engine only
+                * supports scattering for user-mode queues, but will
+                * split DMA writes at intervals of RX_USR_BUF_SIZE
+                * (32-byte units) even for kernel-mode queues.  We
+                * set it to be so large that that never happens.
+                */
                EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_DESC_PUSH_EN, 0);
                EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_USR_BUF_SIZE,
-                                   huge_buf_size);
+                                   (3 * 4096) >> 5);
                EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_MAC_TH, 512 >> 8);
                EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_MAC_TH, 2048 >> 8);
                EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_TX_TH, ctrl_xon_thr);
@@ -1569,7 +1570,7 @@ static void falcon_init_rx_cfg(struct efx_nic *efx)
                /* Data FIFO size is 80K; register fields moved */
                EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_DESC_PUSH_EN, 0);
                EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_USR_BUF_SIZE,
-                                   huge_buf_size);
+                                   EFX_RX_USR_BUF_SIZE >> 5);
                /* Send XON and XOFF at ~3 * max MTU away from empty/full */
                EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_MAC_TH, 27648 >> 8);
                EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_MAC_TH, 54272 >> 8);
@@ -1815,6 +1816,7 @@ const struct efx_nic_type falcon_a1_nic_type = {
        .evq_rptr_tbl_base = FR_AA_EVQ_RPTR_KER,
        .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH),
        .rx_buffer_padding = 0x24,
+       .can_rx_scatter = false,
        .max_interrupt_mode = EFX_INT_MODE_MSI,
        .phys_addr_channels = 4,
        .timer_period_max =  1 << FRF_AB_TC_TIMER_VAL_WIDTH,
@@ -1865,6 +1867,7 @@ const struct efx_nic_type falcon_b0_nic_type = {
        .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH),
        .rx_buffer_hash_size = 0x10,
        .rx_buffer_padding = 0,
+       .can_rx_scatter = true,
        .max_interrupt_mode = EFX_INT_MODE_MSIX,
        .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy
                                   * interrupt handler only supports 32
index 8af42cd1feda7d7dddbd860b76cdddbf69ff01fa..2397f0e8d3ebf7e50c75e13cfeccc9b3c24237fa 100644 (file)
@@ -66,6 +66,10 @@ struct efx_filter_state {
 #endif
 };
 
+static void efx_filter_table_clear_entry(struct efx_nic *efx,
+                                        struct efx_filter_table *table,
+                                        unsigned int filter_idx);
+
 /* The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit
  * key derived from the n-tuple.  The initial LFSR state is 0xffff. */
 static u16 efx_filter_hash(u32 key)
@@ -168,6 +172,25 @@ static void efx_filter_push_rx_config(struct efx_nic *efx)
                        filter_ctl, FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED,
                        !!(table->spec[EFX_FILTER_INDEX_MC_DEF].flags &
                           EFX_FILTER_FLAG_RX_RSS));
+
+               /* There is a single bit to enable RX scatter for all
+                * unmatched packets.  Only set it if scatter is
+                * enabled in both filter specs.
+                */
+               EFX_SET_OWORD_FIELD(
+                       filter_ctl, FRF_BZ_SCATTER_ENBL_NO_MATCH_Q,
+                       !!(table->spec[EFX_FILTER_INDEX_UC_DEF].flags &
+                          table->spec[EFX_FILTER_INDEX_MC_DEF].flags &
+                          EFX_FILTER_FLAG_RX_SCATTER));
+       } else if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) {
+               /* We don't expose 'default' filters because unmatched
+                * packets always go to the queue number found in the
+                * RSS table.  But we still need to set the RX scatter
+                * bit here.
+                */
+               EFX_SET_OWORD_FIELD(
+                       filter_ctl, FRF_BZ_SCATTER_ENBL_NO_MATCH_Q,
+                       efx->rx_scatter);
        }
 
        efx_writeo(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL);
@@ -409,9 +432,18 @@ static void efx_filter_reset_rx_def(struct efx_nic *efx, unsigned filter_idx)
        struct efx_filter_state *state = efx->filter_state;
        struct efx_filter_table *table = &state->table[EFX_FILTER_TABLE_RX_DEF];
        struct efx_filter_spec *spec = &table->spec[filter_idx];
+       enum efx_filter_flags flags = 0;
+
+       /* If there's only one channel then disable RSS for non VF
+        * traffic, thereby allowing VFs to use RSS when the PF can't.
+        */
+       if (efx->n_rx_channels > 1)
+               flags |= EFX_FILTER_FLAG_RX_RSS;
 
-       efx_filter_init_rx(spec, EFX_FILTER_PRI_MANUAL,
-                          EFX_FILTER_FLAG_RX_RSS, 0);
+       if (efx->rx_scatter)
+               flags |= EFX_FILTER_FLAG_RX_SCATTER;
+
+       efx_filter_init_rx(spec, EFX_FILTER_PRI_MANUAL, flags, 0);
        spec->type = EFX_FILTER_UC_DEF + filter_idx;
        table->used_bitmap[0] |= 1 << filter_idx;
 }
@@ -463,13 +495,6 @@ static u32 efx_filter_build(efx_oword_t *filter, struct efx_filter_spec *spec)
                break;
        }
 
-       case EFX_FILTER_TABLE_RX_DEF:
-               /* One filter spec per type */
-               BUILD_BUG_ON(EFX_FILTER_INDEX_UC_DEF != 0);
-               BUILD_BUG_ON(EFX_FILTER_INDEX_MC_DEF !=
-                            EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF);
-               return spec->type - EFX_FILTER_UC_DEF;
-
        case EFX_FILTER_TABLE_RX_MAC: {
                bool is_wild = spec->type == EFX_FILTER_MAC_WILD;
                EFX_POPULATE_OWORD_7(
@@ -520,42 +545,6 @@ static bool efx_filter_equal(const struct efx_filter_spec *left,
        return true;
 }
 
-static int efx_filter_search(struct efx_filter_table *table,
-                            struct efx_filter_spec *spec, u32 key,
-                            bool for_insert, unsigned int *depth_required)
-{
-       unsigned hash, incr, filter_idx, depth, depth_max;
-
-       hash = efx_filter_hash(key);
-       incr = efx_filter_increment(key);
-
-       filter_idx = hash & (table->size - 1);
-       depth = 1;
-       depth_max = (for_insert ?
-                    (spec->priority <= EFX_FILTER_PRI_HINT ?
-                     FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX) :
-                    table->search_depth[spec->type]);
-
-       for (;;) {
-               /* Return success if entry is used and matches this spec
-                * or entry is unused and we are trying to insert.
-                */
-               if (test_bit(filter_idx, table->used_bitmap) ?
-                   efx_filter_equal(spec, &table->spec[filter_idx]) :
-                   for_insert) {
-                       *depth_required = depth;
-                       return filter_idx;
-               }
-
-               /* Return failure if we reached the maximum search depth */
-               if (depth == depth_max)
-                       return for_insert ? -EBUSY : -ENOENT;
-
-               filter_idx = (filter_idx + incr) & (table->size - 1);
-               ++depth;
-       }
-}
-
 /*
  * Construct/deconstruct external filter IDs.  At least the RX filter
  * IDs must be ordered by matching priority, for RX NFC semantics.
@@ -650,44 +639,111 @@ u32 efx_filter_get_rx_id_limit(struct efx_nic *efx)
  * efx_filter_insert_filter - add or replace a filter
  * @efx: NIC in which to insert the filter
  * @spec: Specification for the filter
- * @replace: Flag for whether the specified filter may replace a filter
- *     with an identical match expression and equal or lower priority
+ * @replace_equal: Flag for whether the specified filter may replace an
+ *     existing filter with equal priority
  *
  * On success, return the filter ID.
  * On failure, return a negative error code.
+ *
+ * If an existing filter has equal match values to the new filter
+ * spec, then the new filter might replace it, depending on the
+ * relative priorities.  If the existing filter has lower priority, or
+ * if @replace_equal is set and it has equal priority, then it is
+ * replaced.  Otherwise the function fails, returning -%EPERM if
+ * the existing filter has higher priority or -%EEXIST if it has
+ * equal priority.
  */
 s32 efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec,
-                            bool replace)
+                            bool replace_equal)
 {
        struct efx_filter_state *state = efx->filter_state;
        struct efx_filter_table *table = efx_filter_spec_table(state, spec);
-       struct efx_filter_spec *saved_spec;
        efx_oword_t filter;
-       unsigned int filter_idx, depth = 0;
-       u32 key;
+       int rep_index, ins_index;
+       unsigned int depth = 0;
        int rc;
 
        if (!table || table->size == 0)
                return -EINVAL;
 
-       key = efx_filter_build(&filter, spec);
-
        netif_vdbg(efx, hw, efx->net_dev,
                   "%s: type %d search_depth=%d", __func__, spec->type,
                   table->search_depth[spec->type]);
 
-       spin_lock_bh(&state->lock);
+       if (table->id == EFX_FILTER_TABLE_RX_DEF) {
+               /* One filter spec per type */
+               BUILD_BUG_ON(EFX_FILTER_INDEX_UC_DEF != 0);
+               BUILD_BUG_ON(EFX_FILTER_INDEX_MC_DEF !=
+                            EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF);
+               rep_index = spec->type - EFX_FILTER_INDEX_UC_DEF;
+               ins_index = rep_index;
 
-       rc = efx_filter_search(table, spec, key, true, &depth);
-       if (rc < 0)
-               goto out;
-       filter_idx = rc;
-       BUG_ON(filter_idx >= table->size);
-       saved_spec = &table->spec[filter_idx];
-
-       if (test_bit(filter_idx, table->used_bitmap)) {
-               /* Should we replace the existing filter? */
-               if (!replace) {
+               spin_lock_bh(&state->lock);
+       } else {
+               /* Search concurrently for
+                * (1) a filter to be replaced (rep_index): any filter
+                *     with the same match values, up to the current
+                *     search depth for this type, and
+                * (2) the insertion point (ins_index): (1) or any
+                *     free slot before it or up to the maximum search
+                *     depth for this priority
+                * We fail if we cannot find (2).
+                *
+                * We can stop once either
+                * (a) we find (1), in which case we have definitely
+                *     found (2) as well; or
+                * (b) we have searched exhaustively for (1), and have
+                *     either found (2) or searched exhaustively for it
+                */
+               u32 key = efx_filter_build(&filter, spec);
+               unsigned int hash = efx_filter_hash(key);
+               unsigned int incr = efx_filter_increment(key);
+               unsigned int max_rep_depth = table->search_depth[spec->type];
+               unsigned int max_ins_depth =
+                       spec->priority <= EFX_FILTER_PRI_HINT ?
+                       FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX;
+               unsigned int i = hash & (table->size - 1);
+
+               ins_index = -1;
+               depth = 1;
+
+               spin_lock_bh(&state->lock);
+
+               for (;;) {
+                       if (!test_bit(i, table->used_bitmap)) {
+                               if (ins_index < 0)
+                                       ins_index = i;
+                       } else if (efx_filter_equal(spec, &table->spec[i])) {
+                               /* Case (a) */
+                               if (ins_index < 0)
+                                       ins_index = i;
+                               rep_index = i;
+                               break;
+                       }
+
+                       if (depth >= max_rep_depth &&
+                           (ins_index >= 0 || depth >= max_ins_depth)) {
+                               /* Case (b) */
+                               if (ins_index < 0) {
+                                       rc = -EBUSY;
+                                       goto out;
+                               }
+                               rep_index = -1;
+                               break;
+                       }
+
+                       i = (i + incr) & (table->size - 1);
+                       ++depth;
+               }
+       }
+
+       /* If we found a filter to be replaced, check whether we
+        * should do so
+        */
+       if (rep_index >= 0) {
+               struct efx_filter_spec *saved_spec = &table->spec[rep_index];
+
+               if (spec->priority == saved_spec->priority && !replace_equal) {
                        rc = -EEXIST;
                        goto out;
                }
@@ -695,11 +751,14 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec,
                        rc = -EPERM;
                        goto out;
                }
-       } else {
-               __set_bit(filter_idx, table->used_bitmap);
+       }
+
+       /* Insert the filter */
+       if (ins_index != rep_index) {
+               __set_bit(ins_index, table->used_bitmap);
                ++table->used;
        }
-       *saved_spec = *spec;
+       table->spec[ins_index] = *spec;
 
        if (table->id == EFX_FILTER_TABLE_RX_DEF) {
                efx_filter_push_rx_config(efx);
@@ -713,13 +772,19 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec,
                }
 
                efx_writeo(efx, &filter,
-                          table->offset + table->step * filter_idx);
+                          table->offset + table->step * ins_index);
+
+               /* If we were able to replace a filter by inserting
+                * at a lower depth, clear the replaced filter
+                */
+               if (ins_index != rep_index && rep_index >= 0)
+                       efx_filter_table_clear_entry(efx, table, rep_index);
        }
 
        netif_vdbg(efx, hw, efx->net_dev,
                   "%s: filter type %d index %d rxq %u set",
-                  __func__, spec->type, filter_idx, spec->dmaq_id);
-       rc = efx_filter_make_id(spec, filter_idx);
+                  __func__, spec->type, ins_index, spec->dmaq_id);
+       rc = efx_filter_make_id(spec, ins_index);
 
 out:
        spin_unlock_bh(&state->lock);
@@ -1060,6 +1125,50 @@ void efx_remove_filters(struct efx_nic *efx)
        kfree(state);
 }
 
+/* Update scatter enable flags for filters pointing to our own RX queues */
+void efx_filter_update_rx_scatter(struct efx_nic *efx)
+{
+       struct efx_filter_state *state = efx->filter_state;
+       enum efx_filter_table_id table_id;
+       struct efx_filter_table *table;
+       efx_oword_t filter;
+       unsigned int filter_idx;
+
+       spin_lock_bh(&state->lock);
+
+       for (table_id = EFX_FILTER_TABLE_RX_IP;
+            table_id <= EFX_FILTER_TABLE_RX_DEF;
+            table_id++) {
+               table = &state->table[table_id];
+
+               for (filter_idx = 0; filter_idx < table->size; filter_idx++) {
+                       if (!test_bit(filter_idx, table->used_bitmap) ||
+                           table->spec[filter_idx].dmaq_id >=
+                           efx->n_rx_channels)
+                               continue;
+
+                       if (efx->rx_scatter)
+                               table->spec[filter_idx].flags |=
+                                       EFX_FILTER_FLAG_RX_SCATTER;
+                       else
+                               table->spec[filter_idx].flags &=
+                                       ~EFX_FILTER_FLAG_RX_SCATTER;
+
+                       if (table_id == EFX_FILTER_TABLE_RX_DEF)
+                               /* Pushed by efx_filter_push_rx_config() */
+                               continue;
+
+                       efx_filter_build(&filter, &table->spec[filter_idx]);
+                       efx_writeo(efx, &filter,
+                                  table->offset + table->step * filter_idx);
+               }
+       }
+
+       efx_filter_push_rx_config(efx);
+
+       spin_unlock_bh(&state->lock);
+}
+
 #ifdef CONFIG_RFS_ACCEL
 
 int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
index 9d426d0457bdd5140709b1da8186d817d5b2bbd2..c5c9747861bac39e73b8b2a576078746403af2a7 100644 (file)
 #define          MC_CMD_PTP_MODE_V1_VLAN 0x1 /* enum */
 #define          MC_CMD_PTP_MODE_V2 0x2 /* enum */
 #define          MC_CMD_PTP_MODE_V2_VLAN 0x3 /* enum */
+#define          MC_CMD_PTP_MODE_V2_ENHANCED 0x4 /* enum */
 
 /* MC_CMD_PTP_IN_DISABLE msgrequest */
 #define    MC_CMD_PTP_IN_DISABLE_LEN 8
index 0a90abd2421b62540d7e2fb2888caa0a4a3abf3a..9bd433a095c57b8a127b24a7ec5e55c60a50b24b 100644 (file)
 #define EFX_TXQ_TYPES          4
 #define EFX_MAX_TX_QUEUES      (EFX_TXQ_TYPES * EFX_MAX_CHANNELS)
 
+/* Maximum possible MTU the driver supports */
+#define EFX_MAX_MTU (9 * 1024)
+
+/* Size of an RX scatter buffer.  Small enough to pack 2 into a 4K page. */
+#define EFX_RX_USR_BUF_SIZE 1824
+
 /* Forward declare Precision Time Protocol (PTP) support structure. */
 struct efx_ptp_data;
 
@@ -206,25 +212,23 @@ struct efx_tx_queue {
 /**
  * struct efx_rx_buffer - An Efx RX data buffer
  * @dma_addr: DMA base address of the buffer
- * @skb: The associated socket buffer. Valid iff !(@flags & %EFX_RX_BUF_PAGE).
- *     Will be %NULL if the buffer slot is currently free.
- * @page: The associated page buffer. Valif iff @flags & %EFX_RX_BUF_PAGE.
+ * @page: The associated page buffer.
  *     Will be %NULL if the buffer slot is currently free.
- * @page_offset: Offset within page. Valid iff @flags & %EFX_RX_BUF_PAGE.
- * @len: Buffer length, in bytes.
- * @flags: Flags for buffer and packet state.
+ * @page_offset: If pending: offset in @page of DMA base address.
+ *     If completed: offset in @page of Ethernet header.
+ * @len: If pending: length for DMA descriptor.
+ *     If completed: received length, excluding hash prefix.
+ * @flags: Flags for buffer and packet state.  These are only set on the
+ *     first buffer of a scattered packet.
  */
 struct efx_rx_buffer {
        dma_addr_t dma_addr;
-       union {
-               struct sk_buff *skb;
-               struct page *page;
-       } u;
+       struct page *page;
        u16 page_offset;
        u16 len;
        u16 flags;
 };
-#define EFX_RX_BUF_PAGE                0x0001
+#define EFX_RX_BUF_LAST_IN_PAGE        0x0001
 #define EFX_RX_PKT_CSUMMED     0x0002
 #define EFX_RX_PKT_DISCARD     0x0004
 
@@ -260,14 +264,23 @@ struct efx_rx_page_state {
  * @added_count: Number of buffers added to the receive queue.
  * @notified_count: Number of buffers given to NIC (<= @added_count).
  * @removed_count: Number of buffers removed from the receive queue.
+ * @scatter_n: Number of buffers used by current packet
+ * @page_ring: The ring to store DMA mapped pages for reuse.
+ * @page_add: Counter to calculate the write pointer for the recycle ring.
+ * @page_remove: Counter to calculate the read pointer for the recycle ring.
+ * @page_recycle_count: The number of pages that have been recycled.
+ * @page_recycle_failed: The number of pages that couldn't be recycled because
+ *      the kernel still held a reference to them.
+ * @page_recycle_full: The number of pages that were released because the
+ *      recycle ring was full.
+ * @page_ptr_mask: The number of pages in the RX recycle ring minus 1.
  * @max_fill: RX descriptor maximum fill level (<= ring size)
  * @fast_fill_trigger: RX descriptor fill level that will trigger a fast fill
  *     (<= @max_fill)
  * @min_fill: RX descriptor minimum non-zero fill level.
  *     This records the minimum fill level observed when a ring
  *     refill was triggered.
- * @alloc_page_count: RX allocation strategy counter.
- * @alloc_skb_count: RX allocation strategy counter.
+ * @recycle_count: RX buffer recycle counter.
  * @slow_fill: Timer used to defer efx_nic_generate_fill_event().
  */
 struct efx_rx_queue {
@@ -279,15 +292,22 @@ struct efx_rx_queue {
        bool enabled;
        bool flush_pending;
 
-       int added_count;
-       int notified_count;
-       int removed_count;
+       unsigned int added_count;
+       unsigned int notified_count;
+       unsigned int removed_count;
+       unsigned int scatter_n;
+       struct page **page_ring;
+       unsigned int page_add;
+       unsigned int page_remove;
+       unsigned int page_recycle_count;
+       unsigned int page_recycle_failed;
+       unsigned int page_recycle_full;
+       unsigned int page_ptr_mask;
        unsigned int max_fill;
        unsigned int fast_fill_trigger;
        unsigned int min_fill;
        unsigned int min_overfill;
-       unsigned int alloc_page_count;
-       unsigned int alloc_skb_count;
+       unsigned int recycle_count;
        struct timer_list slow_fill;
        unsigned int slow_fill_count;
 };
@@ -336,10 +356,6 @@ enum efx_rx_alloc_method {
  * @event_test_cpu: Last CPU to handle interrupt or test event for this channel
  * @irq_count: Number of IRQs since last adaptive moderation decision
  * @irq_mod_score: IRQ moderation score
- * @rx_alloc_level: Watermark based heuristic counter for pushing descriptors
- *     and diagnostic counters
- * @rx_alloc_push_pages: RX allocation method currently in use for pushing
- *     descriptors
  * @n_rx_tobe_disc: Count of RX_TOBE_DISC errors
  * @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors
  * @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors
@@ -347,6 +363,12 @@ enum efx_rx_alloc_method {
  * @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors
  * @n_rx_overlength: Count of RX_OVERLENGTH errors
  * @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun
+ * @n_rx_nodesc_trunc: Number of RX packets truncated and then dropped due to
+ *     lack of descriptors
+ * @rx_pkt_n_frags: Number of fragments in next packet to be delivered by
+ *     __efx_rx_packet(), or zero if there is none
+ * @rx_pkt_index: Ring index of first buffer for next packet to be delivered
+ *     by __efx_rx_packet(), if @rx_pkt_n_frags != 0
  * @rx_queue: RX queue for this channel
  * @tx_queue: TX queues for this channel
  */
@@ -371,9 +393,6 @@ struct efx_channel {
        unsigned int rfs_filters_added;
 #endif
 
-       int rx_alloc_level;
-       int rx_alloc_push_pages;
-
        unsigned n_rx_tobe_disc;
        unsigned n_rx_ip_hdr_chksum_err;
        unsigned n_rx_tcp_udp_chksum_err;
@@ -381,11 +400,10 @@ struct efx_channel {
        unsigned n_rx_frm_trunc;
        unsigned n_rx_overlength;
        unsigned n_skbuff_leaks;
+       unsigned int n_rx_nodesc_trunc;
 
-       /* Used to pipeline received packets in order to optimise memory
-        * access with prefetches.
-        */
-       struct efx_rx_buffer *rx_pkt;
+       unsigned int rx_pkt_n_frags;
+       unsigned int rx_pkt_index;
 
        struct efx_rx_queue rx_queue;
        struct efx_tx_queue tx_queue[EFX_TXQ_TYPES];
@@ -410,7 +428,7 @@ struct efx_channel_type {
        void (*post_remove)(struct efx_channel *);
        void (*get_name)(struct efx_channel *, char *buf, size_t len);
        struct efx_channel *(*copy)(const struct efx_channel *);
-       void (*receive_skb)(struct efx_channel *, struct sk_buff *);
+       bool (*receive_skb)(struct efx_channel *, struct sk_buff *);
        bool keep_eventq;
 };
 
@@ -446,6 +464,7 @@ enum nic_state {
        STATE_UNINIT = 0,       /* device being probed/removed or is frozen */
        STATE_READY = 1,        /* hardware ready and netdev registered */
        STATE_DISABLED = 2,     /* device disabled due to hardware errors */
+       STATE_RECOVERY = 3,     /* device recovering from PCI error */
 };
 
 /*
@@ -684,10 +703,13 @@ struct vfdi_status;
  * @n_channels: Number of channels in use
  * @n_rx_channels: Number of channels used for RX (= number of RX queues)
  * @n_tx_channels: Number of channels used for TX
- * @rx_buffer_len: RX buffer length
+ * @rx_dma_len: Current maximum RX DMA length
  * @rx_buffer_order: Order (log2) of number of pages for each RX buffer
+ * @rx_buffer_truesize: Amortised allocation size of an RX buffer,
+ *     for use in sk_buff::truesize
  * @rx_hash_key: Toeplitz hash key for RSS
  * @rx_indir_table: Indirection table for RSS
+ * @rx_scatter: Scatter mode enabled for receives
  * @int_error_count: Number of internal errors seen recently
  * @int_error_expire: Time at which error count will be expired
  * @irq_status: Interrupt status buffer
@@ -800,10 +822,15 @@ struct efx_nic {
        unsigned rss_spread;
        unsigned tx_channel_offset;
        unsigned n_tx_channels;
-       unsigned int rx_buffer_len;
+       unsigned int rx_dma_len;
        unsigned int rx_buffer_order;
+       unsigned int rx_buffer_truesize;
+       unsigned int rx_page_buf_step;
+       unsigned int rx_bufs_per_page;
+       unsigned int rx_pages_per_batch;
        u8 rx_hash_key[40];
        u32 rx_indir_table[128];
+       bool rx_scatter;
 
        unsigned int_error_count;
        unsigned long int_error_expire;
@@ -934,8 +961,9 @@ static inline unsigned int efx_port_num(struct efx_nic *efx)
  * @evq_ptr_tbl_base: Event queue pointer table base address
  * @evq_rptr_tbl_base: Event queue read-pointer table base address
  * @max_dma_mask: Maximum possible DMA mask
- * @rx_buffer_hash_size: Size of hash at start of RX buffer
- * @rx_buffer_padding: Size of padding at end of RX buffer
+ * @rx_buffer_hash_size: Size of hash at start of RX packet
+ * @rx_buffer_padding: Size of padding at end of RX packet
+ * @can_rx_scatter: NIC is able to scatter packet to multiple buffers
  * @max_interrupt_mode: Highest capability interrupt mode supported
  *     from &enum efx_init_mode.
  * @phys_addr_channels: Number of channels with physically addressed
@@ -983,6 +1011,7 @@ struct efx_nic_type {
        u64 max_dma_mask;
        unsigned int rx_buffer_hash_size;
        unsigned int rx_buffer_padding;
+       bool can_rx_scatter;
        unsigned int max_interrupt_mode;
        unsigned int phys_addr_channels;
        unsigned int timer_period_max;
index eaa8e874a3cb0580cbe68ca40ba5004942a91e5f..b0503cd8c2a0807ad8becde51644d7054b56896d 100644 (file)
@@ -305,11 +305,11 @@ int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer,
                         unsigned int len)
 {
        buffer->addr = dma_alloc_coherent(&efx->pci_dev->dev, len,
-                                         &buffer->dma_addr, GFP_ATOMIC);
+                                         &buffer->dma_addr,
+                                         GFP_ATOMIC | __GFP_ZERO);
        if (!buffer->addr)
                return -ENOMEM;
        buffer->len = len;
-       memset(buffer->addr, 0, len);
        return 0;
 }
 
@@ -592,12 +592,22 @@ void efx_nic_init_rx(struct efx_rx_queue *rx_queue)
        struct efx_nic *efx = rx_queue->efx;
        bool is_b0 = efx_nic_rev(efx) >= EFX_REV_FALCON_B0;
        bool iscsi_digest_en = is_b0;
+       bool jumbo_en;
+
+       /* For kernel-mode queues in Falcon A1, the JUMBO flag enables
+        * DMA to continue after a PCIe page boundary (and scattering
+        * is not possible).  In Falcon B0 and Siena, it enables
+        * scatter.
+        */
+       jumbo_en = !is_b0 || efx->rx_scatter;
 
        netif_dbg(efx, hw, efx->net_dev,
                  "RX queue %d ring in special buffers %d-%d\n",
                  efx_rx_queue_index(rx_queue), rx_queue->rxd.index,
                  rx_queue->rxd.index + rx_queue->rxd.entries - 1);
 
+       rx_queue->scatter_n = 0;
+
        /* Pin RX descriptor ring */
        efx_init_special_buffer(efx, &rx_queue->rxd);
 
@@ -614,8 +624,7 @@ void efx_nic_init_rx(struct efx_rx_queue *rx_queue)
                              FRF_AZ_RX_DESCQ_SIZE,
                              __ffs(rx_queue->rxd.entries),
                              FRF_AZ_RX_DESCQ_TYPE, 0 /* kernel queue */ ,
-                             /* For >=B0 this is scatter so disable */
-                             FRF_AZ_RX_DESCQ_JUMBO, !is_b0,
+                             FRF_AZ_RX_DESCQ_JUMBO, jumbo_en,
                              FRF_AZ_RX_DESCQ_EN, 1);
        efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base,
                         efx_rx_queue_index(rx_queue));
@@ -969,13 +978,24 @@ static u16 efx_handle_rx_not_ok(struct efx_rx_queue *rx_queue,
                EFX_RX_PKT_DISCARD : 0;
 }
 
-/* Handle receive events that are not in-order. */
-static void
+/* Handle receive events that are not in-order. Return true if this
+ * can be handled as a partial packet discard, false if it's more
+ * serious.
+ */
+static bool
 efx_handle_rx_bad_index(struct efx_rx_queue *rx_queue, unsigned index)
 {
+       struct efx_channel *channel = efx_rx_queue_channel(rx_queue);
        struct efx_nic *efx = rx_queue->efx;
        unsigned expected, dropped;
 
+       if (rx_queue->scatter_n &&
+           index == ((rx_queue->removed_count + rx_queue->scatter_n - 1) &
+                     rx_queue->ptr_mask)) {
+               ++channel->n_rx_nodesc_trunc;
+               return true;
+       }
+
        expected = rx_queue->removed_count & rx_queue->ptr_mask;
        dropped = (index - expected) & rx_queue->ptr_mask;
        netif_info(efx, rx_err, efx->net_dev,
@@ -984,6 +1004,7 @@ efx_handle_rx_bad_index(struct efx_rx_queue *rx_queue, unsigned index)
 
        efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ?
                           RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE);
+       return false;
 }
 
 /* Handle a packet received event
@@ -999,7 +1020,7 @@ efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event)
        unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt;
        unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt;
        unsigned expected_ptr;
-       bool rx_ev_pkt_ok;
+       bool rx_ev_pkt_ok, rx_ev_sop, rx_ev_cont;
        u16 flags;
        struct efx_rx_queue *rx_queue;
        struct efx_nic *efx = channel->efx;
@@ -1007,21 +1028,56 @@ efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event)
        if (unlikely(ACCESS_ONCE(efx->reset_pending)))
                return;
 
-       /* Basic packet information */
-       rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_BYTE_CNT);
-       rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_OK);
-       rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE);
-       WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_JUMBO_CONT));
-       WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_SOP) != 1);
+       rx_ev_cont = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_JUMBO_CONT);
+       rx_ev_sop = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_SOP);
        WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_Q_LABEL) !=
                channel->channel);
 
        rx_queue = efx_channel_get_rx_queue(channel);
 
        rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_DESC_PTR);
-       expected_ptr = rx_queue->removed_count & rx_queue->ptr_mask;
-       if (unlikely(rx_ev_desc_ptr != expected_ptr))
-               efx_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr);
+       expected_ptr = ((rx_queue->removed_count + rx_queue->scatter_n) &
+                       rx_queue->ptr_mask);
+
+       /* Check for partial drops and other errors */
+       if (unlikely(rx_ev_desc_ptr != expected_ptr) ||
+           unlikely(rx_ev_sop != (rx_queue->scatter_n == 0))) {
+               if (rx_ev_desc_ptr != expected_ptr &&
+                   !efx_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr))
+                       return;
+
+               /* Discard all pending fragments */
+               if (rx_queue->scatter_n) {
+                       efx_rx_packet(
+                               rx_queue,
+                               rx_queue->removed_count & rx_queue->ptr_mask,
+                               rx_queue->scatter_n, 0, EFX_RX_PKT_DISCARD);
+                       rx_queue->removed_count += rx_queue->scatter_n;
+                       rx_queue->scatter_n = 0;
+               }
+
+               /* Return if there is no new fragment */
+               if (rx_ev_desc_ptr != expected_ptr)
+                       return;
+
+               /* Discard new fragment if not SOP */
+               if (!rx_ev_sop) {
+                       efx_rx_packet(
+                               rx_queue,
+                               rx_queue->removed_count & rx_queue->ptr_mask,
+                               1, 0, EFX_RX_PKT_DISCARD);
+                       ++rx_queue->removed_count;
+                       return;
+               }
+       }
+
+       ++rx_queue->scatter_n;
+       if (rx_ev_cont)
+               return;
+
+       rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_BYTE_CNT);
+       rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_OK);
+       rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE);
 
        if (likely(rx_ev_pkt_ok)) {
                /* If packet is marked as OK and packet type is TCP/IP or
@@ -1049,7 +1105,11 @@ efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event)
        channel->irq_mod_score += 2;
 
        /* Handle received packet */
-       efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, flags);
+       efx_rx_packet(rx_queue,
+                     rx_queue->removed_count & rx_queue->ptr_mask,
+                     rx_queue->scatter_n, rx_ev_byte_cnt, flags);
+       rx_queue->removed_count += rx_queue->scatter_n;
+       rx_queue->scatter_n = 0;
 }
 
 /* If this flush done event corresponds to a &struct efx_tx_queue, then
index 3f93624fc27313134fa0682bc67cf154d8af0a31..07f6baa15c0cd4cc075130d45130729eb77c7d1b 100644 (file)
@@ -99,6 +99,9 @@
 #define PTP_V2_VERSION_LENGTH  1
 #define PTP_V2_VERSION_OFFSET  29
 
+#define PTP_V2_UUID_LENGTH     8
+#define PTP_V2_UUID_OFFSET     48
+
 /* Although PTP V2 UUIDs are comprised a ClockIdentity (8) and PortNumber (2),
  * the MC only captures the last six bytes of the clock identity. These values
  * reflect those, not the ones used in the standard.  The standard permits
@@ -429,13 +432,10 @@ static int efx_ptp_process_times(struct efx_nic *efx, u8 *synch_buf,
        unsigned number_readings = (response_length /
                               MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_LEN);
        unsigned i;
-       unsigned min;
-       unsigned min_set = 0;
        unsigned total;
        unsigned ngood = 0;
        unsigned last_good = 0;
        struct efx_ptp_data *ptp = efx->ptp_data;
-       bool min_valid = false;
        u32 last_sec;
        u32 start_sec;
        struct timespec delta;
@@ -443,35 +443,17 @@ static int efx_ptp_process_times(struct efx_nic *efx, u8 *synch_buf,
        if (number_readings == 0)
                return -EAGAIN;
 
-       /* Find minimum value in this set of results, discarding clearly
-        * erroneous results.
+       /* Read the set of results and increment stats for any results that
+        * appera to be erroneous.
         */
        for (i = 0; i < number_readings; i++) {
                efx_ptp_read_timeset(synch_buf, &ptp->timeset[i]);
                synch_buf += MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_LEN;
-               if (ptp->timeset[i].window > SYNCHRONISATION_GRANULARITY_NS) {
-                       if (min_valid) {
-                               if (ptp->timeset[i].window < min_set)
-                                       min_set = ptp->timeset[i].window;
-                       } else {
-                               min_valid = true;
-                               min_set = ptp->timeset[i].window;
-                       }
-               }
-       }
-
-       if (min_valid) {
-               if (ptp->base_sync_valid && (min_set > ptp->base_sync_ns))
-                       min = ptp->base_sync_ns;
-               else
-                       min = min_set;
-       } else {
-               min = SYNCHRONISATION_GRANULARITY_NS;
        }
 
-       /* Discard excessively long synchronise durations.  The MC times
-        * when it finishes reading the host time so the corrected window
-        * time should be fairly constant for a given platform.
+       /* Find the last good host-MC synchronization result. The MC times
+        * when it finishes reading the host time so the corrected window time
+        * should be fairly constant for a given platform.
         */
        total = 0;
        for (i = 0; i < number_readings; i++)
@@ -489,8 +471,8 @@ static int efx_ptp_process_times(struct efx_nic *efx, u8 *synch_buf,
 
        if (ngood == 0) {
                netif_warn(efx, drv, efx->net_dev,
-                          "PTP no suitable synchronisations %dns %dns\n",
-                          ptp->base_sync_ns, min_set);
+                          "PTP no suitable synchronisations %dns\n",
+                          ptp->base_sync_ns);
                return -EAGAIN;
        }
 
@@ -1006,43 +988,53 @@ bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb)
  * the receive timestamp from the MC - this will probably occur after the
  * packet arrival because of the processing in the MC.
  */
-static void efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
+static bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
 {
        struct efx_nic *efx = channel->efx;
        struct efx_ptp_data *ptp = efx->ptp_data;
        struct efx_ptp_match *match = (struct efx_ptp_match *)skb->cb;
-       u8 *data;
+       u8 *match_data_012, *match_data_345;
        unsigned int version;
 
        match->expiry = jiffies + msecs_to_jiffies(PKT_EVENT_LIFETIME_MS);
 
        /* Correct version? */
        if (ptp->mode == MC_CMD_PTP_MODE_V1) {
-               if (skb->len < PTP_V1_MIN_LENGTH) {
-                       netif_receive_skb(skb);
-                       return;
+               if (!pskb_may_pull(skb, PTP_V1_MIN_LENGTH)) {
+                       return false;
                }
                version = ntohs(*(__be16 *)&skb->data[PTP_V1_VERSION_OFFSET]);
                if (version != PTP_VERSION_V1) {
-                       netif_receive_skb(skb);
-                       return;
+                       return false;
                }
+
+               /* PTP V1 uses all six bytes of the UUID to match the packet
+                * to the timestamp
+                */
+               match_data_012 = skb->data + PTP_V1_UUID_OFFSET;
+               match_data_345 = skb->data + PTP_V1_UUID_OFFSET + 3;
        } else {
-               if (skb->len < PTP_V2_MIN_LENGTH) {
-                       netif_receive_skb(skb);
-                       return;
+               if (!pskb_may_pull(skb, PTP_V2_MIN_LENGTH)) {
+                       return false;
                }
                version = skb->data[PTP_V2_VERSION_OFFSET];
-
-               BUG_ON(ptp->mode != MC_CMD_PTP_MODE_V2);
-               BUILD_BUG_ON(PTP_V1_UUID_OFFSET != PTP_V2_MC_UUID_OFFSET);
-               BUILD_BUG_ON(PTP_V1_UUID_LENGTH != PTP_V2_MC_UUID_LENGTH);
-               BUILD_BUG_ON(PTP_V1_SEQUENCE_OFFSET != PTP_V2_SEQUENCE_OFFSET);
-               BUILD_BUG_ON(PTP_V1_SEQUENCE_LENGTH != PTP_V2_SEQUENCE_LENGTH);
-
                if ((version & PTP_VERSION_V2_MASK) != PTP_VERSION_V2) {
-                       netif_receive_skb(skb);
-                       return;
+                       return false;
+               }
+
+               /* The original V2 implementation uses bytes 2-7 of
+                * the UUID to match the packet to the timestamp. This
+                * discards two of the bytes of the MAC address used
+                * to create the UUID (SF bug 33070).  The PTP V2
+                * enhanced mode fixes this issue and uses bytes 0-2
+                * and byte 5-7 of the UUID.
+                */
+               match_data_345 = skb->data + PTP_V2_UUID_OFFSET + 5;
+               if (ptp->mode == MC_CMD_PTP_MODE_V2) {
+                       match_data_012 = skb->data + PTP_V2_UUID_OFFSET + 2;
+               } else {
+                       match_data_012 = skb->data + PTP_V2_UUID_OFFSET + 0;
+                       BUG_ON(ptp->mode != MC_CMD_PTP_MODE_V2_ENHANCED);
                }
        }
 
@@ -1056,14 +1048,19 @@ static void efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
                timestamps = skb_hwtstamps(skb);
                memset(timestamps, 0, sizeof(*timestamps));
 
+               /* We expect the sequence number to be in the same position in
+                * the packet for PTP V1 and V2
+                */
+               BUILD_BUG_ON(PTP_V1_SEQUENCE_OFFSET != PTP_V2_SEQUENCE_OFFSET);
+               BUILD_BUG_ON(PTP_V1_SEQUENCE_LENGTH != PTP_V2_SEQUENCE_LENGTH);
+
                /* Extract UUID/Sequence information */
-               data = skb->data + PTP_V1_UUID_OFFSET;
-               match->words[0] = (data[0]         |
-                                  (data[1] << 8)  |
-                                  (data[2] << 16) |
-                                  (data[3] << 24));
-               match->words[1] = (data[4]         |
-                                  (data[5] << 8)  |
+               match->words[0] = (match_data_012[0]         |
+                                  (match_data_012[1] << 8)  |
+                                  (match_data_012[2] << 16) |
+                                  (match_data_345[0] << 24));
+               match->words[1] = (match_data_345[1]         |
+                                  (match_data_345[2] << 8)  |
                                   (skb->data[PTP_V1_SEQUENCE_OFFSET +
                                              PTP_V1_SEQUENCE_LENGTH - 1] <<
                                    16));
@@ -1073,6 +1070,8 @@ static void efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
 
        skb_queue_tail(&ptp->rxq, skb);
        queue_work(ptp->workwq, &ptp->work);
+
+       return true;
 }
 
 /* Transmit a PTP packet.  This has to be transmitted by the MC
@@ -1167,7 +1166,7 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
         * timestamped
         */
                init->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
-               new_mode = MC_CMD_PTP_MODE_V2;
+               new_mode = MC_CMD_PTP_MODE_V2_ENHANCED;
                enable_wanted = true;
                break;
        case HWTSTAMP_FILTER_PTP_V2_EVENT:
@@ -1186,7 +1185,14 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
        if (init->tx_type != HWTSTAMP_TX_OFF)
                enable_wanted = true;
 
+       /* Old versions of the firmware do not support the improved
+        * UUID filtering option (SF bug 33070).  If the firmware does
+        * not accept the enhanced mode, fall back to the standard PTP
+        * v2 UUID filtering.
+        */
        rc = efx_ptp_change_mode(efx, enable_wanted, new_mode);
+       if ((rc != 0) && (new_mode == MC_CMD_PTP_MODE_V2_ENHANCED))
+               rc = efx_ptp_change_mode(efx, enable_wanted, MC_CMD_PTP_MODE_V2);
        if (rc != 0)
                return rc;
 
index bb579a6128c8ce883ac7156ea24ac541d4901e38..e73e30bac10e268a6f031d717a3551cda1ddeeae 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/udp.h>
 #include <linux/prefetch.h>
 #include <linux/moduleparam.h>
+#include <linux/iommu.h>
 #include <net/ip.h>
 #include <net/checksum.h>
 #include "net_driver.h"
 #include "selftest.h"
 #include "workarounds.h"
 
-/* Number of RX descriptors pushed at once. */
-#define EFX_RX_BATCH  8
+/* Preferred number of descriptors to fill at once */
+#define EFX_RX_PREFERRED_BATCH 8U
 
-/* Maximum size of a buffer sharing a page */
-#define EFX_RX_HALF_PAGE ((PAGE_SIZE >> 1) - sizeof(struct efx_rx_page_state))
+/* Number of RX buffers to recycle pages for.  When creating the RX page recycle
+ * ring, this number is divided by the number of buffers per page to calculate
+ * the number of pages to store in the RX page recycle ring.
+ */
+#define EFX_RECYCLE_RING_SIZE_IOMMU 4096
+#define EFX_RECYCLE_RING_SIZE_NOIOMMU (2 * EFX_RX_PREFERRED_BATCH)
 
 /* Size of buffer allocated for skb header area. */
 #define EFX_SKB_HEADERS  64u
 
-/*
- * rx_alloc_method - RX buffer allocation method
- *
- * This driver supports two methods for allocating and using RX buffers:
- * each RX buffer may be backed by an skb or by an order-n page.
- *
- * When GRO is in use then the second method has a lower overhead,
- * since we don't have to allocate then free skbs on reassembled frames.
- *
- * Values:
- *   - RX_ALLOC_METHOD_AUTO = 0
- *   - RX_ALLOC_METHOD_SKB  = 1
- *   - RX_ALLOC_METHOD_PAGE = 2
- *
- * The heuristic for %RX_ALLOC_METHOD_AUTO is a simple hysteresis count
- * controlled by the parameters below.
- *
- *   - Since pushing and popping descriptors are separated by the rx_queue
- *     size, so the watermarks should be ~rxd_size.
- *   - The performance win by using page-based allocation for GRO is less
- *     than the performance hit of using page-based allocation of non-GRO,
- *     so the watermarks should reflect this.
- *
- * Per channel we maintain a single variable, updated by each channel:
- *
- *   rx_alloc_level += (gro_performed ? RX_ALLOC_FACTOR_GRO :
- *                      RX_ALLOC_FACTOR_SKB)
- * Per NAPI poll interval, we constrain rx_alloc_level to 0..MAX (which
- * limits the hysteresis), and update the allocation strategy:
- *
- *   rx_alloc_method = (rx_alloc_level > RX_ALLOC_LEVEL_GRO ?
- *                      RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB)
- */
-static int rx_alloc_method = RX_ALLOC_METHOD_AUTO;
-
-#define RX_ALLOC_LEVEL_GRO 0x2000
-#define RX_ALLOC_LEVEL_MAX 0x3000
-#define RX_ALLOC_FACTOR_GRO 1
-#define RX_ALLOC_FACTOR_SKB (-2)
-
 /* This is the percentage fill level below which new RX descriptors
  * will be added to the RX descriptor ring.
  */
 static unsigned int rx_refill_threshold;
 
+/* Each packet can consume up to ceil(max_frame_len / buffer_size) buffers */
+#define EFX_RX_MAX_FRAGS DIV_ROUND_UP(EFX_MAX_FRAME_LEN(EFX_MAX_MTU), \
+                                     EFX_RX_USR_BUF_SIZE)
+
 /*
  * RX maximum head room required.
  *
- * This must be at least 1 to prevent overflow and at least 2 to allow
- * pipelined receives.
+ * This must be at least 1 to prevent overflow, plus one packet-worth
+ * to allow pipelined receives.
  */
-#define EFX_RXD_HEAD_ROOM 2
+#define EFX_RXD_HEAD_ROOM (1 + EFX_RX_MAX_FRAGS)
 
-/* Offset of ethernet header within page */
-static inline unsigned int efx_rx_buf_offset(struct efx_nic *efx,
-                                            struct efx_rx_buffer *buf)
+static inline u8 *efx_rx_buf_va(struct efx_rx_buffer *buf)
 {
-       return buf->page_offset + efx->type->rx_buffer_hash_size;
-}
-static inline unsigned int efx_rx_buf_size(struct efx_nic *efx)
-{
-       return PAGE_SIZE << efx->rx_buffer_order;
-}
-
-static u8 *efx_rx_buf_eh(struct efx_nic *efx, struct efx_rx_buffer *buf)
-{
-       if (buf->flags & EFX_RX_BUF_PAGE)
-               return page_address(buf->u.page) + efx_rx_buf_offset(efx, buf);
-       else
-               return (u8 *)buf->u.skb->data + efx->type->rx_buffer_hash_size;
+       return page_address(buf->page) + buf->page_offset;
 }
 
 static inline u32 efx_rx_buf_hash(const u8 *eh)
@@ -119,66 +74,81 @@ static inline u32 efx_rx_buf_hash(const u8 *eh)
 #endif
 }
 
-/**
- * efx_init_rx_buffers_skb - create EFX_RX_BATCH skb-based RX buffers
- *
- * @rx_queue:          Efx RX queue
- *
- * This allocates EFX_RX_BATCH skbs, maps them for DMA, and populates a
- * struct efx_rx_buffer for each one. Return a negative error code or 0
- * on success. May fail having only inserted fewer than EFX_RX_BATCH
- * buffers.
- */
-static int efx_init_rx_buffers_skb(struct efx_rx_queue *rx_queue)
+static inline struct efx_rx_buffer *
+efx_rx_buf_next(struct efx_rx_queue *rx_queue, struct efx_rx_buffer *rx_buf)
+{
+       if (unlikely(rx_buf == efx_rx_buffer(rx_queue, rx_queue->ptr_mask)))
+               return efx_rx_buffer(rx_queue, 0);
+       else
+               return rx_buf + 1;
+}
+
+static inline void efx_sync_rx_buffer(struct efx_nic *efx,
+                                     struct efx_rx_buffer *rx_buf,
+                                     unsigned int len)
+{
+       dma_sync_single_for_cpu(&efx->pci_dev->dev, rx_buf->dma_addr, len,
+                               DMA_FROM_DEVICE);
+}
+
+void efx_rx_config_page_split(struct efx_nic *efx)
+{
+       efx->rx_page_buf_step = ALIGN(efx->rx_dma_len + EFX_PAGE_IP_ALIGN,
+                                     L1_CACHE_BYTES);
+       efx->rx_bufs_per_page = efx->rx_buffer_order ? 1 :
+               ((PAGE_SIZE - sizeof(struct efx_rx_page_state)) /
+                efx->rx_page_buf_step);
+       efx->rx_buffer_truesize = (PAGE_SIZE << efx->rx_buffer_order) /
+               efx->rx_bufs_per_page;
+       efx->rx_pages_per_batch = DIV_ROUND_UP(EFX_RX_PREFERRED_BATCH,
+                                              efx->rx_bufs_per_page);
+}
+
+/* Check the RX page recycle ring for a page that can be reused. */
+static struct page *efx_reuse_page(struct efx_rx_queue *rx_queue)
 {
        struct efx_nic *efx = rx_queue->efx;
-       struct net_device *net_dev = efx->net_dev;
-       struct efx_rx_buffer *rx_buf;
-       struct sk_buff *skb;
-       int skb_len = efx->rx_buffer_len;
-       unsigned index, count;
+       struct page *page;
+       struct efx_rx_page_state *state;
+       unsigned index;
 
-       for (count = 0; count < EFX_RX_BATCH; ++count) {
-               index = rx_queue->added_count & rx_queue->ptr_mask;
-               rx_buf = efx_rx_buffer(rx_queue, index);
-
-               rx_buf->u.skb = skb = netdev_alloc_skb(net_dev, skb_len);
-               if (unlikely(!skb))
-                       return -ENOMEM;
-
-               /* Adjust the SKB for padding */
-               skb_reserve(skb, NET_IP_ALIGN);
-               rx_buf->len = skb_len - NET_IP_ALIGN;
-               rx_buf->flags = 0;
-
-               rx_buf->dma_addr = dma_map_single(&efx->pci_dev->dev,
-                                                 skb->data, rx_buf->len,
-                                                 DMA_FROM_DEVICE);
-               if (unlikely(dma_mapping_error(&efx->pci_dev->dev,
-                                              rx_buf->dma_addr))) {
-                       dev_kfree_skb_any(skb);
-                       rx_buf->u.skb = NULL;
-                       return -EIO;
-               }
+       index = rx_queue->page_remove & rx_queue->page_ptr_mask;
+       page = rx_queue->page_ring[index];
+       if (page == NULL)
+               return NULL;
+
+       rx_queue->page_ring[index] = NULL;
+       /* page_remove cannot exceed page_add. */
+       if (rx_queue->page_remove != rx_queue->page_add)
+               ++rx_queue->page_remove;
 
-               ++rx_queue->added_count;
-               ++rx_queue->alloc_skb_count;
+       /* If page_count is 1 then we hold the only reference to this page. */
+       if (page_count(page) == 1) {
+               ++rx_queue->page_recycle_count;
+               return page;
+       } else {
+               state = page_address(page);
+               dma_unmap_page(&efx->pci_dev->dev, state->dma_addr,
+                              PAGE_SIZE << efx->rx_buffer_order,
+                              DMA_FROM_DEVICE);
+               put_page(page);
+               ++rx_queue->page_recycle_failed;
        }
 
-       return 0;
+       return NULL;
 }
 
 /**
- * efx_init_rx_buffers_page - create EFX_RX_BATCH page-based RX buffers
+ * efx_init_rx_buffers - create EFX_RX_BATCH page-based RX buffers
  *
  * @rx_queue:          Efx RX queue
  *
- * This allocates memory for EFX_RX_BATCH receive buffers, maps them for DMA,
- * and populates struct efx_rx_buffers for each one. Return a negative error
- * code or 0 on success. If a single page can be split between two buffers,
- * then the page will either be inserted fully, or not at at all.
+ * This allocates a batch of pages, maps them for DMA, and populates
+ * struct efx_rx_buffers for each one. Return a negative error code or
+ * 0 on success. If a single page can be used for multiple buffers,
+ * then the page will either be inserted fully, or not at all.
  */
-static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue)
+static int efx_init_rx_buffers(struct efx_rx_queue *rx_queue)
 {
        struct efx_nic *efx = rx_queue->efx;
        struct efx_rx_buffer *rx_buf;
@@ -188,150 +158,140 @@ static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue)
        dma_addr_t dma_addr;
        unsigned index, count;
 
-       /* We can split a page between two buffers */
-       BUILD_BUG_ON(EFX_RX_BATCH & 1);
-
-       for (count = 0; count < EFX_RX_BATCH; ++count) {
-               page = alloc_pages(__GFP_COLD | __GFP_COMP | GFP_ATOMIC,
-                                  efx->rx_buffer_order);
-               if (unlikely(page == NULL))
-                       return -ENOMEM;
-               dma_addr = dma_map_page(&efx->pci_dev->dev, page, 0,
-                                       efx_rx_buf_size(efx),
-                                       DMA_FROM_DEVICE);
-               if (unlikely(dma_mapping_error(&efx->pci_dev->dev, dma_addr))) {
-                       __free_pages(page, efx->rx_buffer_order);
-                       return -EIO;
+       count = 0;
+       do {
+               page = efx_reuse_page(rx_queue);
+               if (page == NULL) {
+                       page = alloc_pages(__GFP_COLD | __GFP_COMP | GFP_ATOMIC,
+                                          efx->rx_buffer_order);
+                       if (unlikely(page == NULL))
+                               return -ENOMEM;
+                       dma_addr =
+                               dma_map_page(&efx->pci_dev->dev, page, 0,
+                                            PAGE_SIZE << efx->rx_buffer_order,
+                                            DMA_FROM_DEVICE);
+                       if (unlikely(dma_mapping_error(&efx->pci_dev->dev,
+                                                      dma_addr))) {
+                               __free_pages(page, efx->rx_buffer_order);
+                               return -EIO;
+                       }
+                       state = page_address(page);
+                       state->dma_addr = dma_addr;
+               } else {
+                       state = page_address(page);
+                       dma_addr = state->dma_addr;
                }
-               state = page_address(page);
-               state->refcnt = 0;
-               state->dma_addr = dma_addr;
 
                dma_addr += sizeof(struct efx_rx_page_state);
                page_offset = sizeof(struct efx_rx_page_state);
 
-       split:
-               index = rx_queue->added_count & rx_queue->ptr_mask;
-               rx_buf = efx_rx_buffer(rx_queue, index);
-               rx_buf->dma_addr = dma_addr + EFX_PAGE_IP_ALIGN;
-               rx_buf->u.page = page;
-               rx_buf->page_offset = page_offset + EFX_PAGE_IP_ALIGN;
-               rx_buf->len = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN;
-               rx_buf->flags = EFX_RX_BUF_PAGE;
-               ++rx_queue->added_count;
-               ++rx_queue->alloc_page_count;
-               ++state->refcnt;
-
-               if ((~count & 1) && (efx->rx_buffer_len <= EFX_RX_HALF_PAGE)) {
-                       /* Use the second half of the page */
+               do {
+                       index = rx_queue->added_count & rx_queue->ptr_mask;
+                       rx_buf = efx_rx_buffer(rx_queue, index);
+                       rx_buf->dma_addr = dma_addr + EFX_PAGE_IP_ALIGN;
+                       rx_buf->page = page;
+                       rx_buf->page_offset = page_offset + EFX_PAGE_IP_ALIGN;
+                       rx_buf->len = efx->rx_dma_len;
+                       rx_buf->flags = 0;
+                       ++rx_queue->added_count;
                        get_page(page);
-                       dma_addr += (PAGE_SIZE >> 1);
-                       page_offset += (PAGE_SIZE >> 1);
-                       ++count;
-                       goto split;
-               }
-       }
+                       dma_addr += efx->rx_page_buf_step;
+                       page_offset += efx->rx_page_buf_step;
+               } while (page_offset + efx->rx_page_buf_step <= PAGE_SIZE);
+
+               rx_buf->flags = EFX_RX_BUF_LAST_IN_PAGE;
+       } while (++count < efx->rx_pages_per_batch);
 
        return 0;
 }
 
+/* Unmap a DMA-mapped page.  This function is only called for the final RX
+ * buffer in a page.
+ */
 static void efx_unmap_rx_buffer(struct efx_nic *efx,
-                               struct efx_rx_buffer *rx_buf,
-                               unsigned int used_len)
+                               struct efx_rx_buffer *rx_buf)
 {
-       if ((rx_buf->flags & EFX_RX_BUF_PAGE) && rx_buf->u.page) {
-               struct efx_rx_page_state *state;
-
-               state = page_address(rx_buf->u.page);
-               if (--state->refcnt == 0) {
-                       dma_unmap_page(&efx->pci_dev->dev,
-                                      state->dma_addr,
-                                      efx_rx_buf_size(efx),
-                                      DMA_FROM_DEVICE);
-               } else if (used_len) {
-                       dma_sync_single_for_cpu(&efx->pci_dev->dev,
-                                               rx_buf->dma_addr, used_len,
-                                               DMA_FROM_DEVICE);
-               }
-       } else if (!(rx_buf->flags & EFX_RX_BUF_PAGE) && rx_buf->u.skb) {
-               dma_unmap_single(&efx->pci_dev->dev, rx_buf->dma_addr,
-                                rx_buf->len, DMA_FROM_DEVICE);
+       struct page *page = rx_buf->page;
+
+       if (page) {
+               struct efx_rx_page_state *state = page_address(page);
+               dma_unmap_page(&efx->pci_dev->dev,
+                              state->dma_addr,
+                              PAGE_SIZE << efx->rx_buffer_order,
+                              DMA_FROM_DEVICE);
        }
 }
 
-static void efx_free_rx_buffer(struct efx_nic *efx,
-                              struct efx_rx_buffer *rx_buf)
+static void efx_free_rx_buffer(struct efx_rx_buffer *rx_buf)
 {
-       if ((rx_buf->flags & EFX_RX_BUF_PAGE) && rx_buf->u.page) {
-               __free_pages(rx_buf->u.page, efx->rx_buffer_order);
-               rx_buf->u.page = NULL;
-       } else if (!(rx_buf->flags & EFX_RX_BUF_PAGE) && rx_buf->u.skb) {
-               dev_kfree_skb_any(rx_buf->u.skb);
-               rx_buf->u.skb = NULL;
+       if (rx_buf->page) {
+               put_page(rx_buf->page);
+               rx_buf->page = NULL;
        }
 }
 
-static void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue,
-                              struct efx_rx_buffer *rx_buf)
+/* Attempt to recycle the page if there is an RX recycle ring; the page can
+ * only be added if this is the final RX buffer, to prevent pages being used in
+ * the descriptor ring and appearing in the recycle ring simultaneously.
+ */
+static void efx_recycle_rx_page(struct efx_channel *channel,
+                               struct efx_rx_buffer *rx_buf)
 {
-       efx_unmap_rx_buffer(rx_queue->efx, rx_buf, 0);
-       efx_free_rx_buffer(rx_queue->efx, rx_buf);
-}
+       struct page *page = rx_buf->page;
+       struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel);
+       struct efx_nic *efx = rx_queue->efx;
+       unsigned index;
 
-/* Attempt to resurrect the other receive buffer that used to share this page,
- * which had previously been passed up to the kernel and freed. */
-static void efx_resurrect_rx_buffer(struct efx_rx_queue *rx_queue,
-                                   struct efx_rx_buffer *rx_buf)
-{
-       struct efx_rx_page_state *state = page_address(rx_buf->u.page);
-       struct efx_rx_buffer *new_buf;
-       unsigned fill_level, index;
-
-       /* +1 because efx_rx_packet() incremented removed_count. +1 because
-        * we'd like to insert an additional descriptor whilst leaving
-        * EFX_RXD_HEAD_ROOM for the non-recycle path */
-       fill_level = (rx_queue->added_count - rx_queue->removed_count + 2);
-       if (unlikely(fill_level > rx_queue->max_fill)) {
-               /* We could place "state" on a list, and drain the list in
-                * efx_fast_push_rx_descriptors(). For now, this will do. */
+       /* Only recycle the page after processing the final buffer. */
+       if (!(rx_buf->flags & EFX_RX_BUF_LAST_IN_PAGE))
                return;
-       }
 
-       ++state->refcnt;
-       get_page(rx_buf->u.page);
+       index = rx_queue->page_add & rx_queue->page_ptr_mask;
+       if (rx_queue->page_ring[index] == NULL) {
+               unsigned read_index = rx_queue->page_remove &
+                       rx_queue->page_ptr_mask;
 
-       index = rx_queue->added_count & rx_queue->ptr_mask;
-       new_buf = efx_rx_buffer(rx_queue, index);
-       new_buf->dma_addr = rx_buf->dma_addr ^ (PAGE_SIZE >> 1);
-       new_buf->u.page = rx_buf->u.page;
-       new_buf->len = rx_buf->len;
-       new_buf->flags = EFX_RX_BUF_PAGE;
-       ++rx_queue->added_count;
+               /* The next slot in the recycle ring is available, but
+                * increment page_remove if the read pointer currently
+                * points here.
+                */
+               if (read_index == index)
+                       ++rx_queue->page_remove;
+               rx_queue->page_ring[index] = page;
+               ++rx_queue->page_add;
+               return;
+       }
+       ++rx_queue->page_recycle_full;
+       efx_unmap_rx_buffer(efx, rx_buf);
+       put_page(rx_buf->page);
 }
 
-/* Recycle the given rx buffer directly back into the rx_queue. There is
- * always room to add this buffer, because we've just popped a buffer. */
-static void efx_recycle_rx_buffer(struct efx_channel *channel,
-                                 struct efx_rx_buffer *rx_buf)
+static void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue,
+                              struct efx_rx_buffer *rx_buf)
 {
-       struct efx_nic *efx = channel->efx;
-       struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel);
-       struct efx_rx_buffer *new_buf;
-       unsigned index;
-
-       rx_buf->flags &= EFX_RX_BUF_PAGE;
-
-       if ((rx_buf->flags & EFX_RX_BUF_PAGE) &&
-           efx->rx_buffer_len <= EFX_RX_HALF_PAGE &&
-           page_count(rx_buf->u.page) == 1)
-               efx_resurrect_rx_buffer(rx_queue, rx_buf);
+       /* Release the page reference we hold for the buffer. */
+       if (rx_buf->page)
+               put_page(rx_buf->page);
+
+       /* If this is the last buffer in a page, unmap and free it. */
+       if (rx_buf->flags & EFX_RX_BUF_LAST_IN_PAGE) {
+               efx_unmap_rx_buffer(rx_queue->efx, rx_buf);
+               efx_free_rx_buffer(rx_buf);
+       }
+       rx_buf->page = NULL;
+}
 
-       index = rx_queue->added_count & rx_queue->ptr_mask;
-       new_buf = efx_rx_buffer(rx_queue, index);
+/* Recycle the pages that are used by buffers that have just been received. */
+static void efx_recycle_rx_buffers(struct efx_channel *channel,
+                                  struct efx_rx_buffer *rx_buf,
+                                  unsigned int n_frags)
+{
+       struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel);
 
-       memcpy(new_buf, rx_buf, sizeof(*new_buf));
-       rx_buf->u.page = NULL;
-       ++rx_queue->added_count;
+       do {
+               efx_recycle_rx_page(channel, rx_buf);
+               rx_buf = efx_rx_buf_next(rx_queue, rx_buf);
+       } while (--n_frags);
 }
 
 /**
@@ -348,8 +308,8 @@ static void efx_recycle_rx_buffer(struct efx_channel *channel,
  */
 void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue)
 {
-       struct efx_channel *channel = efx_rx_queue_channel(rx_queue);
-       unsigned fill_level;
+       struct efx_nic *efx = rx_queue->efx;
+       unsigned int fill_level, batch_size;
        int space, rc = 0;
 
        /* Calculate current fill level, and exit if we don't need to fill */
@@ -364,28 +324,26 @@ void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue)
                        rx_queue->min_fill = fill_level;
        }
 
+       batch_size = efx->rx_pages_per_batch * efx->rx_bufs_per_page;
        space = rx_queue->max_fill - fill_level;
-       EFX_BUG_ON_PARANOID(space < EFX_RX_BATCH);
+       EFX_BUG_ON_PARANOID(space < batch_size);
 
        netif_vdbg(rx_queue->efx, rx_status, rx_queue->efx->net_dev,
                   "RX queue %d fast-filling descriptor ring from"
-                  " level %d to level %d using %s allocation\n",
+                  " level %d to level %d\n",
                   efx_rx_queue_index(rx_queue), fill_level,
-                  rx_queue->max_fill,
-                  channel->rx_alloc_push_pages ? "page" : "skb");
+                  rx_queue->max_fill);
+
 
        do {
-               if (channel->rx_alloc_push_pages)
-                       rc = efx_init_rx_buffers_page(rx_queue);
-               else
-                       rc = efx_init_rx_buffers_skb(rx_queue);
+               rc = efx_init_rx_buffers(rx_queue);
                if (unlikely(rc)) {
                        /* Ensure that we don't leave the rx queue empty */
                        if (rx_queue->added_count == rx_queue->removed_count)
                                efx_schedule_slow_fill(rx_queue);
                        goto out;
                }
-       } while ((space -= EFX_RX_BATCH) >= EFX_RX_BATCH);
+       } while ((space -= batch_size) >= batch_size);
 
        netif_vdbg(rx_queue->efx, rx_status, rx_queue->efx->net_dev,
                   "RX queue %d fast-filled descriptor ring "
@@ -408,7 +366,7 @@ void efx_rx_slow_fill(unsigned long context)
 
 static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue,
                                     struct efx_rx_buffer *rx_buf,
-                                    int len, bool *leak_packet)
+                                    int len)
 {
        struct efx_nic *efx = rx_queue->efx;
        unsigned max_len = rx_buf->len - efx->type->rx_buffer_padding;
@@ -428,11 +386,6 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue,
                                  "RX event (0x%x > 0x%x+0x%x). Leaking\n",
                                  efx_rx_queue_index(rx_queue), len, max_len,
                                  efx->type->rx_buffer_padding);
-               /* If this buffer was skb-allocated, then the meta
-                * data at the end of the skb will be trashed. So
-                * we have no choice but to leak the fragment.
-                */
-               *leak_packet = !(rx_buf->flags & EFX_RX_BUF_PAGE);
                efx_schedule_reset(efx, RESET_TYPE_RX_RECOVERY);
        } else {
                if (net_ratelimit())
@@ -448,212 +401,238 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue,
 /* Pass a received packet up through GRO.  GRO can handle pages
  * regardless of checksum state and skbs with a good checksum.
  */
-static void efx_rx_packet_gro(struct efx_channel *channel,
-                             struct efx_rx_buffer *rx_buf,
-                             const u8 *eh)
+static void
+efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf,
+                 unsigned int n_frags, u8 *eh)
 {
        struct napi_struct *napi = &channel->napi_str;
        gro_result_t gro_result;
+       struct efx_nic *efx = channel->efx;
+       struct sk_buff *skb;
 
-       if (rx_buf->flags & EFX_RX_BUF_PAGE) {
-               struct efx_nic *efx = channel->efx;
-               struct page *page = rx_buf->u.page;
-               struct sk_buff *skb;
+       skb = napi_get_frags(napi);
+       if (unlikely(!skb)) {
+               while (n_frags--) {
+                       put_page(rx_buf->page);
+                       rx_buf->page = NULL;
+                       rx_buf = efx_rx_buf_next(&channel->rx_queue, rx_buf);
+               }
+               return;
+       }
 
-               rx_buf->u.page = NULL;
+       if (efx->net_dev->features & NETIF_F_RXHASH)
+               skb->rxhash = efx_rx_buf_hash(eh);
+       skb->ip_summed = ((rx_buf->flags & EFX_RX_PKT_CSUMMED) ?
+                         CHECKSUM_UNNECESSARY : CHECKSUM_NONE);
+
+       for (;;) {
+               skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
+                                  rx_buf->page, rx_buf->page_offset,
+                                  rx_buf->len);
+               rx_buf->page = NULL;
+               skb->len += rx_buf->len;
+               if (skb_shinfo(skb)->nr_frags == n_frags)
+                       break;
+
+               rx_buf = efx_rx_buf_next(&channel->rx_queue, rx_buf);
+       }
 
-               skb = napi_get_frags(napi);
-               if (!skb) {
-                       put_page(page);
-                       return;
-               }
+       skb->data_len = skb->len;
+       skb->truesize += n_frags * efx->rx_buffer_truesize;
+
+       skb_record_rx_queue(skb, channel->rx_queue.core_index);
+
+       gro_result = napi_gro_frags(napi);
+       if (gro_result != GRO_DROP)
+               channel->irq_mod_score += 2;
+}
 
-               if (efx->net_dev->features & NETIF_F_RXHASH)
-                       skb->rxhash = efx_rx_buf_hash(eh);
+/* Allocate and construct an SKB around page fragments */
+static struct sk_buff *efx_rx_mk_skb(struct efx_channel *channel,
+                                    struct efx_rx_buffer *rx_buf,
+                                    unsigned int n_frags,
+                                    u8 *eh, int hdr_len)
+{
+       struct efx_nic *efx = channel->efx;
+       struct sk_buff *skb;
 
-               skb_fill_page_desc(skb, 0, page,
-                                  efx_rx_buf_offset(efx, rx_buf), rx_buf->len);
+       /* Allocate an SKB to store the headers */
+       skb = netdev_alloc_skb(efx->net_dev, hdr_len + EFX_PAGE_SKB_ALIGN);
+       if (unlikely(skb == NULL))
+               return NULL;
 
-               skb->len = rx_buf->len;
-               skb->data_len = rx_buf->len;
-               skb->truesize += rx_buf->len;
-               skb->ip_summed = ((rx_buf->flags & EFX_RX_PKT_CSUMMED) ?
-                                 CHECKSUM_UNNECESSARY : CHECKSUM_NONE);
+       EFX_BUG_ON_PARANOID(rx_buf->len < hdr_len);
 
-               skb_record_rx_queue(skb, channel->rx_queue.core_index);
+       skb_reserve(skb, EFX_PAGE_SKB_ALIGN);
+       memcpy(__skb_put(skb, hdr_len), eh, hdr_len);
 
-               gro_result = napi_gro_frags(napi);
-       } else {
-               struct sk_buff *skb = rx_buf->u.skb;
+       /* Append the remaining page(s) onto the frag list */
+       if (rx_buf->len > hdr_len) {
+               rx_buf->page_offset += hdr_len;
+               rx_buf->len -= hdr_len;
 
-               EFX_BUG_ON_PARANOID(!(rx_buf->flags & EFX_RX_PKT_CSUMMED));
-               rx_buf->u.skb = NULL;
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               for (;;) {
+                       skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
+                                          rx_buf->page, rx_buf->page_offset,
+                                          rx_buf->len);
+                       rx_buf->page = NULL;
+                       skb->len += rx_buf->len;
+                       skb->data_len += rx_buf->len;
+                       if (skb_shinfo(skb)->nr_frags == n_frags)
+                               break;
 
-               gro_result = napi_gro_receive(napi, skb);
+                       rx_buf = efx_rx_buf_next(&channel->rx_queue, rx_buf);
+               }
+       } else {
+               __free_pages(rx_buf->page, efx->rx_buffer_order);
+               rx_buf->page = NULL;
+               n_frags = 0;
        }
 
-       if (gro_result == GRO_NORMAL) {
-               channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
-       } else if (gro_result != GRO_DROP) {
-               channel->rx_alloc_level += RX_ALLOC_FACTOR_GRO;
-               channel->irq_mod_score += 2;
-       }
+       skb->truesize += n_frags * efx->rx_buffer_truesize;
+
+       /* Move past the ethernet header */
+       skb->protocol = eth_type_trans(skb, efx->net_dev);
+
+       return skb;
 }
 
 void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
-                  unsigned int len, u16 flags)
+                  unsigned int n_frags, unsigned int len, u16 flags)
 {
        struct efx_nic *efx = rx_queue->efx;
        struct efx_channel *channel = efx_rx_queue_channel(rx_queue);
        struct efx_rx_buffer *rx_buf;
-       bool leak_packet = false;
 
        rx_buf = efx_rx_buffer(rx_queue, index);
        rx_buf->flags |= flags;
 
-       /* This allows the refill path to post another buffer.
-        * EFX_RXD_HEAD_ROOM ensures that the slot we are using
-        * isn't overwritten yet.
-        */
-       rx_queue->removed_count++;
-
-       /* Validate the length encoded in the event vs the descriptor pushed */
-       efx_rx_packet__check_len(rx_queue, rx_buf, len, &leak_packet);
+       /* Validate the number of fragments and completed length */
+       if (n_frags == 1) {
+               efx_rx_packet__check_len(rx_queue, rx_buf, len);
+       } else if (unlikely(n_frags > EFX_RX_MAX_FRAGS) ||
+                  unlikely(len <= (n_frags - 1) * EFX_RX_USR_BUF_SIZE) ||
+                  unlikely(len > n_frags * EFX_RX_USR_BUF_SIZE) ||
+                  unlikely(!efx->rx_scatter)) {
+               /* If this isn't an explicit discard request, either
+                * the hardware or the driver is broken.
+                */
+               WARN_ON(!(len == 0 && rx_buf->flags & EFX_RX_PKT_DISCARD));
+               rx_buf->flags |= EFX_RX_PKT_DISCARD;
+       }
 
        netif_vdbg(efx, rx_status, efx->net_dev,
-                  "RX queue %d received id %x at %llx+%x %s%s\n",
+                  "RX queue %d received ids %x-%x len %d %s%s\n",
                   efx_rx_queue_index(rx_queue), index,
-                  (unsigned long long)rx_buf->dma_addr, len,
+                  (index + n_frags - 1) & rx_queue->ptr_mask, len,
                   (rx_buf->flags & EFX_RX_PKT_CSUMMED) ? " [SUMMED]" : "",
                   (rx_buf->flags & EFX_RX_PKT_DISCARD) ? " [DISCARD]" : "");
 
-       /* Discard packet, if instructed to do so */
+       /* Discard packet, if instructed to do so.  Process the
+        * previous receive first.
+        */
        if (unlikely(rx_buf->flags & EFX_RX_PKT_DISCARD)) {
-               if (unlikely(leak_packet))
-                       channel->n_skbuff_leaks++;
-               else
-                       efx_recycle_rx_buffer(channel, rx_buf);
-
-               /* Don't hold off the previous receive */
-               rx_buf = NULL;
-               goto out;
+               efx_rx_flush_packet(channel);
+               put_page(rx_buf->page);
+               efx_recycle_rx_buffers(channel, rx_buf, n_frags);
+               return;
        }
 
-       /* Release and/or sync DMA mapping - assumes all RX buffers
-        * consumed in-order per RX queue
+       if (n_frags == 1)
+               rx_buf->len = len;
+
+       /* Release and/or sync the DMA mapping - assumes all RX buffers
+        * consumed in-order per RX queue.
         */
-       efx_unmap_rx_buffer(efx, rx_buf, len);
+       efx_sync_rx_buffer(efx, rx_buf, rx_buf->len);
 
        /* Prefetch nice and early so data will (hopefully) be in cache by
         * the time we look at it.
         */
-       prefetch(efx_rx_buf_eh(efx, rx_buf));
+       prefetch(efx_rx_buf_va(rx_buf));
+
+       rx_buf->page_offset += efx->type->rx_buffer_hash_size;
+       rx_buf->len -= efx->type->rx_buffer_hash_size;
+
+       if (n_frags > 1) {
+               /* Release/sync DMA mapping for additional fragments.
+                * Fix length for last fragment.
+                */
+               unsigned int tail_frags = n_frags - 1;
+
+               for (;;) {
+                       rx_buf = efx_rx_buf_next(rx_queue, rx_buf);
+                       if (--tail_frags == 0)
+                               break;
+                       efx_sync_rx_buffer(efx, rx_buf, EFX_RX_USR_BUF_SIZE);
+               }
+               rx_buf->len = len - (n_frags - 1) * EFX_RX_USR_BUF_SIZE;
+               efx_sync_rx_buffer(efx, rx_buf, rx_buf->len);
+       }
+
+       /* All fragments have been DMA-synced, so recycle buffers and pages. */
+       rx_buf = efx_rx_buffer(rx_queue, index);
+       efx_recycle_rx_buffers(channel, rx_buf, n_frags);
 
        /* Pipeline receives so that we give time for packet headers to be
         * prefetched into cache.
         */
-       rx_buf->len = len - efx->type->rx_buffer_hash_size;
-out:
-       if (channel->rx_pkt)
-               __efx_rx_packet(channel, channel->rx_pkt);
-       channel->rx_pkt = rx_buf;
+       efx_rx_flush_packet(channel);
+       channel->rx_pkt_n_frags = n_frags;
+       channel->rx_pkt_index = index;
 }
 
-static void efx_rx_deliver(struct efx_channel *channel,
-                          struct efx_rx_buffer *rx_buf)
+static void efx_rx_deliver(struct efx_channel *channel, u8 *eh,
+                          struct efx_rx_buffer *rx_buf,
+                          unsigned int n_frags)
 {
        struct sk_buff *skb;
+       u16 hdr_len = min_t(u16, rx_buf->len, EFX_SKB_HEADERS);
 
-       /* We now own the SKB */
-       skb = rx_buf->u.skb;
-       rx_buf->u.skb = NULL;
+       skb = efx_rx_mk_skb(channel, rx_buf, n_frags, eh, hdr_len);
+       if (unlikely(skb == NULL)) {
+               efx_free_rx_buffer(rx_buf);
+               return;
+       }
+       skb_record_rx_queue(skb, channel->rx_queue.core_index);
 
        /* Set the SKB flags */
        skb_checksum_none_assert(skb);
 
-       /* Record the rx_queue */
-       skb_record_rx_queue(skb, channel->rx_queue.core_index);
-
-       /* Pass the packet up */
        if (channel->type->receive_skb)
-               channel->type->receive_skb(channel, skb);
-       else
-               netif_receive_skb(skb);
+               if (channel->type->receive_skb(channel, skb))
+                       return;
 
-       /* Update allocation strategy method */
-       channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
+       /* Pass the packet up */
+       netif_receive_skb(skb);
 }
 
 /* Handle a received packet.  Second half: Touches packet payload. */
-void __efx_rx_packet(struct efx_channel *channel, struct efx_rx_buffer *rx_buf)
+void __efx_rx_packet(struct efx_channel *channel)
 {
        struct efx_nic *efx = channel->efx;
-       u8 *eh = efx_rx_buf_eh(efx, rx_buf);
+       struct efx_rx_buffer *rx_buf =
+               efx_rx_buffer(&channel->rx_queue, channel->rx_pkt_index);
+       u8 *eh = efx_rx_buf_va(rx_buf);
 
        /* If we're in loopback test, then pass the packet directly to the
         * loopback layer, and free the rx_buf here
         */
        if (unlikely(efx->loopback_selftest)) {
                efx_loopback_rx_packet(efx, eh, rx_buf->len);
-               efx_free_rx_buffer(efx, rx_buf);
-               return;
-       }
-
-       if (!(rx_buf->flags & EFX_RX_BUF_PAGE)) {
-               struct sk_buff *skb = rx_buf->u.skb;
-
-               prefetch(skb_shinfo(skb));
-
-               skb_reserve(skb, efx->type->rx_buffer_hash_size);
-               skb_put(skb, rx_buf->len);
-
-               if (efx->net_dev->features & NETIF_F_RXHASH)
-                       skb->rxhash = efx_rx_buf_hash(eh);
-
-               /* Move past the ethernet header. rx_buf->data still points
-                * at the ethernet header */
-               skb->protocol = eth_type_trans(skb, efx->net_dev);
-
-               skb_record_rx_queue(skb, channel->rx_queue.core_index);
+               efx_free_rx_buffer(rx_buf);
+               goto out;
        }
 
        if (unlikely(!(efx->net_dev->features & NETIF_F_RXCSUM)))
                rx_buf->flags &= ~EFX_RX_PKT_CSUMMED;
 
-       if (likely(rx_buf->flags & (EFX_RX_BUF_PAGE | EFX_RX_PKT_CSUMMED)) &&
-           !channel->type->receive_skb)
-               efx_rx_packet_gro(channel, rx_buf, eh);
+       if (!channel->type->receive_skb)
+               efx_rx_packet_gro(channel, rx_buf, channel->rx_pkt_n_frags, eh);
        else
-               efx_rx_deliver(channel, rx_buf);
-}
-
-void efx_rx_strategy(struct efx_channel *channel)
-{
-       enum efx_rx_alloc_method method = rx_alloc_method;
-
-       if (channel->type->receive_skb) {
-               channel->rx_alloc_push_pages = false;
-               return;
-       }
-
-       /* Only makes sense to use page based allocation if GRO is enabled */
-       if (!(channel->efx->net_dev->features & NETIF_F_GRO)) {
-               method = RX_ALLOC_METHOD_SKB;
-       } else if (method == RX_ALLOC_METHOD_AUTO) {
-               /* Constrain the rx_alloc_level */
-               if (channel->rx_alloc_level < 0)
-                       channel->rx_alloc_level = 0;
-               else if (channel->rx_alloc_level > RX_ALLOC_LEVEL_MAX)
-                       channel->rx_alloc_level = RX_ALLOC_LEVEL_MAX;
-
-               /* Decide on the allocation method */
-               method = ((channel->rx_alloc_level > RX_ALLOC_LEVEL_GRO) ?
-                         RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB);
-       }
-
-       /* Push the option */
-       channel->rx_alloc_push_pages = (method == RX_ALLOC_METHOD_PAGE);
+               efx_rx_deliver(channel, eh, rx_buf, channel->rx_pkt_n_frags);
+out:
+       channel->rx_pkt_n_frags = 0;
 }
 
 int efx_probe_rx_queue(struct efx_rx_queue *rx_queue)
@@ -683,9 +662,32 @@ int efx_probe_rx_queue(struct efx_rx_queue *rx_queue)
                kfree(rx_queue->buffer);
                rx_queue->buffer = NULL;
        }
+
        return rc;
 }
 
+static void efx_init_rx_recycle_ring(struct efx_nic *efx,
+                                    struct efx_rx_queue *rx_queue)
+{
+       unsigned int bufs_in_recycle_ring, page_ring_size;
+
+       /* Set the RX recycle ring size */
+#ifdef CONFIG_PPC64
+       bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU;
+#else
+       if (efx->pci_dev->dev.iommu_group)
+               bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU;
+       else
+               bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_NOIOMMU;
+#endif /* CONFIG_PPC64 */
+
+       page_ring_size = roundup_pow_of_two(bufs_in_recycle_ring /
+                                           efx->rx_bufs_per_page);
+       rx_queue->page_ring = kcalloc(page_ring_size,
+                                     sizeof(*rx_queue->page_ring), GFP_KERNEL);
+       rx_queue->page_ptr_mask = page_ring_size - 1;
+}
+
 void efx_init_rx_queue(struct efx_rx_queue *rx_queue)
 {
        struct efx_nic *efx = rx_queue->efx;
@@ -699,10 +701,18 @@ void efx_init_rx_queue(struct efx_rx_queue *rx_queue)
        rx_queue->notified_count = 0;
        rx_queue->removed_count = 0;
        rx_queue->min_fill = -1U;
+       efx_init_rx_recycle_ring(efx, rx_queue);
+
+       rx_queue->page_remove = 0;
+       rx_queue->page_add = rx_queue->page_ptr_mask + 1;
+       rx_queue->page_recycle_count = 0;
+       rx_queue->page_recycle_failed = 0;
+       rx_queue->page_recycle_full = 0;
 
        /* Initialise limit fields */
        max_fill = efx->rxq_entries - EFX_RXD_HEAD_ROOM;
-       max_trigger = max_fill - EFX_RX_BATCH;
+       max_trigger =
+               max_fill - efx->rx_pages_per_batch * efx->rx_bufs_per_page;
        if (rx_refill_threshold != 0) {
                trigger = max_fill * min(rx_refill_threshold, 100U) / 100U;
                if (trigger > max_trigger)
@@ -722,6 +732,7 @@ void efx_init_rx_queue(struct efx_rx_queue *rx_queue)
 void efx_fini_rx_queue(struct efx_rx_queue *rx_queue)
 {
        int i;
+       struct efx_nic *efx = rx_queue->efx;
        struct efx_rx_buffer *rx_buf;
 
        netif_dbg(rx_queue->efx, drv, rx_queue->efx->net_dev,
@@ -733,13 +744,32 @@ void efx_fini_rx_queue(struct efx_rx_queue *rx_queue)
        del_timer_sync(&rx_queue->slow_fill);
        efx_nic_fini_rx(rx_queue);
 
-       /* Release RX buffers NB start at index 0 not current HW ptr */
+       /* Release RX buffers from the current read ptr to the write ptr */
        if (rx_queue->buffer) {
-               for (i = 0; i <= rx_queue->ptr_mask; i++) {
-                       rx_buf = efx_rx_buffer(rx_queue, i);
+               for (i = rx_queue->removed_count; i < rx_queue->added_count;
+                    i++) {
+                       unsigned index = i & rx_queue->ptr_mask;
+                       rx_buf = efx_rx_buffer(rx_queue, index);
                        efx_fini_rx_buffer(rx_queue, rx_buf);
                }
        }
+
+       /* Unmap and release the pages in the recycle ring. Remove the ring. */
+       for (i = 0; i <= rx_queue->page_ptr_mask; i++) {
+               struct page *page = rx_queue->page_ring[i];
+               struct efx_rx_page_state *state;
+
+               if (page == NULL)
+                       continue;
+
+               state = page_address(page);
+               dma_unmap_page(&efx->pci_dev->dev, state->dma_addr,
+                              PAGE_SIZE << efx->rx_buffer_order,
+                              DMA_FROM_DEVICE);
+               put_page(page);
+       }
+       kfree(rx_queue->page_ring);
+       rx_queue->page_ring = NULL;
 }
 
 void efx_remove_rx_queue(struct efx_rx_queue *rx_queue)
@@ -754,9 +784,6 @@ void efx_remove_rx_queue(struct efx_rx_queue *rx_queue)
 }
 
 
-module_param(rx_alloc_method, int, 0644);
-MODULE_PARM_DESC(rx_alloc_method, "Allocation method used for RX buffers");
-
 module_param(rx_refill_threshold, uint, 0444);
 MODULE_PARM_DESC(rx_refill_threshold,
                 "RX descriptor ring refill threshold (%)");
index ba40f67e4f05da34c89475a68622375646da9dda..51669244d1548f416042f5f1c0670c6383599099 100644 (file)
@@ -202,7 +202,7 @@ out:
 
 static enum reset_type siena_map_reset_reason(enum reset_type reason)
 {
-       return RESET_TYPE_ALL;
+       return RESET_TYPE_RECOVER_OR_ALL;
 }
 
 static int siena_map_reset_flags(u32 *flags)
@@ -245,6 +245,22 @@ static int siena_reset_hw(struct efx_nic *efx, enum reset_type method)
                return efx_mcdi_reset_port(efx);
 }
 
+#ifdef CONFIG_EEH
+/* When a PCI device is isolated from the bus, a subsequent MMIO read is
+ * required for the kernel EEH mechanisms to notice. As the Solarflare driver
+ * was written to minimise MMIO read (for latency) then a periodic call to check
+ * the EEH status of the device is required so that device recovery can happen
+ * in a timely fashion.
+ */
+static void siena_monitor(struct efx_nic *efx)
+{
+       struct eeh_dev *eehdev =
+               of_node_to_eeh_dev(pci_device_to_OF_node(efx->pci_dev));
+
+       eeh_dev_check_failure(eehdev);
+}
+#endif
+
 static int siena_probe_nvconfig(struct efx_nic *efx)
 {
        u32 caps = 0;
@@ -398,6 +414,8 @@ static int siena_init_nic(struct efx_nic *efx)
        EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_HASH_INSRT_HDR, 1);
        EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_HASH_ALG, 1);
        EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_IP_HASH, 1);
+       EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_USR_BUF_SIZE,
+                           EFX_RX_USR_BUF_SIZE >> 5);
        efx_writeo(efx, &temp, FR_AZ_RX_CFG);
 
        /* Set hash key for IPv4 */
@@ -665,7 +683,11 @@ const struct efx_nic_type siena_a0_nic_type = {
        .init = siena_init_nic,
        .dimension_resources = siena_dimension_resources,
        .fini = efx_port_dummy_op_void,
+#ifdef CONFIG_EEH
+       .monitor = siena_monitor,
+#else
        .monitor = NULL,
+#endif
        .map_reset_reason = siena_map_reset_reason,
        .map_reset_flags = siena_map_reset_flags,
        .reset = siena_reset_hw,
@@ -698,6 +720,7 @@ const struct efx_nic_type siena_a0_nic_type = {
        .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH),
        .rx_buffer_hash_size = 0x10,
        .rx_buffer_padding = 0,
+       .can_rx_scatter = true,
        .max_interrupt_mode = EFX_INT_MODE_MSIX,
        .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy
                                   * interrupt handler only supports 32
index 79ad9c94a21bf4b660c1cdaa60352d97facfdbec..4bdbaad9932df37f1fb92e8eb3c5e76bc2efc6be 100644 (file)
@@ -213,10 +213,11 @@ static int meth_init_tx_ring(struct meth_private *priv)
 {
        /* Init TX ring */
        priv->tx_ring = dma_alloc_coherent(NULL, TX_RING_BUFFER_SIZE,
-                                          &priv->tx_ring_dma, GFP_ATOMIC);
+                                          &priv->tx_ring_dma,
+                                          GFP_ATOMIC | __GFP_ZERO);
        if (!priv->tx_ring)
                return -ENOMEM;
-       memset(priv->tx_ring, 0, TX_RING_BUFFER_SIZE);
+
        priv->tx_count = priv->tx_read = priv->tx_write = 0;
        mace->eth.tx_ring_base = priv->tx_ring_dma;
        /* Now init skb save area */
index efca14eaefa9324487e3a0aa496730145920795d..eb4aea3fe793a8372292023877eecae57c5b5acd 100644 (file)
@@ -1187,8 +1187,14 @@ sis900_init_rx_ring(struct net_device *net_dev)
                }
                sis_priv->rx_skbuff[i] = skb;
                sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE;
-                sis_priv->rx_ring[i].bufptr = pci_map_single(sis_priv->pci_dev,
-                        skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+               sis_priv->rx_ring[i].bufptr = pci_map_single(sis_priv->pci_dev,
+                               skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+               if (unlikely(pci_dma_mapping_error(sis_priv->pci_dev,
+                               sis_priv->rx_ring[i].bufptr))) {
+                       dev_kfree_skb(skb);
+                       sis_priv->rx_skbuff[i] = NULL;
+                       break;
+               }
        }
        sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC);
 
@@ -1621,6 +1627,14 @@ sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
        /* set the transmit buffer descriptor and enable Transmit State Machine */
        sis_priv->tx_ring[entry].bufptr = pci_map_single(sis_priv->pci_dev,
                skb->data, skb->len, PCI_DMA_TODEVICE);
+       if (unlikely(pci_dma_mapping_error(sis_priv->pci_dev,
+               sis_priv->tx_ring[entry].bufptr))) {
+                       dev_kfree_skb(skb);
+                       sis_priv->tx_skbuff[entry] = NULL;
+                       net_dev->stats.tx_dropped++;
+                       spin_unlock_irqrestore(&sis_priv->lock, flags);
+                       return NETDEV_TX_OK;
+       }
        sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len);
        sw32(cr, TxENA | sr32(cr));
 
@@ -1824,9 +1838,15 @@ static int sis900_rx(struct net_device *net_dev)
 refill_rx_ring:
                        sis_priv->rx_skbuff[entry] = skb;
                        sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
-                       sis_priv->rx_ring[entry].bufptr =
+                       sis_priv->rx_ring[entry].bufptr =
                                pci_map_single(sis_priv->pci_dev, skb->data,
                                        RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+                       if (unlikely(pci_dma_mapping_error(sis_priv->pci_dev,
+                               sis_priv->rx_ring[entry].bufptr))) {
+                               dev_kfree_skb_irq(skb);
+                               sis_priv->rx_skbuff[entry] = NULL;
+                               break;
+                       }
                }
                sis_priv->cur_rx++;
                entry = sis_priv->cur_rx % NUM_RX_DESC;
@@ -1841,23 +1861,26 @@ refill_rx_ring:
                entry = sis_priv->dirty_rx % NUM_RX_DESC;
 
                if (sis_priv->rx_skbuff[entry] == NULL) {
-                       if ((skb = netdev_alloc_skb(net_dev, RX_BUF_SIZE)) == NULL) {
+                       skb = netdev_alloc_skb(net_dev, RX_BUF_SIZE);
+                       if (skb == NULL) {
                                /* not enough memory for skbuff, this makes a
                                 * "hole" on the buffer ring, it is not clear
                                 * how the hardware will react to this kind
                                 * of degenerated buffer */
-                               if (netif_msg_rx_err(sis_priv))
-                                       printk(KERN_INFO "%s: Memory squeeze, "
-                                               "deferring packet.\n",
-                                               net_dev->name);
                                net_dev->stats.rx_dropped++;
                                break;
                        }
                        sis_priv->rx_skbuff[entry] = skb;
                        sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
-                       sis_priv->rx_ring[entry].bufptr =
+                       sis_priv->rx_ring[entry].bufptr =
                                pci_map_single(sis_priv->pci_dev, skb->data,
                                        RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+                       if (unlikely(pci_dma_mapping_error(sis_priv->pci_dev,
+                                       sis_priv->rx_ring[entry].bufptr))) {
+                               dev_kfree_skb_irq(skb);
+                               sis_priv->rx_skbuff[entry] = NULL;
+                               break;
+                       }
                }
        }
        /* re-enable the potentially idle receive state matchine */
index 50823da9dc1ed8d22abcb9fde6026514df076e56..e85c2e7e82468e8d53d4e5a14f692d9ac6a34c5c 100644 (file)
@@ -1223,9 +1223,7 @@ static void smc_rcv(struct net_device *dev)
                        dev->stats.multicast++;
 
                skb = netdev_alloc_skb(dev, packet_length + 5);
-
                if ( skb == NULL ) {
-                       printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n");
                        dev->stats.rx_dropped++;
                        goto done;
                }
index 591650a8de3887942b4efeb78faa3e5ae3324039..dfbf978315dfb31a8cbbd01ce5beae06abc5f398 100644 (file)
@@ -465,8 +465,6 @@ static inline void  smc_rcv(struct net_device *dev)
                 */
                skb = netdev_alloc_skb(dev, packet_len);
                if (unlikely(skb == NULL)) {
-                       printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
-                               dev->name);
                        SMC_WAIT_MMU_BUSY(lp);
                        SMC_SET_MMU_CMD(lp, MC_RELEASE);
                        dev->stats.rx_dropped++;
index da5cc9a3b34cb3571740d0851569b6b89d236a41..48e2b99bec51335c87a877ed5d16f6e4c488d937 100644 (file)
@@ -2115,7 +2115,7 @@ static int smsc911x_init(struct net_device *dev)
        spin_lock_init(&pdata->dev_lock);
        spin_lock_init(&pdata->mac_lock);
 
-       if (pdata->ioaddr == 0) {
+       if (pdata->ioaddr == NULL) {
                SMSC_WARN(pdata, probe, "pdata->ioaddr: 0x00000000");
                return -ENODEV;
        }
index d457fa2d7509e70273b111fce6d8cff68663dd4b..ffa5c4ad12105a2050d264d4deac90080f7e4c9d 100644 (file)
@@ -848,10 +848,8 @@ static int smsc9420_alloc_rx_buffer(struct smsc9420_pdata *pd, int index)
        BUG_ON(pd->rx_buffers[index].skb);
        BUG_ON(pd->rx_buffers[index].mapping);
 
-       if (unlikely(!skb)) {
-               smsc_warn(RX_ERR, "Failed to allocate new skb!");
+       if (unlikely(!skb))
                return -ENOMEM;
-       }
 
        mapping = pci_map_single(pd->pdev, skb_tail_pointer(skb),
                                 PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
index c0ea838c78d1786eeec6b3b85c5aa8d9cf47051a..f695a50bac47d99cdc141a3cf059dff340e54dc3 100644 (file)
@@ -5,6 +5,7 @@ config STMMAC_ETH
        select MII
        select PHYLIB
        select CRC32
+       select PTP_1588_CLOCK
        ---help---
          This is the driver for the Ethernet IPs are built around a
          Synopsys IP Core and only tested on the STMicroelectronics
@@ -54,22 +55,4 @@ config STMMAC_DA
          By default, the DMA arbitration scheme is based on Round-robin
          (rx:tx priority is 1:1).
 
-choice
-       prompt "Select the DMA TX/RX descriptor operating modes"
-       depends on STMMAC_ETH
-       ---help---
-         This driver supports DMA descriptor to operate both in dual buffer
-         (RING) and linked-list(CHAINED) mode. In RING mode each descriptor
-         points to two data buffer pointers whereas in CHAINED mode they
-         points to only one data buffer pointer.
-
-config STMMAC_RING
-       bool "Enable Descriptor Ring Mode"
-
-config STMMAC_CHAINED
-       bool "Enable Descriptor Chained Mode"
-
-endchoice
-
-
 endif
index c8e8ea60ac19f1bc5d9ae56033e41fafec4e3c7e..356a9dd32be7e57a2ff1855304b4c22d17a5dc03 100644 (file)
@@ -1,9 +1,7 @@
 obj-$(CONFIG_STMMAC_ETH) += stmmac.o
-stmmac-$(CONFIG_STMMAC_RING) += ring_mode.o
-stmmac-$(CONFIG_STMMAC_CHAINED) += chain_mode.o
 stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
 stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
-stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o     \
-             dwmac_lib.o dwmac1000_core.o  dwmac1000_dma.o     \
+stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
+             chain_mode.o dwmac_lib.o dwmac1000_core.o  dwmac1000_dma.o \
              dwmac100_core.o dwmac100_dma.o enh_desc.o  norm_desc.o \
-             mmc_core.o $(stmmac-y)
+             mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o $(stmmac-y)
index 0668659803ed3230ababc2b3b9a1aa2a531dae0b..d234ab540b29101a825afacee7d58e27ef5186f5 100644 (file)
@@ -28,9 +28,9 @@
 
 #include "stmmac.h"
 
-unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
+static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
 {
-       struct stmmac_priv *priv = (struct stmmac_priv *) p;
+       struct stmmac_priv *priv = (struct stmmac_priv *)p;
        unsigned int txsize = priv->dma_tx_size;
        unsigned int entry = priv->cur_tx % txsize;
        struct dma_desc *desc = priv->dma_tx + entry;
@@ -47,7 +47,8 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
 
        desc->des2 = dma_map_single(priv->device, skb->data,
                                    bmax, DMA_TO_DEVICE);
-       priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum);
+       priv->tx_skbuff_dma[entry] = desc->des2;
+       priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE);
 
        while (len != 0) {
                entry = (++priv->cur_tx) % txsize;
@@ -57,8 +58,9 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
                        desc->des2 = dma_map_single(priv->device,
                                                    (skb->data + bmax * i),
                                                    bmax, DMA_TO_DEVICE);
-                       priv->hw->desc->prepare_tx_desc(desc, 0, bmax,
-                                                       csum);
+                       priv->tx_skbuff_dma[entry] = desc->des2;
+                       priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum,
+                                                       STMMAC_CHAIN_MODE);
                        priv->hw->desc->set_tx_owner(desc);
                        priv->tx_skbuff[entry] = NULL;
                        len -= bmax;
@@ -67,8 +69,9 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
                        desc->des2 = dma_map_single(priv->device,
                                                    (skb->data + bmax * i), len,
                                                    DMA_TO_DEVICE);
-                       priv->hw->desc->prepare_tx_desc(desc, 0, len,
-                                                       csum);
+                       priv->tx_skbuff_dma[entry] = desc->des2;
+                       priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
+                                                       STMMAC_CHAIN_MODE);
                        priv->hw->desc->set_tx_owner(desc);
                        priv->tx_skbuff[entry] = NULL;
                        len = 0;
@@ -89,49 +92,70 @@ static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
        return ret;
 }
 
-static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
-{
-}
-
-static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p)
-{
-}
-
-static void stmmac_clean_desc3(struct dma_desc *p)
-{
-}
-
-static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
-                                 unsigned int size)
+static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr,
+                                 unsigned int size, unsigned int extend_desc)
 {
        /*
         * In chained mode the des3 points to the next element in the ring.
         * The latest element has to point to the head.
         */
        int i;
-       struct dma_desc *p = des;
        dma_addr_t dma_phy = phy_addr;
 
-       for (i = 0; i < (size - 1); i++) {
-               dma_phy += sizeof(struct dma_desc);
-               p->des3 = (unsigned int)dma_phy;
-               p++;
+       if (extend_desc) {
+               struct dma_extended_desc *p = (struct dma_extended_desc *)des;
+               for (i = 0; i < (size - 1); i++) {
+                       dma_phy += sizeof(struct dma_extended_desc);
+                       p->basic.des3 = (unsigned int)dma_phy;
+                       p++;
+               }
+               p->basic.des3 = (unsigned int)phy_addr;
+
+       } else {
+               struct dma_desc *p = (struct dma_desc *)des;
+               for (i = 0; i < (size - 1); i++) {
+                       dma_phy += sizeof(struct dma_desc);
+                       p->des3 = (unsigned int)dma_phy;
+                       p++;
+               }
+               p->des3 = (unsigned int)phy_addr;
        }
-       p->des3 = (unsigned int)phy_addr;
 }
 
-static int stmmac_set_16kib_bfsize(int mtu)
+static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
+{
+       struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
+
+       if (priv->hwts_rx_en && !priv->extend_desc)
+               /* NOTE: Device will overwrite des3 with timestamp value if
+                * 1588-2002 time stamping is enabled, hence reinitialize it
+                * to keep explicit chaining in the descriptor.
+                */
+               p->des3 = (unsigned int)(priv->dma_rx_phy +
+                                        (((priv->dirty_rx) + 1) %
+                                         priv->dma_rx_size) *
+                                        sizeof(struct dma_desc));
+}
+
+static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)
 {
-       /* Not supported */
-       return 0;
+       struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
+
+       if (priv->hw->desc->get_tx_ls(p) && !priv->extend_desc)
+               /* NOTE: Device will overwrite des3 with timestamp value if
+                * 1588-2002 time stamping is enabled, hence reinitialize it
+                * to keep explicit chaining in the descriptor.
+                */
+               p->des3 = (unsigned int)(priv->dma_tx_phy +
+                                        (((priv->dirty_tx + 1) %
+                                          priv->dma_tx_size) *
+                                         sizeof(struct dma_desc)));
 }
 
-const struct stmmac_ring_mode_ops ring_mode_ops = {
+const struct stmmac_chain_mode_ops chain_mode_ops = {
+       .init = stmmac_init_dma_chain,
        .is_jumbo_frm = stmmac_is_jumbo_frm,
        .jumbo_frm = stmmac_jumbo_frm,
        .refill_desc3 = stmmac_refill_desc3,
-       .init_desc3 = stmmac_init_desc3,
-       .init_dma_chain = stmmac_init_dma_chain,
        .clean_desc3 = stmmac_clean_desc3,
-       .set_16kib_bfsize = stmmac_set_16kib_bfsize,
 };
index 186d1480612210d39d26b05305243d8fc71e1c16..7788fbe44f0ad35eee37a701733f3576cec271e4 100644 (file)
@@ -117,6 +117,36 @@ struct stmmac_extra_stats {
        unsigned long irq_rx_path_in_lpi_mode_n;
        unsigned long irq_rx_path_exit_lpi_mode_n;
        unsigned long phy_eee_wakeup_error_n;
+       /* Extended RDES status */
+       unsigned long ip_hdr_err;
+       unsigned long ip_payload_err;
+       unsigned long ip_csum_bypassed;
+       unsigned long ipv4_pkt_rcvd;
+       unsigned long ipv6_pkt_rcvd;
+       unsigned long rx_msg_type_ext_no_ptp;
+       unsigned long rx_msg_type_sync;
+       unsigned long rx_msg_type_follow_up;
+       unsigned long rx_msg_type_delay_req;
+       unsigned long rx_msg_type_delay_resp;
+       unsigned long rx_msg_type_pdelay_req;
+       unsigned long rx_msg_type_pdelay_resp;
+       unsigned long rx_msg_type_pdelay_follow_up;
+       unsigned long ptp_frame_type;
+       unsigned long ptp_ver;
+       unsigned long timestamp_dropped;
+       unsigned long av_pkt_rcvd;
+       unsigned long av_tagged_pkt_rcvd;
+       unsigned long vlan_tag_priority_val;
+       unsigned long l3_filter_match;
+       unsigned long l4_filter_match;
+       unsigned long l3_l4_filter_no_match;
+       /* PCS */
+       unsigned long irq_pcs_ane_n;
+       unsigned long irq_pcs_link_n;
+       unsigned long irq_rgmii_n;
+       unsigned long pcs_link;
+       unsigned long pcs_duplex;
+       unsigned long pcs_speed;
 };
 
 /* CSR Frequency Access Defines*/
@@ -138,37 +168,43 @@ struct stmmac_extra_stats {
 #define FLOW_TX                2
 #define FLOW_AUTO      (FLOW_TX | FLOW_RX)
 
-#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */
+/* PCS defines */
+#define STMMAC_PCS_RGMII       (1 << 0)
+#define STMMAC_PCS_SGMII       (1 << 1)
+#define STMMAC_PCS_TBI         (1 << 2)
+#define STMMAC_PCS_RTBI                (1 << 3)
+
+#define SF_DMA_MODE 1          /* DMA STORE-AND-FORWARD Operation Mode */
 
 /* DAM HW feature register fields */
-#define DMA_HW_FEAT_MIISEL     0x00000001 /* 10/100 Mbps Support */
-#define DMA_HW_FEAT_GMIISEL    0x00000002 /* 1000 Mbps Support */
-#define DMA_HW_FEAT_HDSEL      0x00000004 /* Half-Duplex Support */
-#define DMA_HW_FEAT_EXTHASHEN  0x00000008 /* Expanded DA Hash Filter */
-#define DMA_HW_FEAT_HASHSEL    0x00000010 /* HASH Filter */
-#define DMA_HW_FEAT_ADDMACADRSEL       0x00000020 /* Multiple MAC Addr Reg */
-#define DMA_HW_FEAT_PCSSEL     0x00000040 /* PCS registers */
-#define DMA_HW_FEAT_L3L4FLTREN 0x00000080 /* Layer 3 & Layer 4 Feature */
-#define DMA_HW_FEAT_SMASEL     0x00000100 /* SMA(MDIO) Interface */
-#define DMA_HW_FEAT_RWKSEL     0x00000200 /* PMT Remote Wakeup */
-#define DMA_HW_FEAT_MGKSEL     0x00000400 /* PMT Magic Packet */
-#define DMA_HW_FEAT_MMCSEL     0x00000800 /* RMON Module */
-#define DMA_HW_FEAT_TSVER1SEL  0x00001000 /* Only IEEE 1588-2002 Timestamp */
-#define DMA_HW_FEAT_TSVER2SEL  0x00002000 /* IEEE 1588-2008 Adv Timestamp */
-#define DMA_HW_FEAT_EEESEL     0x00004000 /* Energy Efficient Ethernet */
-#define DMA_HW_FEAT_AVSEL      0x00008000 /* AV Feature */
-#define DMA_HW_FEAT_TXCOESEL   0x00010000 /* Checksum Offload in Tx */
-#define DMA_HW_FEAT_RXTYP1COE  0x00020000 /* IP csum Offload(Type 1) in Rx */
-#define DMA_HW_FEAT_RXTYP2COE  0x00040000 /* IP csum Offload(Type 2) in Rx */
-#define DMA_HW_FEAT_RXFIFOSIZE 0x00080000 /* Rx FIFO > 2048 Bytes */
-#define DMA_HW_FEAT_RXCHCNT    0x00300000 /* No. of additional Rx Channels */
-#define DMA_HW_FEAT_TXCHCNT    0x00c00000 /* No. of additional Tx Channels */
-#define DMA_HW_FEAT_ENHDESSEL  0x01000000 /* Alternate (Enhanced Descriptor) */
-#define DMA_HW_FEAT_INTTSEN    0x02000000 /* Timestamping with Internal
-                                             System Time */
-#define DMA_HW_FEAT_FLEXIPPSEN 0x04000000 /* Flexible PPS Output */
-#define DMA_HW_FEAT_SAVLANINS  0x08000000 /* Source Addr or VLAN Insertion */
-#define DMA_HW_FEAT_ACTPHYIF   0x70000000 /* Active/selected PHY interface */
+#define DMA_HW_FEAT_MIISEL     0x00000001      /* 10/100 Mbps Support */
+#define DMA_HW_FEAT_GMIISEL    0x00000002      /* 1000 Mbps Support */
+#define DMA_HW_FEAT_HDSEL      0x00000004      /* Half-Duplex Support */
+#define DMA_HW_FEAT_EXTHASHEN  0x00000008      /* Expanded DA Hash Filter */
+#define DMA_HW_FEAT_HASHSEL    0x00000010      /* HASH Filter */
+#define DMA_HW_FEAT_ADDMAC     0x00000020      /* Multiple MAC Addr Reg */
+#define DMA_HW_FEAT_PCSSEL     0x00000040      /* PCS registers */
+#define DMA_HW_FEAT_L3L4FLTREN 0x00000080      /* Layer 3 & Layer 4 Feature */
+#define DMA_HW_FEAT_SMASEL     0x00000100      /* SMA(MDIO) Interface */
+#define DMA_HW_FEAT_RWKSEL     0x00000200      /* PMT Remote Wakeup */
+#define DMA_HW_FEAT_MGKSEL     0x00000400      /* PMT Magic Packet */
+#define DMA_HW_FEAT_MMCSEL     0x00000800      /* RMON Module */
+#define DMA_HW_FEAT_TSVER1SEL  0x00001000      /* Only IEEE 1588-2002 */
+#define DMA_HW_FEAT_TSVER2SEL  0x00002000      /* IEEE 1588-2008 PTPv2 */
+#define DMA_HW_FEAT_EEESEL     0x00004000      /* Energy Efficient Ethernet */
+#define DMA_HW_FEAT_AVSEL      0x00008000      /* AV Feature */
+#define DMA_HW_FEAT_TXCOESEL   0x00010000      /* Checksum Offload in Tx */
+#define DMA_HW_FEAT_RXTYP1COE  0x00020000      /* IP COE (Type 1) in Rx */
+#define DMA_HW_FEAT_RXTYP2COE  0x00040000      /* IP COE (Type 2) in Rx */
+#define DMA_HW_FEAT_RXFIFOSIZE 0x00080000      /* Rx FIFO > 2048 Bytes */
+#define DMA_HW_FEAT_RXCHCNT    0x00300000      /* No. additional Rx Channels */
+#define DMA_HW_FEAT_TXCHCNT    0x00c00000      /* No. additional Tx Channels */
+#define DMA_HW_FEAT_ENHDESSEL  0x01000000      /* Alternate Descriptor */
+/* Timestamping with Internal System Time */
+#define DMA_HW_FEAT_INTTSEN    0x02000000
+#define DMA_HW_FEAT_FLEXIPPSEN 0x04000000      /* Flexible PPS Output */
+#define DMA_HW_FEAT_SAVLANINS  0x08000000      /* Source Addr or VLAN */
+#define DMA_HW_FEAT_ACTPHYIF   0x70000000      /* Active/selected PHY iface */
 #define DEFAULT_DMA_PBL                8
 
 /* Max/Min RI Watchdog Timer count value */
@@ -180,7 +216,8 @@ struct stmmac_extra_stats {
 #define STMMAC_TX_MAX_FRAMES   256
 #define STMMAC_TX_FRAMES       64
 
-enum rx_frame_status { /* IPC status */
+/* Rx IPC status */
+enum rx_frame_status {
        good_frame = 0,
        discard_frame = 1,
        csum_none = 2,
@@ -194,17 +231,25 @@ enum dma_irq_status {
        handle_tx = 0x8,
 };
 
-enum core_specific_irq_mask {
-       core_mmc_tx_irq = 1,
-       core_mmc_rx_irq = 2,
-       core_mmc_rx_csum_offload_irq = 4,
-       core_irq_receive_pmt_irq = 8,
-       core_irq_tx_path_in_lpi_mode = 16,
-       core_irq_tx_path_exit_lpi_mode = 32,
-       core_irq_rx_path_in_lpi_mode = 64,
-       core_irq_rx_path_exit_lpi_mode = 128,
+#define        CORE_IRQ_TX_PATH_IN_LPI_MODE    (1 << 1)
+#define        CORE_IRQ_TX_PATH_EXIT_LPI_MODE  (1 << 2)
+#define        CORE_IRQ_RX_PATH_IN_LPI_MODE    (1 << 3)
+#define        CORE_IRQ_RX_PATH_EXIT_LPI_MODE  (1 << 4)
+
+#define        CORE_PCS_ANE_COMPLETE           (1 << 5)
+#define        CORE_PCS_LINK_STATUS            (1 << 6)
+#define        CORE_RGMII_IRQ                  (1 << 7)
+
+struct rgmii_adv {
+       unsigned int pause;
+       unsigned int duplex;
+       unsigned int lp_pause;
+       unsigned int lp_duplex;
 };
 
+#define STMMAC_PCS_PAUSE       1
+#define STMMAC_PCS_ASYM_PAUSE  2
+
 /* DMA HW capabilities */
 struct dma_features {
        unsigned int mbps_10_100;
@@ -217,9 +262,9 @@ struct dma_features {
        unsigned int pmt_remote_wake_up;
        unsigned int pmt_magic_frame;
        unsigned int rmon;
-       /* IEEE 1588-2002*/
+       /* IEEE 1588-2002 */
        unsigned int time_stamp;
-       /* IEEE 1588-2008*/
+       /* IEEE 1588-2008 */
        unsigned int atime_stamp;
        /* 802.3az - Energy-Efficient Ethernet (EEE) */
        unsigned int eee;
@@ -232,7 +277,7 @@ struct dma_features {
        /* TX and RX number of channels */
        unsigned int number_rx_channel;
        unsigned int number_tx_channel;
-       /* Alternate (enhanced) DESC mode*/
+       /* Alternate (enhanced) DESC mode */
        unsigned int enh_desc;
 };
 
@@ -255,23 +300,26 @@ struct dma_features {
 #define STMMAC_DEFAULT_LIT_LS_TIMER    0x3E8
 #define STMMAC_DEFAULT_TWT_LS_TIMER    0x0
 
+#define STMMAC_CHAIN_MODE      0x1
+#define STMMAC_RING_MODE       0x2
+
 struct stmmac_desc_ops {
        /* DMA RX descriptor ring initialization */
-       void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size,
-                             int disable_rx_ic);
+       void (*init_rx_desc) (struct dma_desc *p, int disable_rx_ic, int mode,
+                             int end);
        /* DMA TX descriptor ring initialization */
-       void (*init_tx_desc) (struct dma_desc *p, unsigned int ring_size);
+       void (*init_tx_desc) (struct dma_desc *p, int mode, int end);
 
        /* Invoked by the xmit function to prepare the tx descriptor */
        void (*prepare_tx_desc) (struct dma_desc *p, int is_fs, int len,
-                                int csum_flag);
+                                int csum_flag, int mode);
        /* Set/get the owner of the descriptor */
        void (*set_tx_owner) (struct dma_desc *p);
        int (*get_tx_owner) (struct dma_desc *p);
        /* Invoked by the xmit function to close the tx descriptor */
        void (*close_tx_desc) (struct dma_desc *p);
        /* Clean the tx descriptor as soon as the tx irq is received */
-       void (*release_tx_desc) (struct dma_desc *p);
+       void (*release_tx_desc) (struct dma_desc *p, int mode);
        /* Clear interrupt on tx frame completion. When this bit is
         * set an interrupt happens as soon as the frame is transmitted */
        void (*clear_tx_ic) (struct dma_desc *p);
@@ -290,12 +338,22 @@ struct stmmac_desc_ops {
        /* Return the reception status looking at the RDES1 */
        int (*rx_status) (void *data, struct stmmac_extra_stats *x,
                          struct dma_desc *p);
+       void (*rx_extended_status) (void *data, struct stmmac_extra_stats *x,
+                                   struct dma_extended_desc *p);
+       /* Set tx timestamp enable bit */
+       void (*enable_tx_timestamp) (struct dma_desc *p);
+       /* get tx timestamp status */
+       int (*get_tx_timestamp_status) (struct dma_desc *p);
+       /* get timestamp value */
+        u64(*get_timestamp) (void *desc, u32 ats);
+       /* get rx timestamp status */
+       int (*get_rx_timestamp_status) (void *desc, u32 ats);
 };
 
 struct stmmac_dma_ops {
        /* DMA core initialization */
        int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb,
-                    int burst_len, u32 dma_tx, u32 dma_rx);
+                    int burst_len, u32 dma_tx, u32 dma_rx, int atds);
        /* Dump DMA registers */
        void (*dump_regs) (void __iomem *ioaddr);
        /* Set tx/rx threshold in the csr6 register
@@ -321,13 +379,14 @@ struct stmmac_dma_ops {
 
 struct stmmac_ops {
        /* MAC core initialization */
-       void (*core_init) (void __iomem *ioaddr) ____cacheline_aligned;
+       void (*core_init) (void __iomem *ioaddr);
        /* Enable and verify that the IPC module is supported */
        int (*rx_ipc) (void __iomem *ioaddr);
        /* Dump MAC registers */
        void (*dump_regs) (void __iomem *ioaddr);
        /* Handle extra events on specific interrupts hw dependent */
-       int (*host_irq_status) (void __iomem *ioaddr);
+       int (*host_irq_status) (void __iomem *ioaddr,
+                               struct stmmac_extra_stats *x);
        /* Multicast filter setting */
        void (*set_filter) (struct net_device *dev, int id);
        /* Flow control setting */
@@ -344,6 +403,18 @@ struct stmmac_ops {
        void (*reset_eee_mode) (void __iomem *ioaddr);
        void (*set_eee_timer) (void __iomem *ioaddr, int ls, int tw);
        void (*set_eee_pls) (void __iomem *ioaddr, int link);
+       void (*ctrl_ane) (void __iomem *ioaddr, bool restart);
+       void (*get_adv) (void __iomem *ioaddr, struct rgmii_adv *adv);
+};
+
+struct stmmac_hwtimestamp {
+       void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
+       void (*config_sub_second_increment) (void __iomem *ioaddr);
+       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,
+                              int add_sub);
+        u64(*get_systime) (void __iomem *ioaddr);
 };
 
 struct mac_link {
@@ -360,19 +431,28 @@ struct mii_regs {
 struct stmmac_ring_mode_ops {
        unsigned int (*is_jumbo_frm) (int len, int ehn_desc);
        unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum);
-       void (*refill_desc3) (int bfsize, struct dma_desc *p);
-       void (*init_desc3) (int des3_as_data_buf, struct dma_desc *p);
-       void (*init_dma_chain) (struct dma_desc *des, dma_addr_t phy_addr,
-                               unsigned int size);
-       void (*clean_desc3) (struct dma_desc *p);
+       void (*refill_desc3) (void *priv, struct dma_desc *p);
+       void (*init_desc3) (struct dma_desc *p);
+       void (*clean_desc3) (void *priv, struct dma_desc *p);
        int (*set_16kib_bfsize) (int mtu);
 };
 
+struct stmmac_chain_mode_ops {
+       void (*init) (void *des, dma_addr_t phy_addr, unsigned int size,
+                     unsigned int extend_desc);
+       unsigned int (*is_jumbo_frm) (int len, int ehn_desc);
+       unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum);
+       void (*refill_desc3) (void *priv, struct dma_desc *p);
+       void (*clean_desc3) (void *priv, struct dma_desc *p);
+};
+
 struct mac_device_info {
-       const struct stmmac_ops         *mac;
-       const struct stmmac_desc_ops    *desc;
-       const struct stmmac_dma_ops     *dma;
-       const struct stmmac_ring_mode_ops       *ring;
+       const struct stmmac_ops *mac;
+       const struct stmmac_desc_ops *desc;
+       const struct stmmac_dma_ops *dma;
+       const struct stmmac_ring_mode_ops *ring;
+       const struct stmmac_chain_mode_ops *chain;
+       const struct stmmac_hwtimestamp *ptp;
        struct mii_regs mii;    /* MII register Addresses */
        struct mac_link link;
        unsigned int synopsys_uid;
@@ -390,5 +470,6 @@ extern void stmmac_set_mac(void __iomem *ioaddr, bool enable);
 
 extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
 extern const struct stmmac_ring_mode_ops ring_mode_ops;
+extern const struct stmmac_chain_mode_ops chain_mode_ops;
 
 #endif /* __COMMON_H__ */
index 223adf95fd0374e465447adc52d854491721fe4f..ad399603801800267d2704640d40251927a93396 100644 (file)
@@ -24,6 +24,7 @@
 #ifndef __DESCS_H__
 #define __DESCS_H__
 
+/* Basic descriptor structure for normal and alternate descriptors */
 struct dma_desc {
        /* Receive descriptor */
        union {
@@ -60,7 +61,7 @@ struct dma_desc {
                } rx;
                struct {
                        /* RDES0 */
-                       u32 payload_csum_error:1;
+                       u32 rx_mac_addr:1;
                        u32 crc_error:1;
                        u32 dribbling:1;
                        u32 error_gmii:1;
@@ -162,13 +163,57 @@ struct dma_desc {
        unsigned int des3;
 };
 
+/* Extended descriptor structure (supported by new SYNP GMAC generations) */
+struct dma_extended_desc {
+       struct dma_desc basic;
+       union {
+               struct {
+                       u32 ip_payload_type:3;
+                       u32 ip_hdr_err:1;
+                       u32 ip_payload_err:1;
+                       u32 ip_csum_bypassed:1;
+                       u32 ipv4_pkt_rcvd:1;
+                       u32 ipv6_pkt_rcvd:1;
+                       u32 msg_type:4;
+                       u32 ptp_frame_type:1;
+                       u32 ptp_ver:1;
+                       u32 timestamp_dropped:1;
+                       u32 reserved:1;
+                       u32 av_pkt_rcvd:1;
+                       u32 av_tagged_pkt_rcvd:1;
+                       u32 vlan_tag_priority_val:3;
+                       u32 reserved3:3;
+                       u32 l3_filter_match:1;
+                       u32 l4_filter_match:1;
+                       u32 l3_l4_filter_no_match:2;
+                       u32 reserved4:4;
+               } erx;
+               struct {
+                       u32 reserved;
+               } etx;
+       } des4;
+       unsigned int des5;      /* Reserved */
+       unsigned int des6;      /* Tx/Rx Timestamp Low */
+       unsigned int des7;      /* Tx/Rx Timestamp High */
+};
+
 /* Transmit checksum insertion control */
 enum tdes_csum_insertion {
        cic_disabled = 0,       /* Checksum Insertion Control */
        cic_only_ip = 1,        /* Only IP header */
-       cic_no_pseudoheader = 2,        /* IP header but pseudoheader
-                                        * is not calculated */
+       /* IP header but pseudoheader is not calculated */
+       cic_no_pseudoheader = 2,
        cic_full = 3,           /* IP header and pseudoheader */
 };
 
+/* Extended RDES4 definitions */
+#define RDES_EXT_NO_PTP                        0
+#define RDES_EXT_SYNC                  0x1
+#define RDES_EXT_FOLLOW_UP             0x2
+#define RDES_EXT_DELAY_REQ             0x3
+#define RDES_EXT_DELAY_RESP            0x4
+#define RDES_EXT_PDELAY_REQ            0x5
+#define RDES_EXT_PDELAY_RESP           0x6
+#define RDES_EXT_PDELAY_FOLLOW_UP      0x7
+
 #endif /* __DESCS_H__ */
index 7ee9499a6e385a77b9b2110b9578750e767745ab..6f2cc78c5cf513ddcf8abc6e9094725a1ebfe977 100644 (file)
 #ifndef __DESC_COM_H__
 #define __DESC_COM_H__
 
-#if defined(CONFIG_STMMAC_RING)
-static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end)
+/* Specific functions used for Ring mode */
+
+/* Enhanced descriptors */
+static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end)
 {
        p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1;
        if (end)
                p->des01.erx.end_ring = 1;
 }
 
-static inline void ehn_desc_tx_set_on_ring_chain(struct dma_desc *p, int end)
+static inline void ehn_desc_tx_set_on_ring(struct dma_desc *p, int end)
 {
        if (end)
                p->des01.etx.end_ring = 1;
 }
 
-static inline void enh_desc_end_tx_desc(struct dma_desc *p, int ter)
+static inline void enh_desc_end_tx_desc_on_ring(struct dma_desc *p, int ter)
 {
        p->des01.etx.end_ring = ter;
 }
 
-static inline void enh_set_tx_desc_len(struct dma_desc *p, int len)
+static inline void enh_set_tx_desc_len_on_ring(struct dma_desc *p, int len)
 {
        if (unlikely(len > BUF_SIZE_4KiB)) {
                p->des01.etx.buffer1_size = BUF_SIZE_4KiB;
@@ -58,25 +60,26 @@ static inline void enh_set_tx_desc_len(struct dma_desc *p, int len)
                p->des01.etx.buffer1_size = len;
 }
 
-static inline void ndesc_rx_set_on_ring_chain(struct dma_desc *p, int end)
+/* Normal descriptors */
+static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end)
 {
        p->des01.rx.buffer2_size = BUF_SIZE_2KiB - 1;
        if (end)
                p->des01.rx.end_ring = 1;
 }
 
-static inline void ndesc_tx_set_on_ring_chain(struct dma_desc *p, int end)
+static inline void ndesc_tx_set_on_ring(struct dma_desc *p, int end)
 {
        if (end)
                p->des01.tx.end_ring = 1;
 }
 
-static inline void ndesc_end_tx_desc(struct dma_desc *p, int ter)
+static inline void ndesc_end_tx_desc_on_ring(struct dma_desc *p, int ter)
 {
        p->des01.tx.end_ring = ter;
 }
 
-static inline void norm_set_tx_desc_len(struct dma_desc *p, int len)
+static inline void norm_set_tx_desc_len_on_ring(struct dma_desc *p, int len)
 {
        if (unlikely(len > BUF_SIZE_2KiB)) {
                p->des01.etx.buffer1_size = BUF_SIZE_2KiB - 1;
@@ -85,47 +88,47 @@ static inline void norm_set_tx_desc_len(struct dma_desc *p, int len)
                p->des01.tx.buffer1_size = len;
 }
 
-#else
+/* Specific functions used for Chain mode */
 
-static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end)
+/* Enhanced descriptors */
+static inline void ehn_desc_rx_set_on_chain(struct dma_desc *p, int end)
 {
        p->des01.erx.second_address_chained = 1;
 }
 
-static inline void ehn_desc_tx_set_on_ring_chain(struct dma_desc *p, int end)
+static inline void ehn_desc_tx_set_on_chain(struct dma_desc *p, int end)
 {
        p->des01.etx.second_address_chained = 1;
 }
 
-static inline void enh_desc_end_tx_desc(struct dma_desc *p, int ter)
+static inline void enh_desc_end_tx_desc_on_chain(struct dma_desc *p, int ter)
 {
        p->des01.etx.second_address_chained = 1;
 }
 
-static inline void enh_set_tx_desc_len(struct dma_desc *p, int len)
+static inline void enh_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
 {
        p->des01.etx.buffer1_size = len;
 }
 
-static inline void ndesc_rx_set_on_ring_chain(struct dma_desc *p, int end)
+/* Normal descriptors */
+static inline void ndesc_rx_set_on_chain(struct dma_desc *p, int end)
 {
        p->des01.rx.second_address_chained = 1;
 }
 
-static inline void ndesc_tx_set_on_ring_chain(struct dma_desc *p, int ring_size)
+static inline void ndesc_tx_set_on_chain(struct dma_desc *p, int ring_size)
 {
        p->des01.tx.second_address_chained = 1;
 }
 
-static inline void ndesc_end_tx_desc(struct dma_desc *p, int ter)
+static inline void ndesc_end_tx_desc_on_chain(struct dma_desc *p, int ter)
 {
        p->des01.tx.second_address_chained = 1;
 }
 
-static inline void norm_set_tx_desc_len(struct dma_desc *p, int len)
+static inline void norm_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
 {
        p->des01.tx.buffer1_size = len;
 }
-#endif
-
 #endif /* __DESC_COM_H__ */
index 7ad56afd63247a9e3c73c27c923109e48e5f638d..c12aabb8cf93c9f72627b939cf7ed64b8a0819dd 100644 (file)
@@ -89,13 +89,46 @@ enum power_event {
                                (reg * 8))
 #define GMAC_MAX_PERFECT_ADDRESSES     32
 
+/* PCS registers (AN/TBI/SGMII/RGMII) offset */
 #define GMAC_AN_CTRL   0x000000c0      /* AN control */
 #define GMAC_AN_STATUS 0x000000c4      /* AN status */
 #define GMAC_ANE_ADV   0x000000c8      /* Auto-Neg. Advertisement */
-#define GMAC_ANE_LINK  0x000000cc      /* Auto-Neg. link partener ability */
+#define GMAC_ANE_LPA   0x000000cc      /* Auto-Neg. link partener ability */
 #define GMAC_ANE_EXP   0x000000d0      /* ANE expansion */
 #define GMAC_TBI       0x000000d4      /* TBI extend status */
-#define GMAC_GMII_STATUS 0x000000d8    /* S/R-GMII status */
+#define GMAC_S_R_GMII  0x000000d8      /* SGMII RGMII status */
+
+/* AN Configuration defines */
+#define GMAC_AN_CTRL_RAN       0x00000200      /* Restart Auto-Negotiation */
+#define GMAC_AN_CTRL_ANE       0x00001000      /* Auto-Negotiation Enable */
+#define GMAC_AN_CTRL_ELE       0x00004000      /* External Loopback Enable */
+#define GMAC_AN_CTRL_ECD       0x00010000      /* Enable Comma Detect */
+#define GMAC_AN_CTRL_LR                0x00020000      /* Lock to Reference */
+#define GMAC_AN_CTRL_SGMRAL    0x00040000      /* SGMII RAL Control */
+
+/* AN Status defines */
+#define GMAC_AN_STATUS_LS      0x00000004      /* Link Status 0:down 1:up */
+#define GMAC_AN_STATUS_ANA     0x00000008      /* Auto-Negotiation Ability */
+#define GMAC_AN_STATUS_ANC     0x00000020      /* Auto-Negotiation Complete */
+#define GMAC_AN_STATUS_ES      0x00000100      /* Extended Status */
+
+/* Register 54 (SGMII/RGMII status register) */
+#define GMAC_S_R_GMII_LINK             0x8
+#define GMAC_S_R_GMII_SPEED            0x5
+#define GMAC_S_R_GMII_SPEED_SHIFT      0x1
+#define GMAC_S_R_GMII_MODE             0x1
+#define GMAC_S_R_GMII_SPEED_125                2
+#define GMAC_S_R_GMII_SPEED_25         1
+
+/* Common ADV and LPA defines */
+#define GMAC_ANE_FD            (1 << 5)
+#define GMAC_ANE_HD            (1 << 6)
+#define GMAC_ANE_PSE           (3 << 7)
+#define GMAC_ANE_PSE_SHIFT     7
+
+ /* GMAC Configuration defines */
+#define GMAC_CONTROL_TC        0x01000000      /* Transmit Conf. in RGMII/SGMII */
+#define GMAC_CONTROL_WD        0x00800000      /* Disable Watchdog on receive */
 
 /* GMAC Configuration defines */
 #define GMAC_CONTROL_TC        0x01000000      /* Transmit Conf. in RGMII/SGMII */
@@ -108,19 +141,19 @@ enum inter_frame_gap {
        GMAC_CONTROL_IFG_80 = 0x00020000,
        GMAC_CONTROL_IFG_40 = 0x000e0000,
 };
-#define GMAC_CONTROL_DCRS      0x00010000 /* Disable carrier sense during tx */
-#define GMAC_CONTROL_PS                0x00008000 /* Port Select 0:GMI 1:MII */
-#define GMAC_CONTROL_FES       0x00004000 /* Speed 0:10 1:100 */
-#define GMAC_CONTROL_DO                0x00002000 /* Disable Rx Own */
-#define GMAC_CONTROL_LM                0x00001000 /* Loop-back mode */
-#define GMAC_CONTROL_DM                0x00000800 /* Duplex Mode */
-#define GMAC_CONTROL_IPC       0x00000400 /* Checksum Offload */
-#define GMAC_CONTROL_DR                0x00000200 /* Disable Retry */
-#define GMAC_CONTROL_LUD       0x00000100 /* Link up/down */
-#define GMAC_CONTROL_ACS       0x00000080 /* Automatic Pad/FCS Stripping */
-#define GMAC_CONTROL_DC                0x00000010 /* Deferral Check */
-#define GMAC_CONTROL_TE                0x00000008 /* Transmitter Enable */
-#define GMAC_CONTROL_RE                0x00000004 /* Receiver Enable */
+#define GMAC_CONTROL_DCRS      0x00010000      /* Disable carrier sense */
+#define GMAC_CONTROL_PS                0x00008000      /* Port Select 0:GMI 1:MII */
+#define GMAC_CONTROL_FES       0x00004000      /* Speed 0:10 1:100 */
+#define GMAC_CONTROL_DO                0x00002000      /* Disable Rx Own */
+#define GMAC_CONTROL_LM                0x00001000      /* Loop-back mode */
+#define GMAC_CONTROL_DM                0x00000800      /* Duplex Mode */
+#define GMAC_CONTROL_IPC       0x00000400      /* Checksum Offload */
+#define GMAC_CONTROL_DR                0x00000200      /* Disable Retry */
+#define GMAC_CONTROL_LUD       0x00000100      /* Link up/down */
+#define GMAC_CONTROL_ACS       0x00000080      /* Auto Pad/FCS Stripping */
+#define GMAC_CONTROL_DC                0x00000010      /* Deferral Check */
+#define GMAC_CONTROL_TE                0x00000008      /* Transmitter Enable */
+#define GMAC_CONTROL_RE                0x00000004      /* Receiver Enable */
 
 #define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | GMAC_CONTROL_ACS | \
                        GMAC_CONTROL_JE | GMAC_CONTROL_BE)
@@ -151,15 +184,16 @@ enum inter_frame_gap {
 #define DMA_BUS_MODE_SFT_RESET 0x00000001      /* Software Reset */
 #define DMA_BUS_MODE_DA                0x00000002      /* Arbitration scheme */
 #define DMA_BUS_MODE_DSL_MASK  0x0000007c      /* Descriptor Skip Length */
-#define DMA_BUS_MODE_DSL_SHIFT 2       /*   (in DWORDS)      */
+#define DMA_BUS_MODE_DSL_SHIFT 2               /*   (in DWORDS)      */
 /* Programmable burst length (passed thorugh platform)*/
 #define DMA_BUS_MODE_PBL_MASK  0x00003f00      /* Programmable Burst Len */
 #define DMA_BUS_MODE_PBL_SHIFT 8
+#define DMA_BUS_MODE_ATDS      0x00000080      /* Alternate Descriptor Size */
 
 enum rx_tx_priority_ratio {
-       double_ratio = 0x00004000,      /*2:1 */
-       triple_ratio = 0x00008000,      /*3:1 */
-       quadruple_ratio = 0x0000c000,   /*4:1 */
+       double_ratio = 0x00004000,      /* 2:1 */
+       triple_ratio = 0x00008000,      /* 3:1 */
+       quadruple_ratio = 0x0000c000,   /* 4:1 */
 };
 
 #define DMA_BUS_MODE_FB                0x00010000      /* Fixed burst */
@@ -179,9 +213,10 @@ enum rx_tx_priority_ratio {
 #define DMA_BUS_FB               0x00010000    /* Fixed Burst */
 
 /* DMA operation mode defines (start/stop tx/rx are placed in common header)*/
-#define DMA_CONTROL_DT         0x04000000 /* Disable Drop TCP/IP csum error */
-#define DMA_CONTROL_RSF                0x02000000 /* Receive Store and Forward */
-#define DMA_CONTROL_DFF                0x01000000 /* Disaable flushing */
+/* Disable Drop TCP/IP csum error */
+#define DMA_CONTROL_DT         0x04000000
+#define DMA_CONTROL_RSF                0x02000000      /* Receive Store and Forward */
+#define DMA_CONTROL_DFF                0x01000000      /* Disaable flushing */
 /* Threshold for Activating the FC */
 enum rfa {
        act_full_minus_1 = 0x00800000,
@@ -196,7 +231,7 @@ enum rfd {
        deac_full_minus_3 = 0x00401000,
        deac_full_minus_4 = 0x00401800,
 };
-#define DMA_CONTROL_TSF                0x00200000 /* Transmit  Store and Forward */
+#define DMA_CONTROL_TSF        0x00200000      /* Transmit  Store and Forward */
 
 enum ttc_control {
        DMA_CONTROL_TTC_64 = 0x00000000,
index bfe02260549840c17a047670ddf47a28c1ddecf5..7e05e8d0f1c2b3fa8e714a8ada53d5b5be3c4400 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <linux/crc32.h>
 #include <linux/slab.h>
+#include <linux/ethtool.h>
 #include <asm/io.h>
 #include "dwmac1000.h"
 
@@ -71,22 +72,22 @@ static void dwmac1000_dump_regs(void __iomem *ioaddr)
 }
 
 static void dwmac1000_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,
-                               unsigned int reg_n)
+                                   unsigned int reg_n)
 {
        stmmac_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
-                               GMAC_ADDR_LOW(reg_n));
+                           GMAC_ADDR_LOW(reg_n));
 }
 
 static void dwmac1000_get_umac_addr(void __iomem *ioaddr, unsigned char *addr,
-                               unsigned int reg_n)
+                                   unsigned int reg_n)
 {
        stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
-                               GMAC_ADDR_LOW(reg_n));
+                           GMAC_ADDR_LOW(reg_n));
 }
 
 static void dwmac1000_set_filter(struct net_device *dev, int id)
 {
-       void __iomem *ioaddr = (void __iomem *) dev->base_addr;
+       void __iomem *ioaddr = (void __iomem *)dev->base_addr;
        unsigned int value = 0;
        unsigned int perfect_addr_number;
 
@@ -96,7 +97,7 @@ static void dwmac1000_set_filter(struct net_device *dev, int id)
        if (dev->flags & IFF_PROMISC)
                value = GMAC_FRAME_FILTER_PR;
        else if ((netdev_mc_count(dev) > HASH_TABLE_SIZE)
-                  || (dev->flags & IFF_ALLMULTI)) {
+                || (dev->flags & IFF_ALLMULTI)) {
                value = GMAC_FRAME_FILTER_PM;   /* pass all multi */
                writel(0xffffffff, ioaddr + GMAC_HASH_HIGH);
                writel(0xffffffff, ioaddr + GMAC_HASH_LOW);
@@ -110,12 +111,13 @@ static void dwmac1000_set_filter(struct net_device *dev, int id)
                memset(mc_filter, 0, sizeof(mc_filter));
                netdev_for_each_mc_addr(ha, dev) {
                        /* The upper 6 bits of the calculated CRC are used to
-                          index the contens of the hash table */
-                       int bit_nr =
-                           bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26;
+                        * index the contens of the hash table
+                        */
+                       int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26;
                        /* The most significant bit determines the register to
                         * use (H/L) while the other 5 bits determine the bit
-                        * within the register. */
+                        * within the register.
+                        */
                        mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
                }
                writel(mc_filter[0], ioaddr + GMAC_HASH_LOW);
@@ -128,10 +130,11 @@ static void dwmac1000_set_filter(struct net_device *dev, int id)
        else
                perfect_addr_number = GMAC_MAX_PERFECT_ADDRESSES / 2;
 
-       /* Handle multiple unicast addresses (perfect filtering)*/
+       /* Handle multiple unicast addresses (perfect filtering) */
        if (netdev_uc_count(dev) > perfect_addr_number)
-               /* Switch to promiscuous mode is more than 16 addrs
-                  are required */
+               /* Switch to promiscuous mode if more than 16 addrs
+                * are required
+                */
                value |= GMAC_FRAME_FILTER_PR;
        else {
                int reg = 1;
@@ -149,13 +152,13 @@ static void dwmac1000_set_filter(struct net_device *dev, int id)
 #endif
        writel(value, ioaddr + GMAC_FRAME_FILTER);
 
-       CHIP_DBG(KERN_INFO "\tFrame Filter reg: 0x%08x\n\tHash regs: "
-           "HI 0x%08x, LO 0x%08x\n", readl(ioaddr + GMAC_FRAME_FILTER),
-           readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW));
+       CHIP_DBG(KERN_INFO "\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n",
+                readl(ioaddr + GMAC_FRAME_FILTER),
+                readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW));
 }
 
 static void dwmac1000_flow_ctrl(void __iomem *ioaddr, unsigned int duplex,
-                          unsigned int fc, unsigned int pause_time)
+                               unsigned int fc, unsigned int pause_time)
 {
        unsigned int flow = 0;
 
@@ -193,74 +196,106 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)
        writel(pmt, ioaddr + GMAC_PMT);
 }
 
-
-static int dwmac1000_irq_status(void __iomem *ioaddr)
+static int dwmac1000_irq_status(void __iomem *ioaddr,
+                               struct stmmac_extra_stats *x)
 {
        u32 intr_status = readl(ioaddr + GMAC_INT_STATUS);
-       int status = 0;
+       int ret = 0;
 
        /* Not used events (e.g. MMC interrupts) are not handled. */
        if ((intr_status & mmc_tx_irq)) {
                CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n",
-                   readl(ioaddr + GMAC_MMC_TX_INTR));
-               status |= core_mmc_tx_irq;
+                        readl(ioaddr + GMAC_MMC_TX_INTR));
+               x->mmc_tx_irq_n++;
        }
        if (unlikely(intr_status & mmc_rx_irq)) {
                CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n",
-                   readl(ioaddr + GMAC_MMC_RX_INTR));
-               status |= core_mmc_rx_irq;
+                        readl(ioaddr + GMAC_MMC_RX_INTR));
+               x->mmc_rx_irq_n++;
        }
        if (unlikely(intr_status & mmc_rx_csum_offload_irq)) {
                CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n",
-                   readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
-               status |= core_mmc_rx_csum_offload_irq;
+                        readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
+               x->mmc_rx_csum_offload_irq_n++;
        }
        if (unlikely(intr_status & pmt_irq)) {
                CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n");
-               /* clear the PMT bits 5 and 6 by reading the PMT
-                * status register. */
+               /* clear the PMT bits 5 and 6 by reading the PMT status reg */
                readl(ioaddr + GMAC_PMT);
-               status |= core_irq_receive_pmt_irq;
+               x->irq_receive_pmt_irq_n++;
        }
        /* MAC trx/rx EEE LPI entry/exit interrupts */
        if (intr_status & lpiis_irq) {
                /* Clean LPI interrupt by reading the Reg 12 */
-               u32 lpi_status = readl(ioaddr + LPI_CTRL_STATUS);
+               ret = readl(ioaddr + LPI_CTRL_STATUS);
 
-               if (lpi_status & LPI_CTRL_STATUS_TLPIEN) {
+               if (ret & LPI_CTRL_STATUS_TLPIEN) {
                        CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n");
-                       status |= core_irq_tx_path_in_lpi_mode;
+                       x->irq_tx_path_in_lpi_mode_n++;
                }
-               if (lpi_status & LPI_CTRL_STATUS_TLPIEX) {
+               if (ret & LPI_CTRL_STATUS_TLPIEX) {
                        CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n");
-                       status |= core_irq_tx_path_exit_lpi_mode;
+                       x->irq_tx_path_exit_lpi_mode_n++;
                }
-               if (lpi_status & LPI_CTRL_STATUS_RLPIEN) {
+               if (ret & LPI_CTRL_STATUS_RLPIEN) {
                        CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n");
-                       status |= core_irq_rx_path_in_lpi_mode;
+                       x->irq_rx_path_in_lpi_mode_n++;
                }
-               if (lpi_status & LPI_CTRL_STATUS_RLPIEX) {
+               if (ret & LPI_CTRL_STATUS_RLPIEX) {
                        CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n");
-                       status |= core_irq_rx_path_exit_lpi_mode;
+                       x->irq_rx_path_exit_lpi_mode_n++;
+               }
+       }
+
+       if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) {
+               CHIP_DBG(KERN_INFO "GMAC PCS ANE IRQ\n");
+               readl(ioaddr + GMAC_AN_STATUS);
+               x->irq_pcs_ane_n++;
+       }
+       if (intr_status & rgmii_irq) {
+               u32 status = readl(ioaddr + GMAC_S_R_GMII);
+               CHIP_DBG(KERN_INFO "GMAC RGMII/SGMII interrupt\n");
+               x->irq_rgmii_n++;
+
+               /* Save and dump the link status. */
+               if (status & GMAC_S_R_GMII_LINK) {
+                       int speed_value = (status & GMAC_S_R_GMII_SPEED) >>
+                           GMAC_S_R_GMII_SPEED_SHIFT;
+                       x->pcs_duplex = (status & GMAC_S_R_GMII_MODE);
+
+                       if (speed_value == GMAC_S_R_GMII_SPEED_125)
+                               x->pcs_speed = SPEED_1000;
+                       else if (speed_value == GMAC_S_R_GMII_SPEED_25)
+                               x->pcs_speed = SPEED_100;
+                       else
+                               x->pcs_speed = SPEED_10;
+
+                       x->pcs_link = 1;
+                       pr_debug("Link is Up - %d/%s\n", (int)x->pcs_speed,
+                                x->pcs_duplex ? "Full" : "Half");
+               } else {
+                       x->pcs_link = 0;
+                       pr_debug("Link is Down\n");
                }
        }
 
-       return status;
+       return ret;
 }
 
-static void  dwmac1000_set_eee_mode(void __iomem *ioaddr)
+static void dwmac1000_set_eee_mode(void __iomem *ioaddr)
 {
        u32 value;
 
        /* Enable the link status receive on RGMII, SGMII ore SMII
         * receive path and instruct the transmit to enter in LPI
-        * state. */
+        * state.
+        */
        value = readl(ioaddr + LPI_CTRL_STATUS);
        value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
        writel(value, ioaddr + LPI_CTRL_STATUS);
 }
 
-static void  dwmac1000_reset_eee_mode(void __iomem *ioaddr)
+static void dwmac1000_reset_eee_mode(void __iomem *ioaddr)
 {
        u32 value;
 
@@ -269,7 +304,7 @@ static void  dwmac1000_reset_eee_mode(void __iomem *ioaddr)
        writel(value, ioaddr + LPI_CTRL_STATUS);
 }
 
-static void  dwmac1000_set_eee_pls(void __iomem *ioaddr, int link)
+static void dwmac1000_set_eee_pls(void __iomem *ioaddr, int link)
 {
        u32 value;
 
@@ -283,7 +318,7 @@ static void  dwmac1000_set_eee_pls(void __iomem *ioaddr, int link)
        writel(value, ioaddr + LPI_CTRL_STATUS);
 }
 
-static void  dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)
+static void dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)
 {
        int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16);
 
@@ -297,6 +332,41 @@ static void  dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)
        writel(value, ioaddr + LPI_TIMER_CTRL);
 }
 
+static void dwmac1000_ctrl_ane(void __iomem *ioaddr, bool restart)
+{
+       u32 value;
+
+       value = readl(ioaddr + GMAC_AN_CTRL);
+       /* auto negotiation enable and External Loopback enable */
+       value = GMAC_AN_CTRL_ANE | GMAC_AN_CTRL_ELE;
+
+       if (restart)
+               value |= GMAC_AN_CTRL_RAN;
+
+       writel(value, ioaddr + GMAC_AN_CTRL);
+}
+
+static void dwmac1000_get_adv(void __iomem *ioaddr, struct rgmii_adv *adv)
+{
+       u32 value = readl(ioaddr + GMAC_ANE_ADV);
+
+       if (value & GMAC_ANE_FD)
+               adv->duplex = DUPLEX_FULL;
+       if (value & GMAC_ANE_HD)
+               adv->duplex |= DUPLEX_HALF;
+
+       adv->pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT;
+
+       value = readl(ioaddr + GMAC_ANE_LPA);
+
+       if (value & GMAC_ANE_FD)
+               adv->lp_duplex = DUPLEX_FULL;
+       if (value & GMAC_ANE_HD)
+               adv->lp_duplex = DUPLEX_HALF;
+
+       adv->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT;
+}
+
 static const struct stmmac_ops dwmac1000_ops = {
        .core_init = dwmac1000_core_init,
        .rx_ipc = dwmac1000_rx_ipc_enable,
@@ -307,10 +377,12 @@ static const struct stmmac_ops dwmac1000_ops = {
        .pmt = dwmac1000_pmt,
        .set_umac_addr = dwmac1000_set_umac_addr,
        .get_umac_addr = dwmac1000_get_umac_addr,
-       .set_eee_mode =  dwmac1000_set_eee_mode,
-       .reset_eee_mode =  dwmac1000_reset_eee_mode,
-       .set_eee_timer =  dwmac1000_set_eee_timer,
-       .set_eee_pls =  dwmac1000_set_eee_pls,
+       .set_eee_mode = dwmac1000_set_eee_mode,
+       .reset_eee_mode = dwmac1000_reset_eee_mode,
+       .set_eee_timer = dwmac1000_set_eee_timer,
+       .set_eee_pls = dwmac1000_set_eee_pls,
+       .ctrl_ane = dwmac1000_ctrl_ane,
+       .get_adv = dwmac1000_get_adv,
 };
 
 struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr)
index bf83c03bfd06f3699164de132968622690a10965..2c431b6160586c879e443cd8fb06fef49cb8f71e 100644 (file)
@@ -30,8 +30,8 @@
 #include "dwmac1000.h"
 #include "dwmac_dma.h"
 
-static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,
-                             int mb, int burst_len, u32 dma_tx, u32 dma_rx)
+static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb,
+                             int burst_len, u32 dma_tx, u32 dma_rx, int atds)
 {
        u32 value = readl(ioaddr + DMA_BUS_MODE);
        int limit;
@@ -60,7 +60,7 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,
         * depending on pbl value.
         */
        value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) |
-               (pbl << DMA_BUS_MODE_RPBL_SHIFT));
+                                   (pbl << DMA_BUS_MODE_RPBL_SHIFT));
 
        /* Set the Fixed burst mode */
        if (fb)
@@ -73,6 +73,10 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,
 #ifdef CONFIG_STMMAC_DA
        value |= DMA_BUS_MODE_DA;       /* Rx has priority over tx */
 #endif
+
+       if (atds)
+               value |= DMA_BUS_MODE_ATDS;
+
        writel(value, ioaddr + DMA_BUS_MODE);
 
        /* In case of GMAC AXI configuration, program the DMA_AXI_BUS_MODE
@@ -90,14 +94,16 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,
         *
         *  For Non Fixed Burst Mode: provide the maximum value of the
         *  burst length. Any burst equal or below the provided burst
-        *  length would be allowed to perform. */
+        *  length would be allowed to perform.
+        */
        writel(burst_len, ioaddr + DMA_AXI_BUS_MODE);
 
        /* Mask interrupts by writing to CSR7 */
        writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
 
-       /* The base address of the RX/TX descriptor lists must be written into
-        * DMA CSR3 and CSR4, respectively. */
+       /* RX/TX descriptor base address lists must be written into
+        * DMA CSR3 and CSR4, respectively
+        */
        writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR);
        writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR);
 
@@ -105,7 +111,7 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,
 }
 
 static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
-                                   int rxmode)
+                                        int rxmode)
 {
        u32 csr6 = readl(ioaddr + DMA_CONTROL);
 
@@ -114,11 +120,12 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
                /* Transmit COE type 2 cannot be done in cut-through mode. */
                csr6 |= DMA_CONTROL_TSF;
                /* Operating on second frame increase the performance
-                * especially when transmit store-and-forward is used.*/
+                * especially when transmit store-and-forward is used.
+                */
                csr6 |= DMA_CONTROL_OSF;
        } else {
-               CHIP_DBG(KERN_DEBUG "GMAC: disabling TX store and forward mode"
-                             " (threshold = %d)\n", txmode);
+               CHIP_DBG(KERN_DEBUG "GMAC: disabling TX SF (threshold %d)\n",
+                        txmode);
                csr6 &= ~DMA_CONTROL_TSF;
                csr6 &= DMA_CONTROL_TC_TX_MASK;
                /* Set the transmit threshold */
@@ -138,8 +145,8 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
                CHIP_DBG(KERN_DEBUG "GMAC: enable RX store and forward mode\n");
                csr6 |= DMA_CONTROL_RSF;
        } else {
-               CHIP_DBG(KERN_DEBUG "GMAC: disabling RX store and forward mode"
-                             " (threshold = %d)\n", rxmode);
+               CHIP_DBG(KERN_DEBUG "GMAC: disable RX SF mode (threshold %d)\n",
+                        rxmode);
                csr6 &= ~DMA_CONTROL_RSF;
                csr6 &= DMA_CONTROL_TC_RX_MASK;
                if (rxmode <= 32)
index f83210e7c221f22f774127e98d8695452c31086b..007bb2be3f10f7f8dff644e8bdb1028057698579 100644 (file)
@@ -47,8 +47,7 @@ static void dwmac100_dump_mac_regs(void __iomem *ioaddr)
 {
        pr_info("\t----------------------------------------------\n"
                "\t  DWMAC 100 CSR (base addr = 0x%p)\n"
-               "\t----------------------------------------------\n",
-               ioaddr);
+               "\t----------------------------------------------\n", ioaddr);
        pr_info("\tcontrol reg (offset 0x%x): 0x%08x\n", MAC_CONTROL,
                readl(ioaddr + MAC_CONTROL));
        pr_info("\taddr HI (offset 0x%x): 0x%08x\n ", MAC_ADDR_HIGH,
@@ -72,7 +71,8 @@ static int dwmac100_rx_ipc_enable(void __iomem *ioaddr)
        return 0;
 }
 
-static int dwmac100_irq_status(void __iomem *ioaddr)
+static int dwmac100_irq_status(void __iomem *ioaddr,
+                              struct stmmac_extra_stats *x)
 {
        return 0;
 }
@@ -91,7 +91,7 @@ static void dwmac100_get_umac_addr(void __iomem *ioaddr, unsigned char *addr,
 
 static void dwmac100_set_filter(struct net_device *dev, int id)
 {
-       void __iomem *ioaddr = (void __iomem *) dev->base_addr;
+       void __iomem *ioaddr = (void __iomem *)dev->base_addr;
        u32 value = readl(ioaddr + MAC_CONTROL);
 
        if (dev->flags & IFF_PROMISC) {
@@ -112,7 +112,8 @@ static void dwmac100_set_filter(struct net_device *dev, int id)
                struct netdev_hw_addr *ha;
 
                /* Perfect filter mode for physical address and Hash
-                  filter for multicast */
+                * filter for multicast
+                */
                value |= MAC_CONTROL_HP;
                value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR |
                           MAC_CONTROL_IF | MAC_CONTROL_HO);
@@ -120,12 +121,13 @@ static void dwmac100_set_filter(struct net_device *dev, int id)
                memset(mc_filter, 0, sizeof(mc_filter));
                netdev_for_each_mc_addr(ha, dev) {
                        /* The upper 6 bits of the calculated CRC are used to
-                        * index the contens of the hash table */
-                       int bit_nr =
-                           ether_crc(ETH_ALEN, ha->addr) >> 26;
+                        * index the contens of the hash table
+                        */
+                       int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
                        /* The most significant bit determines the register to
                         * use (H/L) while the other 5 bits determine the bit
-                        * within the register. */
+                        * within the register.
+                        */
                        mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
                }
                writel(mc_filter[0], ioaddr + MAC_HASH_LOW);
@@ -134,10 +136,9 @@ static void dwmac100_set_filter(struct net_device *dev, int id)
 
        writel(value, ioaddr + MAC_CONTROL);
 
-       CHIP_DBG(KERN_INFO "%s: CTRL reg: 0x%08x Hash regs: "
-           "HI 0x%08x, LO 0x%08x\n",
-           __func__, readl(ioaddr + MAC_CONTROL),
-           readl(ioaddr + MAC_HASH_HIGH), readl(ioaddr + MAC_HASH_LOW));
+       CHIP_DBG(KERN_INFO "%s: Filter: 0x%08x Hash: HI 0x%08x, LO 0x%08x\n",
+                __func__, readl(ioaddr + MAC_CONTROL),
+                readl(ioaddr + MAC_HASH_HIGH), readl(ioaddr + MAC_HASH_LOW));
 }
 
 static void dwmac100_flow_ctrl(void __iomem *ioaddr, unsigned int duplex,
@@ -150,9 +151,7 @@ static void dwmac100_flow_ctrl(void __iomem *ioaddr, unsigned int duplex,
        writel(flow, ioaddr + MAC_FLOW_CTRL);
 }
 
-/* No PMT module supported for this Ethernet Controller.
- * Tested on ST platforms only.
- */
+/* No PMT module supported on ST boards with this Eth chip. */
 static void dwmac100_pmt(void __iomem *ioaddr, unsigned long mode)
 {
        return;
index c2b4d55a79b69431835003d7e5f423177cad7637..67551c1541385b28e35617085af8ddf48188ba73 100644 (file)
@@ -32,8 +32,8 @@
 #include "dwmac100.h"
 #include "dwmac_dma.h"
 
-static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb,
-                            int mb, int burst_len, u32 dma_tx, u32 dma_rx)
+static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb,
+                            int burst_len, u32 dma_tx, u32 dma_rx, int atds)
 {
        u32 value = readl(ioaddr + DMA_BUS_MODE);
        int limit;
@@ -52,22 +52,25 @@ static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb,
 
        /* Enable Application Access by writing to DMA CSR0 */
        writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT),
-                       ioaddr + DMA_BUS_MODE);
+              ioaddr + DMA_BUS_MODE);
 
        /* Mask interrupts by writing to CSR7 */
        writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
 
-       /* The base address of the RX/TX descriptor lists must be written into
-        * DMA CSR3 and CSR4, respectively. */
+       /* RX/TX descriptor base addr lists must be written into
+        * DMA CSR3 and CSR4, respectively
+        */
        writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR);
        writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR);
 
        return 0;
 }
 
-/* Store and Forward capability is not used at all..
- * The transmit threshold can be programmed by
- * setting the TTC bits in the DMA control register.*/
+/* Store and Forward capability is not used at all.
+ *
+ * The transmit threshold can be programmed by setting the TTC bits in the DMA
+ * control register.
+ */
 static void dwmac100_dma_operation_mode(void __iomem *ioaddr, int txmode,
                                        int rxmode)
 {
@@ -90,16 +93,15 @@ static void dwmac100_dump_dma_regs(void __iomem *ioaddr)
        CHIP_DBG(KERN_DEBUG "DWMAC 100 DMA CSR\n");
        for (i = 0; i < 9; i++)
                pr_debug("\t CSR%d (offset 0x%x): 0x%08x\n", i,
-                      (DMA_BUS_MODE + i * 4),
-                      readl(ioaddr + DMA_BUS_MODE + i * 4));
+                        (DMA_BUS_MODE + i * 4),
+                        readl(ioaddr + DMA_BUS_MODE + i * 4));
        CHIP_DBG(KERN_DEBUG "\t CSR20 (offset 0x%x): 0x%08x\n",
-           DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR));
+                DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR));
        CHIP_DBG(KERN_DEBUG "\t CSR21 (offset 0x%x): 0x%08x\n",
-           DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR));
+                DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR));
 }
 
-/* DMA controller has two counters to track the number of
- * the receive missed frames. */
+/* DMA controller has two counters to track the number of the missed frames. */
 static void dwmac100_dma_diagnostic_fr(void *data, struct stmmac_extra_stats *x,
                                       void __iomem *ioaddr)
 {
index ab4896ecac1c531814aec2e07fbe199b5e965495..8e5662ce488bd80d2a0a7457c25fbf49ca523bb1 100644 (file)
 #define DMA_STATUS_TU  0x00000004      /* Transmit Buffer Unavailable */
 #define DMA_STATUS_TPS 0x00000002      /* Transmit Process Stopped */
 #define DMA_STATUS_TI  0x00000001      /* Transmit Interrupt */
-#define DMA_CONTROL_FTF                0x00100000 /* Flush transmit FIFO */
+#define DMA_CONTROL_FTF                0x00100000      /* Flush transmit FIFO */
 
 extern void dwmac_enable_dma_transmission(void __iomem *ioaddr);
 extern void dwmac_enable_dma_irq(void __iomem *ioaddr);
@@ -112,6 +112,6 @@ extern void dwmac_dma_stop_tx(void __iomem *ioaddr);
 extern void dwmac_dma_start_rx(void __iomem *ioaddr);
 extern void dwmac_dma_stop_rx(void __iomem *ioaddr);
 extern int dwmac_dma_interrupt(void __iomem *ioaddr,
-                               struct stmmac_extra_stats *x);
+                              struct stmmac_extra_stats *x);
 
 #endif /* __DWMAC_DMA_H__ */
index 2fc8ef95f97af22b1d368dd15f40b6ea2e2a5070..0fbc8fafa7060a1a5b1c68725b4e9a2cd1b23e84 100644 (file)
@@ -150,6 +150,57 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
        return ret;
 }
 
+static void enh_desc_get_ext_status(void *data, struct stmmac_extra_stats *x,
+                                   struct dma_extended_desc *p)
+{
+       if (unlikely(p->basic.des01.erx.rx_mac_addr)) {
+               if (p->des4.erx.ip_hdr_err)
+                       x->ip_hdr_err++;
+               if (p->des4.erx.ip_payload_err)
+                       x->ip_payload_err++;
+               if (p->des4.erx.ip_csum_bypassed)
+                       x->ip_csum_bypassed++;
+               if (p->des4.erx.ipv4_pkt_rcvd)
+                       x->ipv4_pkt_rcvd++;
+               if (p->des4.erx.ipv6_pkt_rcvd)
+                       x->ipv6_pkt_rcvd++;
+               if (p->des4.erx.msg_type == RDES_EXT_SYNC)
+                       x->rx_msg_type_sync++;
+               else if (p->des4.erx.msg_type == RDES_EXT_FOLLOW_UP)
+                       x->rx_msg_type_follow_up++;
+               else if (p->des4.erx.msg_type == RDES_EXT_DELAY_REQ)
+                       x->rx_msg_type_delay_req++;
+               else if (p->des4.erx.msg_type == RDES_EXT_DELAY_RESP)
+                       x->rx_msg_type_delay_resp++;
+               else if (p->des4.erx.msg_type == RDES_EXT_DELAY_REQ)
+                       x->rx_msg_type_pdelay_req++;
+               else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_RESP)
+                       x->rx_msg_type_pdelay_resp++;
+               else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_FOLLOW_UP)
+                       x->rx_msg_type_pdelay_follow_up++;
+               else
+                       x->rx_msg_type_ext_no_ptp++;
+               if (p->des4.erx.ptp_frame_type)
+                       x->ptp_frame_type++;
+               if (p->des4.erx.ptp_ver)
+                       x->ptp_ver++;
+               if (p->des4.erx.timestamp_dropped)
+                       x->timestamp_dropped++;
+               if (p->des4.erx.av_pkt_rcvd)
+                       x->av_pkt_rcvd++;
+               if (p->des4.erx.av_tagged_pkt_rcvd)
+                       x->av_tagged_pkt_rcvd++;
+               if (p->des4.erx.vlan_tag_priority_val)
+                       x->vlan_tag_priority_val++;
+               if (p->des4.erx.l3_filter_match)
+                       x->l3_filter_match++;
+               if (p->des4.erx.l4_filter_match)
+                       x->l4_filter_match++;
+               if (p->des4.erx.l3_l4_filter_no_match)
+                       x->l3_l4_filter_no_match++;
+       }
+}
+
 static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
                                  struct dma_desc *p)
 {
@@ -198,7 +249,7 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
         * At any rate, we need to understand if the CSUM hw computation is ok
         * and report this info to the upper layers. */
        ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error,
-               p->des01.erx.frame_type, p->des01.erx.payload_csum_error);
+               p->des01.erx.frame_type, p->des01.erx.rx_mac_addr);
 
        if (unlikely(p->des01.erx.dribbling)) {
                CHIP_DBG(KERN_ERR "GMAC RX: dribbling error\n");
@@ -225,34 +276,32 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
                x->rx_vlan++;
        }
 #endif
+
        return ret;
 }
 
-static void enh_desc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
-                                 int disable_rx_ic)
+static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
+                                 int mode, int end)
 {
-       int i;
-       for (i = 0; i < ring_size; i++) {
-               p->des01.erx.own = 1;
-               p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
+       p->des01.erx.own = 1;
+       p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
 
-               ehn_desc_rx_set_on_ring_chain(p, (i == ring_size - 1));
+       if (mode == STMMAC_CHAIN_MODE)
+               ehn_desc_rx_set_on_chain(p, end);
+       else
+               ehn_desc_rx_set_on_ring(p, end);
 
-               if (disable_rx_ic)
-                       p->des01.erx.disable_ic = 1;
-               p++;
-       }
+       if (disable_rx_ic)
+               p->des01.erx.disable_ic = 1;
 }
 
-static void enh_desc_init_tx_desc(struct dma_desc *p, unsigned int ring_size)
+static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end)
 {
-       int i;
-
-       for (i = 0; i < ring_size; i++) {
-               p->des01.etx.own = 0;
-               ehn_desc_tx_set_on_ring_chain(p, (i == ring_size - 1));
-               p++;
-       }
+       p->des01.etx.own = 0;
+       if (mode == STMMAC_CHAIN_MODE)
+               ehn_desc_tx_set_on_chain(p, end);
+       else
+               ehn_desc_tx_set_on_ring(p, end);
 }
 
 static int enh_desc_get_tx_owner(struct dma_desc *p)
@@ -280,20 +329,26 @@ static int enh_desc_get_tx_ls(struct dma_desc *p)
        return p->des01.etx.last_segment;
 }
 
-static void enh_desc_release_tx_desc(struct dma_desc *p)
+static void enh_desc_release_tx_desc(struct dma_desc *p, int mode)
 {
        int ter = p->des01.etx.end_ring;
 
        memset(p, 0, offsetof(struct dma_desc, des2));
-       enh_desc_end_tx_desc(p, ter);
+       if (mode == STMMAC_CHAIN_MODE)
+               enh_desc_end_tx_desc_on_chain(p, ter);
+       else
+               enh_desc_end_tx_desc_on_ring(p, ter);
 }
 
 static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
-                                    int csum_flag)
+                                    int csum_flag, int mode)
 {
        p->des01.etx.first_segment = is_fs;
 
-       enh_set_tx_desc_len(p, len);
+       if (mode == STMMAC_CHAIN_MODE)
+               enh_set_tx_desc_len_on_chain(p, len);
+       else
+               enh_set_tx_desc_len_on_ring(p, len);
 
        if (likely(csum_flag))
                p->des01.etx.checksum_insertion = cic_full;
@@ -323,6 +378,49 @@ static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
                return p->des01.erx.frame_length;
 }
 
+static void enh_desc_enable_tx_timestamp(struct dma_desc *p)
+{
+       p->des01.etx.time_stamp_enable = 1;
+}
+
+static int enh_desc_get_tx_timestamp_status(struct dma_desc *p)
+{
+       return p->des01.etx.time_stamp_status;
+}
+
+static u64 enh_desc_get_timestamp(void *desc, u32 ats)
+{
+       u64 ns;
+
+       if (ats) {
+               struct dma_extended_desc *p = (struct dma_extended_desc *)desc;
+               ns = p->des6;
+               /* convert high/sec time stamp value to nanosecond */
+               ns += p->des7 * 1000000000ULL;
+       } else {
+               struct dma_desc *p = (struct dma_desc *)desc;
+               ns = p->des2;
+               ns += p->des3 * 1000000000ULL;
+       }
+
+       return ns;
+}
+
+static int enh_desc_get_rx_timestamp_status(void *desc, u32 ats)
+{
+       if (ats) {
+               struct dma_extended_desc *p = (struct dma_extended_desc *)desc;
+               return p->basic.des01.erx.ipc_csum_error;
+       } else {
+               struct dma_desc *p = (struct dma_desc *)desc;
+               if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
+                       /* timestamp is corrupted, hence don't store it */
+                       return 0;
+               else
+                       return 1;
+       }
+}
+
 const struct stmmac_desc_ops enh_desc_ops = {
        .tx_status = enh_desc_get_tx_status,
        .rx_status = enh_desc_get_rx_status,
@@ -339,4 +437,9 @@ const struct stmmac_desc_ops enh_desc_ops = {
        .set_tx_owner = enh_desc_set_tx_owner,
        .set_rx_owner = enh_desc_set_rx_owner,
        .get_rx_frame_len = enh_desc_get_rx_frame_len,
+       .rx_extended_status = enh_desc_get_ext_status,
+       .enable_tx_timestamp = enh_desc_enable_tx_timestamp,
+       .get_tx_timestamp_status = enh_desc_get_tx_timestamp_status,
+       .get_timestamp = enh_desc_get_timestamp,
+       .get_rx_timestamp_status = enh_desc_get_rx_timestamp_status,
 };
index 67995ef252515f53c6796ecf77bbe66460223944..48ec001566b5540421c5e5a25fe0b18db3c25e58 100644 (file)
@@ -28,8 +28,7 @@
 /* MMC control register */
 /* When set, all counter are reset */
 #define MMC_CNTRL_COUNTER_RESET                0x1
-/* When set, do not roll over zero
- * after reaching the max value*/
+/* When set, do not roll over zero after reaching the max value*/
 #define MMC_CNTRL_COUNTER_STOP_ROLLOVER        0x2
 #define MMC_CNTRL_RESET_ON_READ                0x4     /* Reset after reading */
 #define MMC_CNTRL_COUNTER_FREEZER      0x8     /* Freeze counter values to the
index 68962c549a2d6296815853abaa10cd92e56cfb39..11775b99afc5c22b4f9181328bd1381c40222713 100644 (file)
@@ -79,8 +79,8 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
        struct net_device_stats *stats = (struct net_device_stats *)data;
 
        if (unlikely(p->des01.rx.last_descriptor == 0)) {
-               pr_warning("ndesc Error: Oversized Ethernet "
-                          "frame spanned multiple buffers\n");
+               pr_warn("%s: Oversized frame spanned multiple buffers\n",
+                       __func__);
                stats->rx_length_errors++;
                return discard_frame;
        }
@@ -122,30 +122,28 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
        return ret;
 }
 
-static void ndesc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
-                              int disable_rx_ic)
+static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode,
+                              int end)
 {
-       int i;
-       for (i = 0; i < ring_size; i++) {
-               p->des01.rx.own = 1;
-               p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1;
+       p->des01.rx.own = 1;
+       p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1;
 
-               ndesc_rx_set_on_ring_chain(p, (i == ring_size - 1));
+       if (mode == STMMAC_CHAIN_MODE)
+               ndesc_rx_set_on_chain(p, end);
+       else
+               ndesc_rx_set_on_ring(p, end);
 
-               if (disable_rx_ic)
-                       p->des01.rx.disable_ic = 1;
-               p++;
-       }
+       if (disable_rx_ic)
+               p->des01.rx.disable_ic = 1;
 }
 
-static void ndesc_init_tx_desc(struct dma_desc *p, unsigned int ring_size)
+static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end)
 {
-       int i;
-       for (i = 0; i < ring_size; i++) {
-               p->des01.tx.own = 0;
-               ndesc_tx_set_on_ring_chain(p, (i == (ring_size - 1)));
-               p++;
-       }
+       p->des01.tx.own = 0;
+       if (mode == STMMAC_CHAIN_MODE)
+               ndesc_tx_set_on_chain(p, end);
+       else
+               ndesc_tx_set_on_ring(p, end);
 }
 
 static int ndesc_get_tx_owner(struct dma_desc *p)
@@ -173,19 +171,25 @@ static int ndesc_get_tx_ls(struct dma_desc *p)
        return p->des01.tx.last_segment;
 }
 
-static void ndesc_release_tx_desc(struct dma_desc *p)
+static void ndesc_release_tx_desc(struct dma_desc *p, int mode)
 {
        int ter = p->des01.tx.end_ring;
 
        memset(p, 0, offsetof(struct dma_desc, des2));
-       ndesc_end_tx_desc(p, ter);
+       if (mode == STMMAC_CHAIN_MODE)
+               ndesc_end_tx_desc_on_chain(p, ter);
+       else
+               ndesc_end_tx_desc_on_ring(p, ter);
 }
 
 static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
-                                 int csum_flag)
+                                 int csum_flag, int mode)
 {
        p->des01.tx.first_segment = is_fs;
-       norm_set_tx_desc_len(p, len);
+       if (mode == STMMAC_CHAIN_MODE)
+               norm_set_tx_desc_len_on_chain(p, len);
+       else
+               norm_set_tx_desc_len_on_ring(p, len);
 
        if (likely(csum_flag))
                p->des01.tx.checksum_insertion = cic_full;
@@ -215,6 +219,39 @@ static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
                return p->des01.rx.frame_length;
 }
 
+static void ndesc_enable_tx_timestamp(struct dma_desc *p)
+{
+       p->des01.tx.time_stamp_enable = 1;
+}
+
+static int ndesc_get_tx_timestamp_status(struct dma_desc *p)
+{
+       return p->des01.tx.time_stamp_status;
+}
+
+static u64 ndesc_get_timestamp(void *desc, u32 ats)
+{
+       struct dma_desc *p = (struct dma_desc *)desc;
+       u64 ns;
+
+       ns = p->des2;
+       /* convert high/sec time stamp value to nanosecond */
+       ns += p->des3 * 1000000000ULL;
+
+       return ns;
+}
+
+static int ndesc_get_rx_timestamp_status(void *desc, u32 ats)
+{
+       struct dma_desc *p = (struct dma_desc *)desc;
+
+       if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
+               /* timestamp is corrupted, hence don't store it */
+               return 0;
+       else
+               return 1;
+}
+
 const struct stmmac_desc_ops ndesc_ops = {
        .tx_status = ndesc_get_tx_status,
        .rx_status = ndesc_get_rx_status,
@@ -231,4 +268,8 @@ const struct stmmac_desc_ops ndesc_ops = {
        .set_tx_owner = ndesc_set_tx_owner,
        .set_rx_owner = ndesc_set_rx_owner,
        .get_rx_frame_len = ndesc_get_rx_frame_len,
+       .enable_tx_timestamp = ndesc_enable_tx_timestamp,
+       .get_tx_timestamp_status = ndesc_get_tx_timestamp_status,
+       .get_timestamp = ndesc_get_timestamp,
+       .get_rx_timestamp_status = ndesc_get_rx_timestamp_status,
 };
index 4b785e10f2ed7d0994d17df944f01907480a767e..c9d942a5c335d022a3eb8bd59ebe5345d655d557 100644 (file)
@@ -30,7 +30,7 @@
 
 static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
 {
-       struct stmmac_priv *priv = (struct stmmac_priv *) p;
+       struct stmmac_priv *priv = (struct stmmac_priv *)p;
        unsigned int txsize = priv->dma_tx_size;
        unsigned int entry = priv->cur_tx % txsize;
        struct dma_desc *desc = priv->dma_tx + entry;
@@ -48,25 +48,30 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
 
                desc->des2 = dma_map_single(priv->device, skb->data,
                                            bmax, DMA_TO_DEVICE);
+               priv->tx_skbuff_dma[entry] = desc->des2;
                desc->des3 = desc->des2 + BUF_SIZE_4KiB;
-               priv->hw->desc->prepare_tx_desc(desc, 1, bmax,
-                                               csum);
+               priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum,
+                                               STMMAC_RING_MODE);
                wmb();
                entry = (++priv->cur_tx) % txsize;
                desc = priv->dma_tx + entry;
 
                desc->des2 = dma_map_single(priv->device, skb->data + bmax,
                                            len, DMA_TO_DEVICE);
+               priv->tx_skbuff_dma[entry] = desc->des2;
                desc->des3 = desc->des2 + BUF_SIZE_4KiB;
-               priv->hw->desc->prepare_tx_desc(desc, 0, len, csum);
+               priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
+                                               STMMAC_RING_MODE);
                wmb();
                priv->hw->desc->set_tx_owner(desc);
                priv->tx_skbuff[entry] = NULL;
        } else {
                desc->des2 = dma_map_single(priv->device, skb->data,
                                            nopaged_len, DMA_TO_DEVICE);
+               priv->tx_skbuff_dma[entry] = desc->des2;
                desc->des3 = desc->des2 + BUF_SIZE_4KiB;
-               priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum);
+               priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum,
+                                               STMMAC_RING_MODE);
        }
 
        return entry;
@@ -82,27 +87,23 @@ static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
        return ret;
 }
 
-static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
+static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
 {
-       /* Fill DES3 in case of RING mode */
-       if (bfsize >= BUF_SIZE_8KiB)
-               p->des3 = p->des2 + BUF_SIZE_8KiB;
-}
+       struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
 
-/* In ring mode we need to fill the desc3 because it is used
- * as buffer */
-static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p)
-{
-       if (unlikely(des3_as_data_buf))
-               p->des3 = p->des2 + BUF_SIZE_8KiB;
+       if (unlikely(priv->plat->has_gmac))
+               /* Fill DES3 in case of RING mode */
+               if (priv->dma_buf_sz >= BUF_SIZE_8KiB)
+                       p->des3 = p->des2 + BUF_SIZE_8KiB;
 }
 
-static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
-                                 unsigned int size)
+/* In ring mode we need to fill the desc3 because it is used as buffer */
+static void stmmac_init_desc3(struct dma_desc *p)
 {
+       p->des3 = p->des2 + BUF_SIZE_8KiB;
 }
 
-static void stmmac_clean_desc3(struct dma_desc *p)
+static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)
 {
        if (unlikely(p->des3))
                p->des3 = 0;
@@ -121,7 +122,6 @@ const struct stmmac_ring_mode_ops ring_mode_ops = {
        .jumbo_frm = stmmac_jumbo_frm,
        .refill_desc3 = stmmac_refill_desc3,
        .init_desc3 = stmmac_init_desc3,
-       .init_dma_chain = stmmac_init_dma_chain,
        .clean_desc3 = stmmac_clean_desc3,
        .set_16kib_bfsize = stmmac_set_16kib_bfsize,
 };
index b05df8983be54e76a7e691057830630abcb185d9..c922fde929a14a29cedc2fe03f3031e84fd13024 100644 (file)
 #define __STMMAC_H__
 
 #define STMMAC_RESOURCE_NAME   "stmmaceth"
-#define DRV_MODULE_VERSION     "Nov_2012"
+#define DRV_MODULE_VERSION     "March_2013"
 
 #include <linux/clk.h>
 #include <linux/stmmac.h>
 #include <linux/phy.h>
 #include <linux/pci.h>
 #include "common.h"
+#include <linux/ptp_clock_kernel.h>
 
 struct stmmac_priv {
        /* Frequently used values are kept adjacent for cache effect */
-       struct dma_desc *dma_tx ____cacheline_aligned;
-       dma_addr_t dma_tx_phy;
+       struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp;
+       struct dma_desc *dma_tx;
        struct sk_buff **tx_skbuff;
        unsigned int cur_tx;
        unsigned int dirty_tx;
        unsigned int dma_tx_size;
+       u32 tx_count_frames;
+       u32 tx_coal_frames;
+       u32 tx_coal_timer;
+       dma_addr_t *tx_skbuff_dma;
+       dma_addr_t dma_tx_phy;
        int tx_coalesce;
+       int hwts_tx_en;
+       spinlock_t tx_lock;
+       bool tx_path_in_lpi_mode;
+       struct timer_list txtimer;
 
-       struct dma_desc *dma_rx ;
+       struct dma_desc *dma_rx ____cacheline_aligned_in_smp;
+       struct dma_extended_desc *dma_erx;
+       struct sk_buff **rx_skbuff;
        unsigned int cur_rx;
        unsigned int dirty_rx;
-       struct sk_buff **rx_skbuff;
+       unsigned int dma_rx_size;
+       unsigned int dma_buf_sz;
+       u32 rx_riwt;
+       int hwts_rx_en;
        dma_addr_t *rx_skbuff_dma;
+       dma_addr_t dma_rx_phy;
 
+       struct napi_struct napi ____cacheline_aligned_in_smp;
+
+       void __iomem *ioaddr;
        struct net_device *dev;
-       dma_addr_t dma_rx_phy;
-       unsigned int dma_rx_size;
-       unsigned int dma_buf_sz;
        struct device *device;
        struct mac_device_info *hw;
-       void __iomem *ioaddr;
-
-       struct stmmac_extra_stats xstats;
-       struct napi_struct napi;
        int no_csum_insertion;
+       spinlock_t lock;
 
-       struct phy_device *phydev;
+       struct phy_device *phydev ____cacheline_aligned_in_smp;
        int oldlink;
        int speed;
        int oldduplex;
@@ -69,30 +82,31 @@ struct stmmac_priv {
        struct mii_bus *mii;
        int mii_irq[PHY_MAX_ADDR];
 
-       u32 msg_enable;
-       spinlock_t lock;
-       spinlock_t tx_lock;
-       int wolopts;
-       int wol_irq;
+       struct stmmac_extra_stats xstats ____cacheline_aligned_in_smp;
        struct plat_stmmacenet_data *plat;
-       struct stmmac_counters mmc;
        struct dma_features dma_cap;
+       struct stmmac_counters mmc;
        int hw_cap_support;
+       int synopsys_id;
+       u32 msg_enable;
+       int wolopts;
+       int wol_irq;
        struct clk *stmmac_clk;
        int clk_csr;
-       int synopsys_id;
        struct timer_list eee_ctrl_timer;
-       bool tx_path_in_lpi_mode;
        int lpi_irq;
        int eee_enabled;
        int eee_active;
        int tx_lpi_timer;
-       struct timer_list txtimer;
-       u32 tx_count_frames;
-       u32 tx_coal_frames;
-       u32 tx_coal_timer;
+       int pcs;
+       unsigned int mode;
+       int extend_desc;
+       struct ptp_clock *ptp_clock;
+       struct ptp_clock_info ptp_clock_ops;
+       unsigned int default_addend;
+       u32 adv_ts;
        int use_riwt;
-       u32 rx_riwt;
+       spinlock_t ptp_lock;
 };
 
 extern int phyaddr;
@@ -102,6 +116,9 @@ extern int stmmac_mdio_register(struct net_device *ndev);
 extern void stmmac_set_ethtool_ops(struct net_device *netdev);
 extern const struct stmmac_desc_ops enh_desc_ops;
 extern const struct stmmac_desc_ops ndesc_ops;
+extern const struct stmmac_hwtimestamp stmmac_ptp;
+extern int stmmac_ptp_register(struct stmmac_priv *priv);
+extern void stmmac_ptp_unregister(struct stmmac_priv *priv);
 int stmmac_freeze(struct net_device *ndev);
 int stmmac_restore(struct net_device *ndev);
 int stmmac_resume(struct net_device *ndev);
@@ -125,6 +142,7 @@ static inline int stmmac_register_platform(void)
 
        return err;
 }
+
 static inline void stmmac_unregister_platform(void)
 {
        platform_driver_unregister(&stmmac_pltfr_driver);
@@ -136,6 +154,7 @@ static inline int stmmac_register_platform(void)
 
        return 0;
 }
+
 static inline void stmmac_unregister_platform(void)
 {
 }
@@ -153,6 +172,7 @@ static inline int stmmac_register_pci(void)
 
        return err;
 }
+
 static inline void stmmac_unregister_pci(void)
 {
        pci_unregister_driver(&stmmac_pci_driver);
@@ -164,6 +184,7 @@ static inline int stmmac_register_pci(void)
 
        return 0;
 }
+
 static inline void stmmac_unregister_pci(void)
 {
 }
index d1ac39c1b05d2894ceaee0ed41aeb49f03b88a04..c5f9cb85c8ef9c31c2ce05894faffa2a484a8093 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/interrupt.h>
 #include <linux/mii.h>
 #include <linux/phy.h>
+#include <linux/net_tstamp.h>
 #include <asm/io.h>
 
 #include "stmmac.h"
@@ -108,6 +109,33 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
        STMMAC_STAT(irq_rx_path_in_lpi_mode_n),
        STMMAC_STAT(irq_rx_path_exit_lpi_mode_n),
        STMMAC_STAT(phy_eee_wakeup_error_n),
+       /* Extended RDES status */
+       STMMAC_STAT(ip_hdr_err),
+       STMMAC_STAT(ip_payload_err),
+       STMMAC_STAT(ip_csum_bypassed),
+       STMMAC_STAT(ipv4_pkt_rcvd),
+       STMMAC_STAT(ipv6_pkt_rcvd),
+       STMMAC_STAT(rx_msg_type_ext_no_ptp),
+       STMMAC_STAT(rx_msg_type_sync),
+       STMMAC_STAT(rx_msg_type_follow_up),
+       STMMAC_STAT(rx_msg_type_delay_req),
+       STMMAC_STAT(rx_msg_type_delay_resp),
+       STMMAC_STAT(rx_msg_type_pdelay_req),
+       STMMAC_STAT(rx_msg_type_pdelay_resp),
+       STMMAC_STAT(rx_msg_type_pdelay_follow_up),
+       STMMAC_STAT(ptp_frame_type),
+       STMMAC_STAT(ptp_ver),
+       STMMAC_STAT(timestamp_dropped),
+       STMMAC_STAT(av_pkt_rcvd),
+       STMMAC_STAT(av_tagged_pkt_rcvd),
+       STMMAC_STAT(vlan_tag_priority_val),
+       STMMAC_STAT(l3_filter_match),
+       STMMAC_STAT(l4_filter_match),
+       STMMAC_STAT(l3_l4_filter_no_match),
+       /* PCS */
+       STMMAC_STAT(irq_pcs_ane_n),
+       STMMAC_STAT(irq_pcs_link_n),
+       STMMAC_STAT(irq_rgmii_n),
 };
 #define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
 
@@ -219,6 +247,70 @@ static int stmmac_ethtool_getsettings(struct net_device *dev,
        struct stmmac_priv *priv = netdev_priv(dev);
        struct phy_device *phy = priv->phydev;
        int rc;
+
+       if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) {
+               struct rgmii_adv adv;
+
+               if (!priv->xstats.pcs_link) {
+                       ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+                       cmd->duplex = DUPLEX_UNKNOWN;
+                       return 0;
+               }
+               cmd->duplex = priv->xstats.pcs_duplex;
+
+               ethtool_cmd_speed_set(cmd, priv->xstats.pcs_speed);
+
+               /* Get and convert ADV/LP_ADV from the HW AN registers */
+               if (priv->hw->mac->get_adv)
+                       priv->hw->mac->get_adv(priv->ioaddr, &adv);
+               else
+                       return -EOPNOTSUPP;     /* should never happen indeed */
+
+               /* Encoding of PSE bits is defined in 802.3z, 37.2.1.4 */
+
+               if (adv.pause & STMMAC_PCS_PAUSE)
+                       cmd->advertising |= ADVERTISED_Pause;
+               if (adv.pause & STMMAC_PCS_ASYM_PAUSE)
+                       cmd->advertising |= ADVERTISED_Asym_Pause;
+               if (adv.lp_pause & STMMAC_PCS_PAUSE)
+                       cmd->lp_advertising |= ADVERTISED_Pause;
+               if (adv.lp_pause & STMMAC_PCS_ASYM_PAUSE)
+                       cmd->lp_advertising |= ADVERTISED_Asym_Pause;
+
+               /* Reg49[3] always set because ANE is always supported */
+               cmd->autoneg = ADVERTISED_Autoneg;
+               cmd->supported |= SUPPORTED_Autoneg;
+               cmd->advertising |= ADVERTISED_Autoneg;
+               cmd->lp_advertising |= ADVERTISED_Autoneg;
+
+               if (adv.duplex) {
+                       cmd->supported |= (SUPPORTED_1000baseT_Full |
+                                          SUPPORTED_100baseT_Full |
+                                          SUPPORTED_10baseT_Full);
+                       cmd->advertising |= (ADVERTISED_1000baseT_Full |
+                                            ADVERTISED_100baseT_Full |
+                                            ADVERTISED_10baseT_Full);
+               } else {
+                       cmd->supported |= (SUPPORTED_1000baseT_Half |
+                                          SUPPORTED_100baseT_Half |
+                                          SUPPORTED_10baseT_Half);
+                       cmd->advertising |= (ADVERTISED_1000baseT_Half |
+                                            ADVERTISED_100baseT_Half |
+                                            ADVERTISED_10baseT_Half);
+               }
+               if (adv.lp_duplex)
+                       cmd->lp_advertising |= (ADVERTISED_1000baseT_Full |
+                                               ADVERTISED_100baseT_Full |
+                                               ADVERTISED_10baseT_Full);
+               else
+                       cmd->lp_advertising |= (ADVERTISED_1000baseT_Half |
+                                               ADVERTISED_100baseT_Half |
+                                               ADVERTISED_10baseT_Half);
+               cmd->port = PORT_OTHER;
+
+               return 0;
+       }
+
        if (phy == NULL) {
                pr_err("%s: %s: PHY is not registered\n",
                       __func__, dev->name);
@@ -243,6 +335,30 @@ static int stmmac_ethtool_setsettings(struct net_device *dev,
        struct phy_device *phy = priv->phydev;
        int rc;
 
+       if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) {
+               u32 mask = ADVERTISED_Autoneg | ADVERTISED_Pause;
+
+               /* Only support ANE */
+               if (cmd->autoneg != AUTONEG_ENABLE)
+                       return -EINVAL;
+
+               if (cmd->autoneg == AUTONEG_ENABLE) {
+                       mask &= (ADVERTISED_1000baseT_Half |
+                       ADVERTISED_1000baseT_Full |
+                       ADVERTISED_100baseT_Half |
+                       ADVERTISED_100baseT_Full |
+                       ADVERTISED_10baseT_Half |
+                       ADVERTISED_10baseT_Full);
+
+                       spin_lock(&priv->lock);
+                       if (priv->hw->mac->ctrl_ane)
+                               priv->hw->mac->ctrl_ane(priv->ioaddr, 1);
+                       spin_unlock(&priv->lock);
+               }
+
+               return 0;
+       }
+
        spin_lock(&priv->lock);
        rc = phy_ethtool_sset(phy, cmd);
        spin_unlock(&priv->lock);
@@ -312,6 +428,9 @@ stmmac_get_pauseparam(struct net_device *netdev,
 {
        struct stmmac_priv *priv = netdev_priv(netdev);
 
+       if (priv->pcs)  /* FIXME */
+               return;
+
        spin_lock(&priv->lock);
 
        pause->rx_pause = 0;
@@ -335,6 +454,9 @@ stmmac_set_pauseparam(struct net_device *netdev,
        int new_pause = FLOW_OFF;
        int ret = 0;
 
+       if (priv->pcs)  /* FIXME */
+               return -EOPNOTSUPP;
+
        spin_lock(&priv->lock);
 
        if (pause->rx_pause)
@@ -604,6 +726,38 @@ static int stmmac_set_coalesce(struct net_device *dev,
        return 0;
 }
 
+static int stmmac_get_ts_info(struct net_device *dev,
+                             struct ethtool_ts_info *info)
+{
+       struct stmmac_priv *priv = netdev_priv(dev);
+
+       if ((priv->hwts_tx_en) && (priv->hwts_rx_en)) {
+
+               info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+                                       SOF_TIMESTAMPING_RX_HARDWARE |
+                                       SOF_TIMESTAMPING_RAW_HARDWARE;
+
+               if (priv->ptp_clock)
+                       info->phc_index = ptp_clock_index(priv->ptp_clock);
+
+               info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
+
+               info->rx_filters = ((1 << HWTSTAMP_FILTER_NONE) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) |
+                                   (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
+                                   (1 << HWTSTAMP_FILTER_ALL));
+               return 0;
+       } else
+               return ethtool_op_get_ts_info(dev, info);
+}
+
 static const struct ethtool_ops stmmac_ethtool_ops = {
        .begin = stmmac_check_if_running,
        .get_drvinfo = stmmac_ethtool_getdrvinfo,
@@ -623,7 +777,7 @@ static const struct ethtool_ops stmmac_ethtool_ops = {
        .get_eee = stmmac_ethtool_op_get_eee,
        .set_eee = stmmac_ethtool_op_set_eee,
        .get_sset_count = stmmac_get_sset_count,
-       .get_ts_info = ethtool_op_get_ts_info,
+       .get_ts_info = stmmac_get_ts_info,
        .get_coalesce = stmmac_get_coalesce,
        .set_coalesce = stmmac_set_coalesce,
 };
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
new file mode 100644 (file)
index 0000000..def7e75
--- /dev/null
@@ -0,0 +1,148 @@
+/*******************************************************************************
+  Copyright (C) 2013  Vayavya Labs Pvt Ltd
+
+  This implements all the API for managing HW timestamp & PTP.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
+  Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+*******************************************************************************/
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include "common.h"
+#include "stmmac_ptp.h"
+
+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)
+{
+       u32 value = readl(ioaddr + PTP_TCR);
+       unsigned long data;
+
+       /* Convert the ptp_clock to nano second
+        * formula = (1/ptp_clock) * 1000000000
+        * where, ptp_clock = 50MHz.
+        */
+       data = (1000000000ULL / 50000000);
+
+       /* 0.465ns accuracy */
+       if (value & PTP_TCR_TSCTRLSSR)
+               data = (data * 100) / 465;
+
+       writel(data, ioaddr + PTP_SSIR);
+}
+
+static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
+{
+       int limit;
+       u32 value;
+
+       writel(sec, ioaddr + PTP_STSUR);
+       writel(nsec, ioaddr + PTP_STNSUR);
+       /* issue command to initialize the system time value */
+       value = readl(ioaddr + PTP_TCR);
+       value |= PTP_TCR_TSINIT;
+       writel(value, ioaddr + PTP_TCR);
+
+       /* wait for present system time initialize to complete */
+       limit = 10;
+       while (limit--) {
+               if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
+                       break;
+               mdelay(10);
+       }
+       if (limit < 0)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int stmmac_config_addend(void __iomem *ioaddr, u32 addend)
+{
+       u32 value;
+       int limit;
+
+       writel(addend, ioaddr + PTP_TAR);
+       /* issue command to update the addend value */
+       value = readl(ioaddr + PTP_TCR);
+       value |= PTP_TCR_TSADDREG;
+       writel(value, ioaddr + PTP_TCR);
+
+       /* wait for present addend update to complete */
+       limit = 10;
+       while (limit--) {
+               if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
+                       break;
+               mdelay(10);
+       }
+       if (limit < 0)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int stmmac_adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec,
+                                int add_sub)
+{
+       u32 value;
+       int limit;
+
+       writel(sec, ioaddr + PTP_STSUR);
+       writel(((add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec),
+               ioaddr + PTP_STNSUR);
+       /* issue command to initialize the system time value */
+       value = readl(ioaddr + PTP_TCR);
+       value |= PTP_TCR_TSUPDT;
+       writel(value, ioaddr + PTP_TCR);
+
+       /* wait for present system time adjust/update to complete */
+       limit = 10;
+       while (limit--) {
+               if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSUPDT))
+                       break;
+               mdelay(10);
+       }
+       if (limit < 0)
+               return -EBUSY;
+
+       return 0;
+}
+
+static u64 stmmac_get_systime(void __iomem *ioaddr)
+{
+       u64 ns;
+
+       ns = readl(ioaddr + PTP_STNSR);
+       /* convert sec time value to nanosecond */
+       ns += readl(ioaddr + PTP_STSR) * 1000000000ULL;
+
+       return ns;
+}
+
+const struct stmmac_hwtimestamp stmmac_ptp = {
+       .config_hw_tstamping = stmmac_config_hw_tstamping,
+       .init_systime = stmmac_init_systime,
+       .config_sub_second_increment = stmmac_config_sub_second_increment,
+       .config_addend = stmmac_config_addend,
+       .adjust_systime = stmmac_adjust_systime,
+       .get_systime = stmmac_get_systime,
+};
index 39c6c55246331bd4ef9b2d380f213112f8a82072..618446ae1ec1b3b2ae924b2a5d083215c6b0be15 100644 (file)
@@ -46,7 +46,9 @@
 #ifdef CONFIG_STMMAC_DEBUG_FS
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
-#endif
+#endif /* CONFIG_STMMAC_DEBUG_FS */
+#include <linux/net_tstamp.h>
+#include "stmmac_ptp.h"
 #include "stmmac.h"
 
 #undef STMMAC_DEBUG
 #define JUMBO_LEN      9000
 
 /* Module parameters */
-#define TX_TIMEO 5000 /* default 5 seconds */
+#define TX_TIMEO       5000
 static int watchdog = TX_TIMEO;
 module_param(watchdog, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds");
+MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds (default 5s)");
 
-static int debug = -1;         /* -1: default, 0: no output, 16:  all */
+static int debug = -1;
 module_param(debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Message Level (0: no output, 16: all)");
+MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");
 
 int phyaddr = -1;
 module_param(phyaddr, int, S_IRUGO);
@@ -130,6 +132,13 @@ module_param(eee_timer, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
 #define STMMAC_LPI_TIMER(x) (jiffies + msecs_to_jiffies(x))
 
+/* By default the driver will use the ring mode to manage tx and rx descriptors
+ * but passing this value so user can force to use the chain instead of the ring
+ */
+static unsigned int chain_mode;
+module_param(chain_mode, int, S_IRUGO);
+MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");
+
 static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
 
 #ifdef CONFIG_STMMAC_DEBUG_FS
@@ -164,6 +173,18 @@ static void stmmac_verify_args(void)
                eee_timer = STMMAC_DEFAULT_LPI_TIMER;
 }
 
+/**
+ * stmmac_clk_csr_set - dynamically set the MDC clock
+ * @priv: driver private structure
+ * Description: this is to dynamically set the MDC clock according to the csr
+ * clock input.
+ * Note:
+ *     If a specific clk_csr value is passed from the platform
+ *     this means that the CSR Clock Range selection cannot be
+ *     changed at run-time and it is fixed (as reported in the driver
+ *     documentation). Viceversa the driver will try to set the MDC
+ *     clock dynamically according to the actual clock input.
+ */
 static void stmmac_clk_csr_set(struct stmmac_priv *priv)
 {
        u32 clk_rate;
@@ -171,7 +192,12 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv)
        clk_rate = clk_get_rate(priv->stmmac_clk);
 
        /* Platform provided default clk_csr would be assumed valid
-        * for all other cases except for the below mentioned ones. */
+        * for all other cases except for the below mentioned ones.
+        * For values higher than the IEEE 802.3 specified frequency
+        * we can not estimate the proper divider as it is not known
+        * the frequency of clk_csr_i. So we do not change the default
+        * divider.
+        */
        if (!(priv->clk_csr & MAC_CSR_H_FRQ_MASK)) {
                if (clk_rate < CSR_F_35M)
                        priv->clk_csr = STMMAC_CSR_20_35M;
@@ -185,10 +211,7 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv)
                        priv->clk_csr = STMMAC_CSR_150_250M;
                else if ((clk_rate >= CSR_F_250M) && (clk_rate < CSR_F_300M))
                        priv->clk_csr = STMMAC_CSR_250_300M;
-       } /* For values higher than the IEEE 802.3 specified frequency
-          * we can not estimate the proper divider as it is not known
-          * the frequency of clk_csr_i. So we do not change the default
-          * divider. */
+       }
 }
 
 #if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG)
@@ -213,18 +236,25 @@ static inline u32 stmmac_tx_avail(struct stmmac_priv *priv)
        return priv->dirty_tx + priv->dma_tx_size - priv->cur_tx - 1;
 }
 
-/* On some ST platforms, some HW system configuraton registers have to be
- * set according to the link speed negotiated.
+/**
+ * stmmac_hw_fix_mac_speed: callback for speed selection
+ * @priv: driver private structure
+ * Description: on some platforms (e.g. ST), some HW system configuraton
+ * registers have to be set according to the link speed negotiated.
  */
 static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv)
 {
        struct phy_device *phydev = priv->phydev;
 
        if (likely(priv->plat->fix_mac_speed))
-               priv->plat->fix_mac_speed(priv->plat->bsp_priv,
-                                         phydev->speed);
+               priv->plat->fix_mac_speed(priv->plat->bsp_priv, phydev->speed);
 }
 
+/**
+ * stmmac_enable_eee_mode: Check and enter in LPI mode
+ * @priv: driver private structure
+ * Description: this function is to verify and enter in LPI mode for EEE.
+ */
 static void stmmac_enable_eee_mode(struct stmmac_priv *priv)
 {
        /* Check and enter in LPI mode */
@@ -233,19 +263,24 @@ static void stmmac_enable_eee_mode(struct stmmac_priv *priv)
                priv->hw->mac->set_eee_mode(priv->ioaddr);
 }
 
+/**
+ * stmmac_disable_eee_mode: disable/exit from EEE
+ * @priv: driver private structure
+ * Description: this function is to exit and disable EEE in case of
+ * LPI state is true. This is called by the xmit.
+ */
 void stmmac_disable_eee_mode(struct stmmac_priv *priv)
 {
-       /* Exit and disable EEE in case of we are are in LPI state. */
        priv->hw->mac->reset_eee_mode(priv->ioaddr);
        del_timer_sync(&priv->eee_ctrl_timer);
        priv->tx_path_in_lpi_mode = false;
 }
 
 /**
- * stmmac_eee_ctrl_timer
+ * stmmac_eee_ctrl_timer: EEE TX SW timer.
  * @arg : data hook
  * Description:
- *  If there is no data transfer and if we are not in LPI state,
+ *  if there is no data transfer and if we are not in LPI state,
  *  then MAC Transmitter can be moved to LPI state.
  */
 static void stmmac_eee_ctrl_timer(unsigned long arg)
@@ -257,8 +292,8 @@ static void stmmac_eee_ctrl_timer(unsigned long arg)
 }
 
 /**
- * stmmac_eee_init
- * @priv: private device pointer
+ * stmmac_eee_init: init EEE
+ * @priv: driver private structure
  * Description:
  *  If the EEE support has been enabled while configuring the driver,
  *  if the GMAC actually supports the EEE (from the HW cap reg) and the
@@ -294,16 +329,359 @@ out:
        return ret;
 }
 
+/**
+ * stmmac_eee_adjust: adjust HW EEE according to the speed
+ * @priv: driver private structure
+ * Description:
+ *     When the EEE has been already initialised we have to
+ *     modify the PLS bit in the LPI ctrl & status reg according
+ *     to the PHY link status. For this reason.
+ */
 static void stmmac_eee_adjust(struct stmmac_priv *priv)
 {
-       /* When the EEE has been already initialised we have to
-        * modify the PLS bit in the LPI ctrl & status reg according
-        * to the PHY link status. For this reason.
-        */
        if (priv->eee_enabled)
                priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link);
 }
 
+/* stmmac_get_tx_hwtstamp: get HW TX timestamps
+ * @priv: driver private structure
+ * @entry : descriptor index to be used.
+ * @skb : the socket buffer
+ * Description :
+ * This function will read timestamp from the descriptor & pass it to stack.
+ * and also perform some sanity checks.
+ */
+static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
+                                  unsigned int entry, struct sk_buff *skb)
+{
+       struct skb_shared_hwtstamps shhwtstamp;
+       u64 ns;
+       void *desc = NULL;
+
+       if (!priv->hwts_tx_en)
+               return;
+
+       /* exit if skb doesn't support hw tstamp */
+       if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)))
+               return;
+
+       if (priv->adv_ts)
+               desc = (priv->dma_etx + entry);
+       else
+               desc = (priv->dma_tx + entry);
+
+       /* check tx tstamp status */
+       if (!priv->hw->desc->get_tx_timestamp_status((struct dma_desc *)desc))
+               return;
+
+       /* get the valid tstamp */
+       ns = priv->hw->desc->get_timestamp(desc, priv->adv_ts);
+
+       memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
+       shhwtstamp.hwtstamp = ns_to_ktime(ns);
+       /* pass tstamp to stack */
+       skb_tstamp_tx(skb, &shhwtstamp);
+
+       return;
+}
+
+/* stmmac_get_rx_hwtstamp: get HW RX timestamps
+ * @priv: driver private structure
+ * @entry : descriptor index to be used.
+ * @skb : the socket buffer
+ * Description :
+ * This function will read received packet's timestamp from the descriptor
+ * and pass it to stack. It also perform some sanity checks.
+ */
+static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv,
+                                  unsigned int entry, struct sk_buff *skb)
+{
+       struct skb_shared_hwtstamps *shhwtstamp = NULL;
+       u64 ns;
+       void *desc = NULL;
+
+       if (!priv->hwts_rx_en)
+               return;
+
+       if (priv->adv_ts)
+               desc = (priv->dma_erx + entry);
+       else
+               desc = (priv->dma_rx + entry);
+
+       /* exit if rx tstamp is not valid */
+       if (!priv->hw->desc->get_rx_timestamp_status(desc, priv->adv_ts))
+               return;
+
+       /* get valid tstamp */
+       ns = priv->hw->desc->get_timestamp(desc, priv->adv_ts);
+       shhwtstamp = skb_hwtstamps(skb);
+       memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
+       shhwtstamp->hwtstamp = ns_to_ktime(ns);
+}
+
+/**
+ *  stmmac_hwtstamp_ioctl - control hardware timestamping.
+ *  @dev: device pointer.
+ *  @ifr: An IOCTL specefic structure, that can contain a pointer to
+ *  a proprietary structure used to pass information to the driver.
+ *  Description:
+ *  This function configures the MAC to enable/disable both outgoing(TX)
+ *  and incoming(RX) packets time stamping based on user input.
+ *  Return Value:
+ *  0 on success and an appropriate -ve integer on failure.
+ */
+static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
+{
+       struct stmmac_priv *priv = netdev_priv(dev);
+       struct hwtstamp_config config;
+       struct timespec now;
+       u64 temp = 0;
+       u32 ptp_v2 = 0;
+       u32 tstamp_all = 0;
+       u32 ptp_over_ipv4_udp = 0;
+       u32 ptp_over_ipv6_udp = 0;
+       u32 ptp_over_ethernet = 0;
+       u32 snap_type_sel = 0;
+       u32 ts_master_en = 0;
+       u32 ts_event_en = 0;
+       u32 value = 0;
+
+       if (!(priv->dma_cap.time_stamp || priv->adv_ts)) {
+               netdev_alert(priv->dev, "No support for HW time stamping\n");
+               priv->hwts_tx_en = 0;
+               priv->hwts_rx_en = 0;
+
+               return -EOPNOTSUPP;
+       }
+
+       if (copy_from_user(&config, ifr->ifr_data,
+                          sizeof(struct hwtstamp_config)))
+               return -EFAULT;
+
+       pr_debug("%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
+                __func__, config.flags, config.tx_type, config.rx_filter);
+
+       /* reserved for future extensions */
+       if (config.flags)
+               return -EINVAL;
+
+       switch (config.tx_type) {
+       case HWTSTAMP_TX_OFF:
+               priv->hwts_tx_en = 0;
+               break;
+       case HWTSTAMP_TX_ON:
+               priv->hwts_tx_en = 1;
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       if (priv->adv_ts) {
+               switch (config.rx_filter) {
+               case HWTSTAMP_FILTER_NONE:
+                       /* time stamp no incoming packet at all */
+                       config.rx_filter = HWTSTAMP_FILTER_NONE;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+                       /* PTP v1, UDP, any kind of event packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+                       /* take time stamp for all event messages */
+                       snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+                       /* PTP v1, UDP, Sync packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC;
+                       /* take time stamp for SYNC messages only */
+                       ts_event_en = PTP_TCR_TSEVNTENA;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+                       /* PTP v1, UDP, Delay_req packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ;
+                       /* take time stamp for Delay_Req messages only */
+                       ts_master_en = PTP_TCR_TSMSTRENA;
+                       ts_event_en = PTP_TCR_TSEVNTENA;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+                       /* PTP v2, UDP, any kind of event packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+                       ptp_v2 = PTP_TCR_TSVER2ENA;
+                       /* take time stamp for all event messages */
+                       snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+                       /* PTP v2, UDP, Sync packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC;
+                       ptp_v2 = PTP_TCR_TSVER2ENA;
+                       /* take time stamp for SYNC messages only */
+                       ts_event_en = PTP_TCR_TSEVNTENA;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+                       /* PTP v2, UDP, Delay_req packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ;
+                       ptp_v2 = PTP_TCR_TSVER2ENA;
+                       /* take time stamp for Delay_Req messages only */
+                       ts_master_en = PTP_TCR_TSMSTRENA;
+                       ts_event_en = PTP_TCR_TSEVNTENA;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V2_EVENT:
+                       /* PTP v2/802.AS1 any layer, any kind of event packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+                       ptp_v2 = PTP_TCR_TSVER2ENA;
+                       /* take time stamp for all event messages */
+                       snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       ptp_over_ethernet = PTP_TCR_TSIPENA;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V2_SYNC:
+                       /* PTP v2/802.AS1, any layer, Sync packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC;
+                       ptp_v2 = PTP_TCR_TSVER2ENA;
+                       /* take time stamp for SYNC messages only */
+                       ts_event_en = PTP_TCR_TSEVNTENA;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       ptp_over_ethernet = PTP_TCR_TSIPENA;
+                       break;
+
+               case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+                       /* PTP v2/802.AS1, any layer, Delay_req packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ;
+                       ptp_v2 = PTP_TCR_TSVER2ENA;
+                       /* take time stamp for Delay_Req messages only */
+                       ts_master_en = PTP_TCR_TSMSTRENA;
+                       ts_event_en = PTP_TCR_TSEVNTENA;
+
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+                       ptp_over_ethernet = PTP_TCR_TSIPENA;
+                       break;
+
+               case HWTSTAMP_FILTER_ALL:
+                       /* time stamp any incoming packet */
+                       config.rx_filter = HWTSTAMP_FILTER_ALL;
+                       tstamp_all = PTP_TCR_TSENALL;
+                       break;
+
+               default:
+                       return -ERANGE;
+               }
+       } else {
+               switch (config.rx_filter) {
+               case HWTSTAMP_FILTER_NONE:
+                       config.rx_filter = HWTSTAMP_FILTER_NONE;
+                       break;
+               default:
+                       /* PTP v1, UDP, any kind of event packet */
+                       config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+                       break;
+               }
+       }
+       priv->hwts_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1);
+
+       if (!priv->hwts_tx_en && !priv->hwts_rx_en)
+               priv->hw->ptp->config_hw_tstamping(priv->ioaddr, 0);
+       else {
+               value = (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | PTP_TCR_TSCTRLSSR |
+                        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);
+
+               /* calculate default added value:
+                * formula is :
+                * addend = (2^32)/freq_div_ratio;
+                * where, freq_div_ratio = STMMAC_SYSCLOCK/50MHz
+                * hence, addend = ((2^32) * 50MHz)/STMMAC_SYSCLOCK;
+                * NOTE: STMMAC_SYSCLOCK should be >= 50MHz to
+                *       achive 20ns accuracy.
+                *
+                * 2^x * y == (y << x), hence
+                * 2^32 * 50000000 ==> (50000000 << 32)
+                */
+               temp = (u64) (50000000ULL << 32);
+               priv->default_addend = div_u64(temp, STMMAC_SYSCLOCK);
+               priv->hw->ptp->config_addend(priv->ioaddr,
+                                            priv->default_addend);
+
+               /* initialize system time */
+               getnstimeofday(&now);
+               priv->hw->ptp->init_systime(priv->ioaddr, now.tv_sec,
+                                           now.tv_nsec);
+       }
+
+       return copy_to_user(ifr->ifr_data, &config,
+                           sizeof(struct hwtstamp_config)) ? -EFAULT : 0;
+}
+
+/**
+ * stmmac_init_ptp: init PTP
+ * @priv: driver private structure
+ * Description: this is to verify if the HW supports the PTPv1 or v2.
+ * This is done by looking at the HW cap. register.
+ * Also it registers the ptp driver.
+ */
+static int stmmac_init_ptp(struct stmmac_priv *priv)
+{
+       if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
+               return -EOPNOTSUPP;
+
+       if (netif_msg_hw(priv)) {
+               if (priv->dma_cap.time_stamp) {
+                       pr_debug("IEEE 1588-2002 Time Stamp supported\n");
+                       priv->adv_ts = 0;
+               }
+               if (priv->dma_cap.atime_stamp && priv->extend_desc) {
+                       pr_debug
+                           ("IEEE 1588-2008 Advanced Time Stamp supported\n");
+                       priv->adv_ts = 1;
+               }
+       }
+
+       priv->hw->ptp = &stmmac_ptp;
+       priv->hwts_tx_en = 0;
+       priv->hwts_rx_en = 0;
+
+       return stmmac_ptp_register(priv);
+}
+
+static void stmmac_release_ptp(struct stmmac_priv *priv)
+{
+       stmmac_ptp_unregister(priv);
+}
+
 /**
  * stmmac_adjust_link
  * @dev: net device structure
@@ -349,7 +727,7 @@ static void stmmac_adjust_link(struct net_device *dev)
                        case 1000:
                                if (likely(priv->plat->has_gmac))
                                        ctrl &= ~priv->hw->link.port;
-                                       stmmac_hw_fix_mac_speed(priv);
+                               stmmac_hw_fix_mac_speed(priv);
                                break;
                        case 100:
                        case 10:
@@ -367,8 +745,8 @@ static void stmmac_adjust_link(struct net_device *dev)
                                break;
                        default:
                                if (netif_msg_link(priv))
-                                       pr_warning("%s: Speed (%d) is not 10"
-                                      " or 100!\n", dev->name, phydev->speed);
+                                       pr_warn("%s: Speed (%d) not 10/100\n",
+                                               dev->name, phydev->speed);
                                break;
                        }
 
@@ -398,6 +776,31 @@ static void stmmac_adjust_link(struct net_device *dev)
        DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n");
 }
 
+/**
+ * stmmac_check_pcs_mode: verify if RGMII/SGMII is supported
+ * @priv: driver private structure
+ * Description: this is to verify if the HW supports the PCS.
+ * Physical Coding Sublayer (PCS) interface that can be used when the MAC is
+ * configured for the TBI, RTBI, or SGMII PHY interface.
+ */
+static void stmmac_check_pcs_mode(struct stmmac_priv *priv)
+{
+       int interface = priv->plat->interface;
+
+       if (priv->dma_cap.pcs) {
+               if ((interface & PHY_INTERFACE_MODE_RGMII) ||
+                   (interface & PHY_INTERFACE_MODE_RGMII_ID) ||
+                   (interface & PHY_INTERFACE_MODE_RGMII_RXID) ||
+                   (interface & PHY_INTERFACE_MODE_RGMII_TXID)) {
+                       pr_debug("STMMAC: PCS RGMII support enable\n");
+                       priv->pcs = STMMAC_PCS_RGMII;
+               } else if (interface & PHY_INTERFACE_MODE_SGMII) {
+                       pr_debug("STMMAC: PCS SGMII support enable\n");
+                       priv->pcs = STMMAC_PCS_SGMII;
+               }
+       }
+}
+
 /**
  * stmmac_init_phy - PHY initialization
  * @dev: net device structure
@@ -419,10 +822,10 @@ static int stmmac_init_phy(struct net_device *dev)
 
        if (priv->plat->phy_bus_name)
                snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x",
-                               priv->plat->phy_bus_name, priv->plat->bus_id);
+                        priv->plat->phy_bus_name, priv->plat->bus_id);
        else
                snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
-                               priv->plat->bus_id);
+                        priv->plat->bus_id);
 
        snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
                 priv->plat->phy_addr);
@@ -461,29 +864,57 @@ static int stmmac_init_phy(struct net_device *dev)
 }
 
 /**
- * display_ring
- * @p: pointer to the ring.
+ * stmmac_display_ring: display ring
+ * @head: pointer to the head of the ring passed.
  * @size: size of the ring.
- * Description: display all the descriptors within the ring.
+ * @extend_desc: to verify if extended descriptors are used.
+ * Description: display the control/status and buffer descriptors.
  */
-static void display_ring(struct dma_desc *p, int size)
+static void stmmac_display_ring(void *head, int size, int extend_desc)
 {
-       struct tmp_s {
-               u64 a;
-               unsigned int b;
-               unsigned int c;
-       };
        int i;
+       struct dma_extended_desc *ep = (struct dma_extended_desc *)head;
+       struct dma_desc *p = (struct dma_desc *)head;
+
        for (i = 0; i < size; i++) {
-               struct tmp_s *x = (struct tmp_s *)(p + i);
-               pr_info("\t%d [0x%x]: DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x",
-                      i, (unsigned int)virt_to_phys(&p[i]),
-                      (unsigned int)(x->a), (unsigned int)((x->a) >> 32),
-                      x->b, x->c);
+               u64 x;
+               if (extend_desc) {
+                       x = *(u64 *) ep;
+                       pr_info("%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n",
+                               i, (unsigned int)virt_to_phys(ep),
+                               (unsigned int)x, (unsigned int)(x >> 32),
+                               ep->basic.des2, ep->basic.des3);
+                       ep++;
+               } else {
+                       x = *(u64 *) p;
+                       pr_info("%d [0x%x]: 0x%x 0x%x 0x%x 0x%x",
+                               i, (unsigned int)virt_to_phys(p),
+                               (unsigned int)x, (unsigned int)(x >> 32),
+                               p->des2, p->des3);
+                       p++;
+               }
                pr_info("\n");
        }
 }
 
+static void stmmac_display_rings(struct stmmac_priv *priv)
+{
+       unsigned int txsize = priv->dma_tx_size;
+       unsigned int rxsize = priv->dma_rx_size;
+
+       if (priv->extend_desc) {
+               pr_info("Extended RX descriptor ring:\n");
+               stmmac_display_ring((void *)priv->dma_erx, rxsize, 1);
+               pr_info("Extended TX descriptor ring:\n");
+               stmmac_display_ring((void *)priv->dma_etx, txsize, 1);
+       } else {
+               pr_info("RX descriptor ring:\n");
+               stmmac_display_ring((void *)priv->dma_rx, rxsize, 0);
+               pr_info("TX descriptor ring:\n");
+               stmmac_display_ring((void *)priv->dma_tx, txsize, 0);
+       }
+}
+
 static int stmmac_set_bfsize(int mtu, int bufsize)
 {
        int ret = bufsize;
@@ -500,6 +931,65 @@ static int stmmac_set_bfsize(int mtu, int bufsize)
        return ret;
 }
 
+/**
+ * stmmac_clear_descriptors: clear descriptors
+ * @priv: driver private structure
+ * Description: this function is called to clear the tx and rx descriptors
+ * in case of both basic and extended descriptors are used.
+ */
+static void stmmac_clear_descriptors(struct stmmac_priv *priv)
+{
+       int i;
+       unsigned int txsize = priv->dma_tx_size;
+       unsigned int rxsize = priv->dma_rx_size;
+
+       /* Clear the Rx/Tx descriptors */
+       for (i = 0; i < rxsize; i++)
+               if (priv->extend_desc)
+                       priv->hw->desc->init_rx_desc(&priv->dma_erx[i].basic,
+                                                    priv->use_riwt, priv->mode,
+                                                    (i == rxsize - 1));
+               else
+                       priv->hw->desc->init_rx_desc(&priv->dma_rx[i],
+                                                    priv->use_riwt, priv->mode,
+                                                    (i == rxsize - 1));
+       for (i = 0; i < txsize; i++)
+               if (priv->extend_desc)
+                       priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic,
+                                                    priv->mode,
+                                                    (i == txsize - 1));
+               else
+                       priv->hw->desc->init_tx_desc(&priv->dma_tx[i],
+                                                    priv->mode,
+                                                    (i == txsize - 1));
+}
+
+static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
+                                 int i)
+{
+       struct sk_buff *skb;
+
+       skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN,
+                                GFP_KERNEL);
+       if (unlikely(skb == NULL)) {
+               pr_err("%s: Rx init fails; skb is NULL\n", __func__);
+               return 1;
+       }
+       skb_reserve(skb, NET_IP_ALIGN);
+       priv->rx_skbuff[i] = skb;
+       priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
+                                               priv->dma_buf_sz,
+                                               DMA_FROM_DEVICE);
+
+       p->des2 = priv->rx_skbuff_dma[i];
+
+       if ((priv->mode == STMMAC_RING_MODE) &&
+           (priv->dma_buf_sz == BUF_SIZE_16KiB))
+               priv->hw->ring->init_desc3(p);
+
+       return 0;
+}
+
 /**
  * init_dma_desc_rings - init the RX/TX descriptor rings
  * @dev: net device structure
@@ -511,110 +1001,114 @@ static void init_dma_desc_rings(struct net_device *dev)
 {
        int i;
        struct stmmac_priv *priv = netdev_priv(dev);
-       struct sk_buff *skb;
        unsigned int txsize = priv->dma_tx_size;
        unsigned int rxsize = priv->dma_rx_size;
-       unsigned int bfsize;
-       int dis_ic = 0;
-       int des3_as_data_buf = 0;
+       unsigned int bfsize = 0;
 
        /* Set the max buffer size according to the DESC mode
-        * and the MTU. Note that RING mode allows 16KiB bsize. */
-       bfsize = priv->hw->ring->set_16kib_bfsize(dev->mtu);
+        * and the MTU. Note that RING mode allows 16KiB bsize.
+        */
+       if (priv->mode == STMMAC_RING_MODE)
+               bfsize = priv->hw->ring->set_16kib_bfsize(dev->mtu);
 
-       if (bfsize == BUF_SIZE_16KiB)
-               des3_as_data_buf = 1;
-       else
+       if (bfsize < BUF_SIZE_16KiB)
                bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz);
 
        DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n",
            txsize, rxsize, bfsize);
 
-       priv->rx_skbuff_dma = kmalloc_array(rxsize, sizeof(dma_addr_t),
-                                           GFP_KERNEL);
-       priv->rx_skbuff = kmalloc_array(rxsize, sizeof(struct sk_buff *),
-                                       GFP_KERNEL);
-       priv->dma_rx =
-           (struct dma_desc *)dma_alloc_coherent(priv->device,
-                                                 rxsize *
+       if (priv->extend_desc) {
+               priv->dma_erx = dma_alloc_coherent(priv->device, rxsize *
+                                                  sizeof(struct
+                                                         dma_extended_desc),
+                                                  &priv->dma_rx_phy,
+                                                  GFP_KERNEL);
+               priv->dma_etx = dma_alloc_coherent(priv->device, txsize *
+                                                  sizeof(struct
+                                                         dma_extended_desc),
+                                                  &priv->dma_tx_phy,
+                                                  GFP_KERNEL);
+               if ((!priv->dma_erx) || (!priv->dma_etx))
+                       return;
+       } else {
+               priv->dma_rx = dma_alloc_coherent(priv->device, rxsize *
                                                  sizeof(struct dma_desc),
                                                  &priv->dma_rx_phy,
                                                  GFP_KERNEL);
-       priv->tx_skbuff = kmalloc_array(txsize, sizeof(struct sk_buff *),
-                                       GFP_KERNEL);
-       priv->dma_tx =
-           (struct dma_desc *)dma_alloc_coherent(priv->device,
-                                                 txsize *
+               priv->dma_tx = dma_alloc_coherent(priv->device, txsize *
                                                  sizeof(struct dma_desc),
                                                  &priv->dma_tx_phy,
                                                  GFP_KERNEL);
-
-       if ((priv->dma_rx == NULL) || (priv->dma_tx == NULL)) {
-               pr_err("%s:ERROR allocating the DMA Tx/Rx desc\n", __func__);
-               return;
+               if ((!priv->dma_rx) || (!priv->dma_tx))
+                       return;
        }
 
-       DBG(probe, INFO, "stmmac (%s) DMA desc: virt addr (Rx %p, "
-           "Tx %p)\n\tDMA phy addr (Rx 0x%08x, Tx 0x%08x)\n",
-           dev->name, priv->dma_rx, priv->dma_tx,
-           (unsigned int)priv->dma_rx_phy, (unsigned int)priv->dma_tx_phy);
+       priv->rx_skbuff_dma = kmalloc_array(rxsize, sizeof(dma_addr_t),
+                                           GFP_KERNEL);
+       priv->rx_skbuff = kmalloc_array(rxsize, sizeof(struct sk_buff *),
+                                       GFP_KERNEL);
+       priv->tx_skbuff_dma = kmalloc_array(txsize, sizeof(dma_addr_t),
+                                           GFP_KERNEL);
+       priv->tx_skbuff = kmalloc_array(txsize, sizeof(struct sk_buff *),
+                                       GFP_KERNEL);
+       if (netif_msg_drv(priv))
+               pr_debug("(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", __func__,
+                        (u32) priv->dma_rx_phy, (u32) priv->dma_tx_phy);
 
        /* RX INITIALIZATION */
-       DBG(probe, INFO, "stmmac: SKB addresses:\n"
-                        "skb\t\tskb data\tdma data\n");
-
+       DBG(probe, INFO, "stmmac: SKB addresses:\nskb\t\tskb data\tdma data\n");
        for (i = 0; i < rxsize; i++) {
-               struct dma_desc *p = priv->dma_rx + i;
+               struct dma_desc *p;
+               if (priv->extend_desc)
+                       p = &((priv->dma_erx + i)->basic);
+               else
+                       p = priv->dma_rx + i;
 
-               skb = __netdev_alloc_skb(dev, bfsize + NET_IP_ALIGN,
-                                        GFP_KERNEL);
-               if (unlikely(skb == NULL)) {
-                       pr_err("%s: Rx init fails; skb is NULL\n", __func__);
+               if (stmmac_init_rx_buffers(priv, p, i))
                        break;
-               }
-               skb_reserve(skb, NET_IP_ALIGN);
-               priv->rx_skbuff[i] = skb;
-               priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
-                                               bfsize, DMA_FROM_DEVICE);
-
-               p->des2 = priv->rx_skbuff_dma[i];
-
-               priv->hw->ring->init_desc3(des3_as_data_buf, p);
 
                DBG(probe, INFO, "[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i],
-                       priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]);
+                   priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]);
        }
        priv->cur_rx = 0;
        priv->dirty_rx = (unsigned int)(i - rxsize);
        priv->dma_buf_sz = bfsize;
        buf_sz = bfsize;
 
+       /* Setup the chained descriptor addresses */
+       if (priv->mode == STMMAC_CHAIN_MODE) {
+               if (priv->extend_desc) {
+                       priv->hw->chain->init(priv->dma_erx, priv->dma_rx_phy,
+                                             rxsize, 1);
+                       priv->hw->chain->init(priv->dma_etx, priv->dma_tx_phy,
+                                             txsize, 1);
+               } else {
+                       priv->hw->chain->init(priv->dma_rx, priv->dma_rx_phy,
+                                             rxsize, 0);
+                       priv->hw->chain->init(priv->dma_tx, priv->dma_tx_phy,
+                                             txsize, 0);
+               }
+       }
+
        /* TX INITIALIZATION */
        for (i = 0; i < txsize; i++) {
+               struct dma_desc *p;
+               if (priv->extend_desc)
+                       p = &((priv->dma_etx + i)->basic);
+               else
+                       p = priv->dma_tx + i;
+               p->des2 = 0;
+               priv->tx_skbuff_dma[i] = 0;
                priv->tx_skbuff[i] = NULL;
-               priv->dma_tx[i].des2 = 0;
        }
 
-       /* In case of Chained mode this sets the des3 to the next
-        * element in the chain */
-       priv->hw->ring->init_dma_chain(priv->dma_rx, priv->dma_rx_phy, rxsize);
-       priv->hw->ring->init_dma_chain(priv->dma_tx, priv->dma_tx_phy, txsize);
-
        priv->dirty_tx = 0;
        priv->cur_tx = 0;
 
-       if (priv->use_riwt)
-               dis_ic = 1;
-       /* Clear the Rx/Tx descriptors */
-       priv->hw->desc->init_rx_desc(priv->dma_rx, rxsize, dis_ic);
-       priv->hw->desc->init_tx_desc(priv->dma_tx, txsize);
+       stmmac_clear_descriptors(priv);
 
-       if (netif_msg_hw(priv)) {
-               pr_info("RX descriptor ring:\n");
-               display_ring(priv->dma_rx, rxsize);
-               pr_info("TX descriptor ring:\n");
-               display_ring(priv->dma_tx, txsize);
-       }
+       if (netif_msg_hw(priv))
+               stmmac_display_rings(priv);
 }
 
 static void dma_free_rx_skbufs(struct stmmac_priv *priv)
@@ -637,13 +1131,20 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv)
 
        for (i = 0; i < priv->dma_tx_size; i++) {
                if (priv->tx_skbuff[i] != NULL) {
-                       struct dma_desc *p = priv->dma_tx + i;
-                       if (p->des2)
-                               dma_unmap_single(priv->device, p->des2,
+                       struct dma_desc *p;
+                       if (priv->extend_desc)
+                               p = &((priv->dma_etx + i)->basic);
+                       else
+                               p = priv->dma_tx + i;
+
+                       if (priv->tx_skbuff_dma[i])
+                               dma_unmap_single(priv->device,
+                                                priv->tx_skbuff_dma[i],
                                                 priv->hw->desc->get_tx_len(p),
                                                 DMA_TO_DEVICE);
                        dev_kfree_skb_any(priv->tx_skbuff[i]);
                        priv->tx_skbuff[i] = NULL;
+                       priv->tx_skbuff_dma[i] = 0;
                }
        }
 }
@@ -654,29 +1155,38 @@ static void free_dma_desc_resources(struct stmmac_priv *priv)
        dma_free_rx_skbufs(priv);
        dma_free_tx_skbufs(priv);
 
-       /* Free the region of consistent memory previously allocated for
-        * the DMA */
-       dma_free_coherent(priv->device,
-                         priv->dma_tx_size * sizeof(struct dma_desc),
-                         priv->dma_tx, priv->dma_tx_phy);
-       dma_free_coherent(priv->device,
-                         priv->dma_rx_size * sizeof(struct dma_desc),
-                         priv->dma_rx, priv->dma_rx_phy);
+       /* Free DMA regions of consistent memory previously allocated */
+       if (!priv->extend_desc) {
+               dma_free_coherent(priv->device,
+                                 priv->dma_tx_size * sizeof(struct dma_desc),
+                                 priv->dma_tx, priv->dma_tx_phy);
+               dma_free_coherent(priv->device,
+                                 priv->dma_rx_size * sizeof(struct dma_desc),
+                                 priv->dma_rx, priv->dma_rx_phy);
+       } else {
+               dma_free_coherent(priv->device, priv->dma_tx_size *
+                                 sizeof(struct dma_extended_desc),
+                                 priv->dma_etx, priv->dma_tx_phy);
+               dma_free_coherent(priv->device, priv->dma_rx_size *
+                                 sizeof(struct dma_extended_desc),
+                                 priv->dma_erx, priv->dma_rx_phy);
+       }
        kfree(priv->rx_skbuff_dma);
        kfree(priv->rx_skbuff);
+       kfree(priv->tx_skbuff_dma);
        kfree(priv->tx_skbuff);
 }
 
 /**
  *  stmmac_dma_operation_mode - HW DMA operation mode
- *  @priv : pointer to the private device structure.
+ *  @priv: driver private structure
  *  Description: it sets the DMA operation mode: tx/rx DMA thresholds
  *  or Store-And-Forward capability.
  */
 static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
 {
        if (likely(priv->plat->force_sf_dma_mode ||
-               ((priv->plat->tx_coe) && (!priv->no_csum_insertion)))) {
+                  ((priv->plat->tx_coe) && (!priv->no_csum_insertion)))) {
                /*
                 * In case of GMAC, SF mode can be enabled
                 * to perform the TX COE in HW. This depends on:
@@ -684,8 +1194,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
                 * 2) There is no bugged Jumbo frame support
                 *    that needs to not insert csum in the TDES.
                 */
-               priv->hw->dma->dma_mode(priv->ioaddr,
-                                       SF_DMA_MODE, SF_DMA_MODE);
+               priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE);
                tc = SF_DMA_MODE;
        } else
                priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE);
@@ -693,7 +1202,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
 
 /**
  * stmmac_tx_clean:
- * @priv: private data pointer
+ * @priv: driver private structure
  * Description: it reclaims resources after transmission completes.
  */
 static void stmmac_tx_clean(struct stmmac_priv *priv)
@@ -708,40 +1217,50 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)
                int last;
                unsigned int entry = priv->dirty_tx % txsize;
                struct sk_buff *skb = priv->tx_skbuff[entry];
-               struct dma_desc *p = priv->dma_tx + entry;
+               struct dma_desc *p;
+
+               if (priv->extend_desc)
+                       p = (struct dma_desc *)(priv->dma_etx + entry);
+               else
+                       p = priv->dma_tx + entry;
 
                /* Check if the descriptor is owned by the DMA. */
                if (priv->hw->desc->get_tx_owner(p))
                        break;
 
-               /* Verify tx error by looking at the last segment */
+               /* Verify tx error by looking at the last segment. */
                last = priv->hw->desc->get_tx_ls(p);
                if (likely(last)) {
                        int tx_error =
-                               priv->hw->desc->tx_status(&priv->dev->stats,
-                                                         &priv->xstats, p,
-                                                         priv->ioaddr);
+                           priv->hw->desc->tx_status(&priv->dev->stats,
+                                                     &priv->xstats, p,
+                                                     priv->ioaddr);
                        if (likely(tx_error == 0)) {
                                priv->dev->stats.tx_packets++;
                                priv->xstats.tx_pkt_n++;
                        } else
                                priv->dev->stats.tx_errors++;
+
+                       stmmac_get_tx_hwtstamp(priv, entry, skb);
                }
                TX_DBG("%s: curr %d, dirty %d\n", __func__,
-                       priv->cur_tx, priv->dirty_tx);
+                      priv->cur_tx, priv->dirty_tx);
 
-               if (likely(p->des2))
-                       dma_unmap_single(priv->device, p->des2,
+               if (likely(priv->tx_skbuff_dma[entry])) {
+                       dma_unmap_single(priv->device,
+                                        priv->tx_skbuff_dma[entry],
                                         priv->hw->desc->get_tx_len(p),
                                         DMA_TO_DEVICE);
-               priv->hw->ring->clean_desc3(p);
+                       priv->tx_skbuff_dma[entry] = 0;
+               }
+               priv->hw->ring->clean_desc3(priv, p);
 
                if (likely(skb != NULL)) {
                        dev_kfree_skb(skb);
                        priv->tx_skbuff[entry] = NULL;
                }
 
-               priv->hw->desc->release_tx_desc(p);
+               priv->hw->desc->release_tx_desc(p, priv->mode);
 
                priv->dirty_tx++;
        }
@@ -749,7 +1268,7 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)
                     stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv))) {
                netif_tx_lock(priv->dev);
                if (netif_queue_stopped(priv->dev) &&
-                    stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv)) {
+                   stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv)) {
                        TX_DBG("%s: restart transmit\n", __func__);
                        netif_wake_queue(priv->dev);
                }
@@ -773,20 +1292,29 @@ static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv)
        priv->hw->dma->disable_dma_irq(priv->ioaddr);
 }
 
-
 /**
- * stmmac_tx_err:
- * @priv: pointer to the private device structure
+ * stmmac_tx_err: irq tx error mng function
+ * @priv: driver private structure
  * Description: it cleans the descriptors and restarts the transmission
  * in case of errors.
  */
 static void stmmac_tx_err(struct stmmac_priv *priv)
 {
+       int i;
+       int txsize = priv->dma_tx_size;
        netif_stop_queue(priv->dev);
 
        priv->hw->dma->stop_tx(priv->ioaddr);
        dma_free_tx_skbufs(priv);
-       priv->hw->desc->init_tx_desc(priv->dma_tx, priv->dma_tx_size);
+       for (i = 0; i < txsize; i++)
+               if (priv->extend_desc)
+                       priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic,
+                                                    priv->mode,
+                                                    (i == txsize - 1));
+               else
+                       priv->hw->desc->init_tx_desc(&priv->dma_tx[i],
+                                                    priv->mode,
+                                                    (i == txsize - 1));
        priv->dirty_tx = 0;
        priv->cur_tx = 0;
        priv->hw->dma->start_tx(priv->ioaddr);
@@ -795,6 +1323,14 @@ static void stmmac_tx_err(struct stmmac_priv *priv)
        netif_wake_queue(priv->dev);
 }
 
+/**
+ * stmmac_dma_interrupt: DMA ISR
+ * @priv: driver private structure
+ * Description: this is the DMA ISR. It is called by the main ISR.
+ * It calls the dwmac dma routine to understand which type of interrupt
+ * happened. In case of there is a Normal interrupt and either TX or RX
+ * interrupt happened so the NAPI is scheduled.
+ */
 static void stmmac_dma_interrupt(struct stmmac_priv *priv)
 {
        int status;
@@ -817,13 +1353,16 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
                stmmac_tx_err(priv);
 }
 
+/**
+ * stmmac_mmc_setup: setup the Mac Management Counters (MMC)
+ * @priv: driver private structure
+ * Description: this masks the MMC irq, in fact, the counters are managed in SW.
+ */
 static void stmmac_mmc_setup(struct stmmac_priv *priv)
 {
        unsigned int mode = MMC_CNTRL_RESET_ON_READ | MMC_CNTRL_COUNTER_RESET |
-                           MMC_CNTRL_PRESET | MMC_CNTRL_FULL_HALF_PRESET;
+           MMC_CNTRL_PRESET | MMC_CNTRL_FULL_HALF_PRESET;
 
-       /* Mask MMC irq, counters are managed in SW and registers
-        * are cleared on each READ eventually. */
        dwmac_mmc_intr_all_mask(priv->ioaddr);
 
        if (priv->dma_cap.rmon) {
@@ -837,8 +1376,7 @@ static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv)
 {
        u32 hwid = priv->hw->synopsys_uid;
 
-       /* Only check valid Synopsys Id because old MAC chips
-        * have no HW registers where get the ID */
+       /* Check Synopsys Id (not available on old chips) */
        if (likely(hwid)) {
                u32 uid = ((hwid & 0x0000ff00) >> 8);
                u32 synid = (hwid & 0x000000ff);
@@ -852,14 +1390,24 @@ static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv)
 }
 
 /**
- * stmmac_selec_desc_mode
- * @priv : private structure
- * Description: select the Enhanced/Alternate or Normal descriptors
+ * stmmac_selec_desc_mode: to select among: normal/alternate/extend descriptors
+ * @priv: driver private structure
+ * Description: select the Enhanced/Alternate or Normal descriptors.
+ * In case of Enhanced/Alternate, it looks at the extended descriptors are
+ * supported by the HW cap. register.
  */
 static void stmmac_selec_desc_mode(struct stmmac_priv *priv)
 {
        if (priv->plat->enh_desc) {
                pr_info(" Enhanced/Alternate descriptors\n");
+
+               /* GMAC older than 3.50 has no extended descriptors */
+               if (priv->synopsys_id >= DWMAC_CORE_3_50) {
+                       pr_info("\tEnabled extended descriptors\n");
+                       priv->extend_desc = 1;
+               } else
+                       pr_warn("Extended descriptors not supported\n");
+
                priv->hw->desc = &enh_desc_ops;
        } else {
                pr_info(" Normal descriptors\n");
@@ -868,8 +1416,8 @@ static void stmmac_selec_desc_mode(struct stmmac_priv *priv)
 }
 
 /**
- * stmmac_get_hw_features
- * @priv : private device pointer
+ * stmmac_get_hw_features: get MAC capabilities from the HW cap. register.
+ * @priv: driver private structure
  * Description:
  *  new GMAC chip generations have a new register to indicate the
  *  presence of the optional feature/functions.
@@ -887,69 +1435,78 @@ static int stmmac_get_hw_features(struct stmmac_priv *priv)
                priv->dma_cap.mbps_1000 = (hw_cap & DMA_HW_FEAT_GMIISEL) >> 1;
                priv->dma_cap.half_duplex = (hw_cap & DMA_HW_FEAT_HDSEL) >> 2;
                priv->dma_cap.hash_filter = (hw_cap & DMA_HW_FEAT_HASHSEL) >> 4;
-               priv->dma_cap.multi_addr =
-                       (hw_cap & DMA_HW_FEAT_ADDMACADRSEL) >> 5;
+               priv->dma_cap.multi_addr = (hw_cap & DMA_HW_FEAT_ADDMAC) >> 5;
                priv->dma_cap.pcs = (hw_cap & DMA_HW_FEAT_PCSSEL) >> 6;
                priv->dma_cap.sma_mdio = (hw_cap & DMA_HW_FEAT_SMASEL) >> 8;
                priv->dma_cap.pmt_remote_wake_up =
-                       (hw_cap & DMA_HW_FEAT_RWKSEL) >> 9;
+                   (hw_cap & DMA_HW_FEAT_RWKSEL) >> 9;
                priv->dma_cap.pmt_magic_frame =
-                       (hw_cap & DMA_HW_FEAT_MGKSEL) >> 10;
+                   (hw_cap & DMA_HW_FEAT_MGKSEL) >> 10;
                /* MMC */
                priv->dma_cap.rmon = (hw_cap & DMA_HW_FEAT_MMCSEL) >> 11;
-               /* IEEE 1588-2002*/
+               /* IEEE 1588-2002 */
                priv->dma_cap.time_stamp =
-                       (hw_cap & DMA_HW_FEAT_TSVER1SEL) >> 12;
-               /* IEEE 1588-2008*/
+                   (hw_cap & DMA_HW_FEAT_TSVER1SEL) >> 12;
+               /* IEEE 1588-2008 */
                priv->dma_cap.atime_stamp =
-                       (hw_cap & DMA_HW_FEAT_TSVER2SEL) >> 13;
+                   (hw_cap & DMA_HW_FEAT_TSVER2SEL) >> 13;
                /* 802.3az - Energy-Efficient Ethernet (EEE) */
                priv->dma_cap.eee = (hw_cap & DMA_HW_FEAT_EEESEL) >> 14;
                priv->dma_cap.av = (hw_cap & DMA_HW_FEAT_AVSEL) >> 15;
                /* TX and RX csum */
                priv->dma_cap.tx_coe = (hw_cap & DMA_HW_FEAT_TXCOESEL) >> 16;
                priv->dma_cap.rx_coe_type1 =
-                       (hw_cap & DMA_HW_FEAT_RXTYP1COE) >> 17;
+                   (hw_cap & DMA_HW_FEAT_RXTYP1COE) >> 17;
                priv->dma_cap.rx_coe_type2 =
-                       (hw_cap & DMA_HW_FEAT_RXTYP2COE) >> 18;
+                   (hw_cap & DMA_HW_FEAT_RXTYP2COE) >> 18;
                priv->dma_cap.rxfifo_over_2048 =
-                       (hw_cap & DMA_HW_FEAT_RXFIFOSIZE) >> 19;
+                   (hw_cap & DMA_HW_FEAT_RXFIFOSIZE) >> 19;
                /* TX and RX number of channels */
                priv->dma_cap.number_rx_channel =
-                       (hw_cap & DMA_HW_FEAT_RXCHCNT) >> 20;
+                   (hw_cap & DMA_HW_FEAT_RXCHCNT) >> 20;
                priv->dma_cap.number_tx_channel =
-                       (hw_cap & DMA_HW_FEAT_TXCHCNT) >> 22;
-               /* Alternate (enhanced) DESC mode*/
-               priv->dma_cap.enh_desc =
-                       (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24;
+                   (hw_cap & DMA_HW_FEAT_TXCHCNT) >> 22;
+               /* Alternate (enhanced) DESC mode */
+               priv->dma_cap.enh_desc = (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24;
        }
 
        return hw_cap;
 }
 
+/**
+ * stmmac_check_ether_addr: check if the MAC addr is valid
+ * @priv: driver private structure
+ * Description:
+ * it is to verify if the MAC address is valid, in case of failures it
+ * generates a random MAC address
+ */
 static void stmmac_check_ether_addr(struct stmmac_priv *priv)
 {
-       /* verify if the MAC address is valid, in case of failures it
-        * generates a random MAC address */
        if (!is_valid_ether_addr(priv->dev->dev_addr)) {
                priv->hw->mac->get_umac_addr((void __iomem *)
                                             priv->dev->base_addr,
                                             priv->dev->dev_addr, 0);
-               if  (!is_valid_ether_addr(priv->dev->dev_addr))
+               if (!is_valid_ether_addr(priv->dev->dev_addr))
                        eth_hw_addr_random(priv->dev);
        }
-       pr_warning("%s: device MAC address %pM\n", priv->dev->name,
-                                                  priv->dev->dev_addr);
+       pr_warn("%s: device MAC address %pM\n", priv->dev->name,
+               priv->dev->dev_addr);
 }
 
+/**
+ * stmmac_init_dma_engine: DMA init.
+ * @priv: driver private structure
+ * Description:
+ * It inits the DMA invoking the specific MAC/GMAC callback.
+ * Some DMA parameters can be passed from the platform;
+ * in case of these are not passed a default is kept for the MAC or GMAC.
+ */
 static int stmmac_init_dma_engine(struct stmmac_priv *priv)
 {
        int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, burst_len = 0;
        int mixed_burst = 0;
+       int atds = 0;
 
-       /* Some DMA parameters can be passed from the platform;
-        * in case of these are not passed we keep a default
-        * (good for all the chips) and init the DMA! */
        if (priv->plat->dma_cfg) {
                pbl = priv->plat->dma_cfg->pbl;
                fixed_burst = priv->plat->dma_cfg->fixed_burst;
@@ -957,13 +1514,16 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
                burst_len = priv->plat->dma_cfg->burst_len;
        }
 
+       if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
+               atds = 1;
+
        return priv->hw->dma->init(priv->ioaddr, pbl, fixed_burst, mixed_burst,
                                   burst_len, priv->dma_tx_phy,
-                                  priv->dma_rx_phy);
+                                  priv->dma_rx_phy, atds);
 }
 
 /**
- * stmmac_tx_timer:
+ * stmmac_tx_timer: mitigation sw timer for tx.
  * @data: data pointer
  * Description:
  * This is the timer handler to directly invoke the stmmac_tx_clean.
@@ -976,8 +1536,8 @@ static void stmmac_tx_timer(unsigned long data)
 }
 
 /**
- * stmmac_tx_timer:
- * @priv: private data structure
+ * stmmac_init_tx_coalesce: init tx mitigation options.
+ * @priv: driver private structure
  * Description:
  * This inits the transmit coalesce parameters: i.e. timer rate,
  * timer handler and default threshold used for enabling the
@@ -1012,10 +1572,14 @@ static int stmmac_open(struct net_device *dev)
 
        stmmac_check_ether_addr(priv);
 
-       ret = stmmac_init_phy(dev);
-       if (unlikely(ret)) {
-               pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret);
-               goto open_error;
+       if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
+           priv->pcs != STMMAC_PCS_RTBI) {
+               ret = stmmac_init_phy(dev);
+               if (ret) {
+                       pr_err("%s: Cannot attach to PHY (error: %d)\n",
+                              __func__, ret);
+                       goto open_error;
+               }
        }
 
        /* Create and initialize the TX/RX descriptors chains. */
@@ -1043,7 +1607,7 @@ static int stmmac_open(struct net_device *dev)
 
        /* Request the IRQ lines */
        ret = request_irq(dev->irq, stmmac_interrupt,
-                        IRQF_SHARED, dev->name, dev);
+                         IRQF_SHARED, dev->name, dev);
        if (unlikely(ret < 0)) {
                pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n",
                       __func__, dev->irq, ret);
@@ -1055,8 +1619,8 @@ static int stmmac_open(struct net_device *dev)
                ret = request_irq(priv->wol_irq, stmmac_interrupt,
                                  IRQF_SHARED, dev->name, dev);
                if (unlikely(ret < 0)) {
-                       pr_err("%s: ERROR: allocating the ext WoL IRQ %d "
-                              "(error: %d)\n", __func__, priv->wol_irq, ret);
+                       pr_err("%s: ERROR: allocating the WoL IRQ %d (%d)\n",
+                              __func__, priv->wol_irq, ret);
                        goto open_error_wolirq;
                }
        }
@@ -1084,10 +1648,14 @@ static int stmmac_open(struct net_device *dev)
 
        stmmac_mmc_setup(priv);
 
+       ret = stmmac_init_ptp(priv);
+       if (ret)
+               pr_warn("%s: failed PTP initialisation\n", __func__);
+
 #ifdef CONFIG_STMMAC_DEBUG_FS
        ret = stmmac_init_fs(dev);
        if (ret < 0)
-               pr_warning("%s: failed debugFS registration\n", __func__);
+               pr_warn("%s: failed debugFS registration\n", __func__);
 #endif
        /* Start the ball rolling... */
        DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name);
@@ -1104,7 +1672,13 @@ static int stmmac_open(struct net_device *dev)
                phy_start(priv->phydev);
 
        priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS_TIMER;
-       priv->eee_enabled = stmmac_eee_init(priv);
+
+       /* Using PCS we cannot dial with the phy registers at this stage
+        * so we do not support extra feature like EEE.
+        */
+       if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
+           priv->pcs != STMMAC_PCS_RTBI)
+               priv->eee_enabled = stmmac_eee_init(priv);
 
        stmmac_init_tx_coalesce(priv);
 
@@ -1113,6 +1687,9 @@ static int stmmac_open(struct net_device *dev)
                priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT);
        }
 
+       if (priv->pcs && priv->hw->mac->ctrl_ane)
+               priv->hw->mac->ctrl_ane(priv->ioaddr, 0);
+
        napi_enable(&priv->napi);
        netif_start_queue(dev);
 
@@ -1184,21 +1761,25 @@ static int stmmac_release(struct net_device *dev)
 #endif
        clk_disable_unprepare(priv->stmmac_clk);
 
+       stmmac_release_ptp(priv);
+
        return 0;
 }
 
 /**
- *  stmmac_xmit:
+ *  stmmac_xmit: Tx entry point of the driver
  *  @skb : the socket buffer
  *  @dev : device pointer
- *  Description : Tx entry point of the driver.
+ *  Description : this is the tx entry point of the driver.
+ *  It programs the chain or the ring and supports oversized frames
+ *  and SG feature.
  */
 static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct stmmac_priv *priv = netdev_priv(dev);
        unsigned int txsize = priv->dma_tx_size;
        unsigned int entry;
-       int i, csum_insertion = 0;
+       int i, csum_insertion = 0, is_jumbo = 0;
        int nfrags = skb_shinfo(skb)->nr_frags;
        struct dma_desc *desc, *first;
        unsigned int nopaged_len = skb_headlen(skb);
@@ -1207,8 +1788,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
                if (!netif_queue_stopped(dev)) {
                        netif_stop_queue(dev);
                        /* This is a hard error, log it. */
-                       pr_err("%s: BUG! Tx Ring full when queue awake\n",
-                               __func__);
+                       pr_err("%s: Tx Ring full when queue awake\n", __func__);
                }
                return NETDEV_TX_BUSY;
        }
@@ -1222,10 +1802,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
 #ifdef STMMAC_XMIT_DEBUG
        if ((skb->len > ETH_FRAME_LEN) || nfrags)
-               pr_debug("stmmac xmit: [entry %d]\n"
-                        "\tskb addr %p - len: %d - nopaged_len: %d\n"
+               pr_debug("%s: [entry %d]: skb addr %p len: %d nopagedlen: %d\n"
                         "\tn_frags: %d - ip_summed: %d - %s gso\n"
-                        "\ttx_count_frames %d\n", entry,
+                        "\ttx_count_frames %d\n", __func__, entry,
                         skb, skb->len, nopaged_len, nfrags, skb->ip_summed,
                         !skb_is_gso(skb) ? "isn't" : "is",
                         priv->tx_count_frames);
@@ -1233,7 +1812,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
        csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
 
-       desc = priv->dma_tx + entry;
+       if (priv->extend_desc)
+               desc = (struct dma_desc *)(priv->dma_etx + entry);
+       else
+               desc = priv->dma_tx + entry;
+
        first = desc;
 
 #ifdef STMMAC_XMIT_DEBUG
@@ -1244,28 +1827,46 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 #endif
        priv->tx_skbuff[entry] = skb;
 
-       if (priv->hw->ring->is_jumbo_frm(skb->len, priv->plat->enh_desc)) {
-               entry = priv->hw->ring->jumbo_frm(priv, skb, csum_insertion);
-               desc = priv->dma_tx + entry;
+       /* To program the descriptors according to the size of the frame */
+       if (priv->mode == STMMAC_RING_MODE) {
+               is_jumbo = priv->hw->ring->is_jumbo_frm(skb->len,
+                                                       priv->plat->enh_desc);
+               if (unlikely(is_jumbo))
+                       entry = priv->hw->ring->jumbo_frm(priv, skb,
+                                                         csum_insertion);
        } else {
+               is_jumbo = priv->hw->chain->is_jumbo_frm(skb->len,
+                                                        priv->plat->enh_desc);
+               if (unlikely(is_jumbo))
+                       entry = priv->hw->chain->jumbo_frm(priv, skb,
+                                                          csum_insertion);
+       }
+       if (likely(!is_jumbo)) {
                desc->des2 = dma_map_single(priv->device, skb->data,
-                                       nopaged_len, DMA_TO_DEVICE);
+                                           nopaged_len, DMA_TO_DEVICE);
+               priv->tx_skbuff_dma[entry] = desc->des2;
                priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len,
-                                               csum_insertion);
-       }
+                                               csum_insertion, priv->mode);
+       } else
+               desc = first;
 
        for (i = 0; i < nfrags; i++) {
                const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
                int len = skb_frag_size(frag);
 
                entry = (++priv->cur_tx) % txsize;
-               desc = priv->dma_tx + entry;
+               if (priv->extend_desc)
+                       desc = (struct dma_desc *)(priv->dma_etx + entry);
+               else
+                       desc = priv->dma_tx + entry;
 
                TX_DBG("\t[entry %d] segment len: %d\n", entry, len);
                desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len,
                                              DMA_TO_DEVICE);
+               priv->tx_skbuff_dma[entry] = desc->des2;
                priv->tx_skbuff[entry] = NULL;
-               priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion);
+               priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion,
+                                               priv->mode);
                wmb();
                priv->hw->desc->set_tx_owner(desc);
                wmb();
@@ -1298,11 +1899,14 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
 #ifdef STMMAC_XMIT_DEBUG
        if (netif_msg_pktdata(priv)) {
-               pr_info("stmmac xmit: current=%d, dirty=%d, entry=%d, "
-                      "first=%p, nfrags=%d\n",
-                      (priv->cur_tx % txsize), (priv->dirty_tx % txsize),
-                      entry, first, nfrags);
-               display_ring(priv->dma_tx, txsize);
+               pr_info("%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d"
+                       __func__, (priv->cur_tx % txsize),
+                       (priv->dirty_tx % txsize), entry, first, nfrags);
+               if (priv->extend_desc)
+                       stmmac_display_ring((void *)priv->dma_etx, txsize, 1);
+               else
+                       stmmac_display_ring((void *)priv->dma_tx, txsize, 0);
+
                pr_info(">>> frame to be transmitted: ");
                print_pkt(skb->data, skb->len);
        }
@@ -1314,7 +1918,15 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
        dev->stats.tx_bytes += skb->len;
 
-       skb_tx_timestamp(skb);
+       if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+                    priv->hwts_tx_en)) {
+               /* declare that device is doing timestamping */
+               skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+               priv->hw->desc->enable_tx_timestamp(first);
+       }
+
+       if (!priv->hwts_tx_en)
+               skb_tx_timestamp(skb);
 
        priv->hw->dma->enable_dma_transmission(priv->ioaddr);
 
@@ -1323,14 +1935,26 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
        return NETDEV_TX_OK;
 }
 
+/**
+ * stmmac_rx_refill: refill used skb preallocated buffers
+ * @priv: driver private structure
+ * Description : this is to reallocate the skb for the reception process
+ * that is based on zero-copy.
+ */
 static inline void stmmac_rx_refill(struct stmmac_priv *priv)
 {
        unsigned int rxsize = priv->dma_rx_size;
        int bfsize = priv->dma_buf_sz;
-       struct dma_desc *p = priv->dma_rx;
 
        for (; priv->cur_rx - priv->dirty_rx > 0; priv->dirty_rx++) {
                unsigned int entry = priv->dirty_rx % rxsize;
+               struct dma_desc *p;
+
+               if (priv->extend_desc)
+                       p = (struct dma_desc *)(priv->dma_erx + entry);
+               else
+                       p = priv->dma_rx + entry;
+
                if (likely(priv->rx_skbuff[entry] == NULL)) {
                        struct sk_buff *skb;
 
@@ -1344,80 +1968,116 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv)
                            dma_map_single(priv->device, skb->data, bfsize,
                                           DMA_FROM_DEVICE);
 
-                       (p + entry)->des2 = priv->rx_skbuff_dma[entry];
+                       p->des2 = priv->rx_skbuff_dma[entry];
 
-                       if (unlikely(priv->plat->has_gmac))
-                               priv->hw->ring->refill_desc3(bfsize, p + entry);
+                       priv->hw->ring->refill_desc3(priv, p);
 
                        RX_DBG(KERN_INFO "\trefill entry #%d\n", entry);
                }
                wmb();
-               priv->hw->desc->set_rx_owner(p + entry);
+               priv->hw->desc->set_rx_owner(p);
                wmb();
        }
 }
 
+/**
+ * stmmac_rx_refill: refill used skb preallocated buffers
+ * @priv: driver private structure
+ * @limit: napi bugget.
+ * Description :  this the function called by the napi poll method.
+ * It gets all the frames inside the ring.
+ */
 static int stmmac_rx(struct stmmac_priv *priv, int limit)
 {
        unsigned int rxsize = priv->dma_rx_size;
        unsigned int entry = priv->cur_rx % rxsize;
        unsigned int next_entry;
        unsigned int count = 0;
-       struct dma_desc *p = priv->dma_rx + entry;
-       struct dma_desc *p_next;
+       int coe = priv->plat->rx_coe;
 
 #ifdef STMMAC_RX_DEBUG
        if (netif_msg_hw(priv)) {
                pr_debug(">>> stmmac_rx: descriptor ring:\n");
-               display_ring(priv->dma_rx, rxsize);
+               if (priv->extend_desc)
+                       stmmac_display_ring((void *)priv->dma_erx, rxsize, 1);
+               else
+                       stmmac_display_ring((void *)priv->dma_rx, rxsize, 0);
        }
 #endif
-       while (!priv->hw->desc->get_rx_owner(p)) {
+       while (count < limit) {
                int status;
+               struct dma_desc *p;
 
-               if (count >= limit)
+               if (priv->extend_desc)
+                       p = (struct dma_desc *)(priv->dma_erx + entry);
+               else
+                       p = priv->dma_rx + entry;
+
+               if (priv->hw->desc->get_rx_owner(p))
                        break;
 
                count++;
 
                next_entry = (++priv->cur_rx) % rxsize;
-               p_next = priv->dma_rx + next_entry;
-               prefetch(p_next);
+               if (priv->extend_desc)
+                       prefetch(priv->dma_erx + next_entry);
+               else
+                       prefetch(priv->dma_rx + next_entry);
 
                /* read the status of the incoming frame */
-               status = (priv->hw->desc->rx_status(&priv->dev->stats,
-                                                   &priv->xstats, p));
-               if (unlikely(status == discard_frame))
+               status = priv->hw->desc->rx_status(&priv->dev->stats,
+                                                  &priv->xstats, p);
+               if ((priv->extend_desc) && (priv->hw->desc->rx_extended_status))
+                       priv->hw->desc->rx_extended_status(&priv->dev->stats,
+                                                          &priv->xstats,
+                                                          priv->dma_erx +
+                                                          entry);
+               if (unlikely(status == discard_frame)) {
                        priv->dev->stats.rx_errors++;
-               else {
+                       if (priv->hwts_rx_en && !priv->extend_desc) {
+                               /* DESC2 & DESC3 will be overwitten by device
+                                * with timestamp value, hence reinitialize
+                                * them in stmmac_rx_refill() function so that
+                                * device can reuse it.
+                                */
+                               priv->rx_skbuff[entry] = NULL;
+                               dma_unmap_single(priv->device,
+                                                priv->rx_skbuff_dma[entry],
+                                                priv->dma_buf_sz,
+                                                DMA_FROM_DEVICE);
+                       }
+               } else {
                        struct sk_buff *skb;
                        int frame_len;
 
-                       frame_len = priv->hw->desc->get_rx_frame_len(p,
-                                       priv->plat->rx_coe);
+                       frame_len = priv->hw->desc->get_rx_frame_len(p, coe);
+
                        /* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3
-                        * Type frames (LLC/LLC-SNAP) */
+                        * Type frames (LLC/LLC-SNAP)
+                        */
                        if (unlikely(status != llc_snap))
                                frame_len -= ETH_FCS_LEN;
 #ifdef STMMAC_RX_DEBUG
                        if (frame_len > ETH_FRAME_LEN)
                                pr_debug("\tRX frame size %d, COE status: %d\n",
-                                       frame_len, status);
+                                        frame_len, status);
 
                        if (netif_msg_hw(priv))
                                pr_debug("\tdesc: %p [entry %d] buff=0x%x\n",
-                                       p, entry, p->des2);
+                                        p, entry, p->des2);
 #endif
                        skb = priv->rx_skbuff[entry];
                        if (unlikely(!skb)) {
                                pr_err("%s: Inconsistent Rx descriptor chain\n",
-                                       priv->dev->name);
+                                      priv->dev->name);
                                priv->dev->stats.rx_dropped++;
                                break;
                        }
                        prefetch(skb->data - NET_IP_ALIGN);
                        priv->rx_skbuff[entry] = NULL;
 
+                       stmmac_get_rx_hwtstamp(priv, entry, skb);
+
                        skb_put(skb, frame_len);
                        dma_unmap_single(priv->device,
                                         priv->rx_skbuff_dma[entry],
@@ -1430,7 +2090,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
 #endif
                        skb->protocol = eth_type_trans(skb, priv->dev);
 
-                       if (unlikely(!priv->plat->rx_coe))
+                       if (unlikely(!coe))
                                skb_checksum_none_assert(skb);
                        else
                                skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -1441,7 +2101,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
                        priv->dev->stats.rx_bytes += frame_len;
                }
                entry = next_entry;
-               p = p_next;     /* use prefetched values */
        }
 
        stmmac_rx_refill(priv);
@@ -1499,18 +2158,16 @@ static int stmmac_config(struct net_device *dev, struct ifmap *map)
 
        /* Don't allow changing the I/O address */
        if (map->base_addr != dev->base_addr) {
-               pr_warning("%s: can't change I/O address\n", dev->name);
+               pr_warn("%s: can't change I/O address\n", dev->name);
                return -EOPNOTSUPP;
        }
 
        /* Don't allow changing the IRQ */
        if (map->irq != dev->irq) {
-               pr_warning("%s: can't change IRQ number %d\n",
-                      dev->name, dev->irq);
+               pr_warn("%s: not change IRQ number %d\n", dev->name, dev->irq);
                return -EOPNOTSUPP;
        }
 
-       /* ignore other fields */
        return 0;
 }
 
@@ -1570,7 +2227,7 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
 }
 
 static netdev_features_t stmmac_fix_features(struct net_device *dev,
-       netdev_features_t features)
+                                            netdev_features_t features)
 {
        struct stmmac_priv *priv = netdev_priv(dev);
 
@@ -1584,13 +2241,22 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
        /* Some GMAC devices have a bugged Jumbo frame support that
         * needs to have the Tx COE disabled for oversized frames
         * (due to limited buffer sizes). In this case we disable
-        * the TX csum insertionin the TDES and not use SF. */
+        * 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;
 
        return features;
 }
 
+/**
+ *  stmmac_interrupt - main ISR
+ *  @irq: interrupt number.
+ *  @dev_id: to pass the net device pointer.
+ *  Description: this is the main driver interrupt service routine.
+ *  It calls the DMA ISR and also the core ISR to manage PMT, MMC, LPI
+ *  interrupts.
+ */
 static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
 {
        struct net_device *dev = (struct net_device *)dev_id;
@@ -1604,30 +2270,14 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
        /* To handle GMAC own interrupts */
        if (priv->plat->has_gmac) {
                int status = priv->hw->mac->host_irq_status((void __iomem *)
-                                                           dev->base_addr);
+                                                           dev->base_addr,
+                                                           &priv->xstats);
                if (unlikely(status)) {
-                       if (status & core_mmc_tx_irq)
-                               priv->xstats.mmc_tx_irq_n++;
-                       if (status & core_mmc_rx_irq)
-                               priv->xstats.mmc_rx_irq_n++;
-                       if (status & core_mmc_rx_csum_offload_irq)
-                               priv->xstats.mmc_rx_csum_offload_irq_n++;
-                       if (status & core_irq_receive_pmt_irq)
-                               priv->xstats.irq_receive_pmt_irq_n++;
-
                        /* For LPI we need to save the tx status */
-                       if (status & core_irq_tx_path_in_lpi_mode) {
-                               priv->xstats.irq_tx_path_in_lpi_mode_n++;
+                       if (status & CORE_IRQ_TX_PATH_IN_LPI_MODE)
                                priv->tx_path_in_lpi_mode = true;
-                       }
-                       if (status & core_irq_tx_path_exit_lpi_mode) {
-                               priv->xstats.irq_tx_path_exit_lpi_mode_n++;
+                       if (status & CORE_IRQ_TX_PATH_EXIT_LPI_MODE)
                                priv->tx_path_in_lpi_mode = false;
-                       }
-                       if (status & core_irq_rx_path_in_lpi_mode)
-                               priv->xstats.irq_rx_path_in_lpi_mode_n++;
-                       if (status & core_irq_rx_path_exit_lpi_mode)
-                               priv->xstats.irq_rx_path_exit_lpi_mode_n++;
                }
        }
 
@@ -1639,7 +2289,8 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
 /* Polling receive - used by NETCONSOLE and other diagnostic tools
- * to allow network I/O with interrupts disabled. */
+ * to allow network I/O with interrupts disabled.
+ */
 static void stmmac_poll_controller(struct net_device *dev)
 {
        disable_irq(dev->irq);
@@ -1655,21 +2306,30 @@ static void stmmac_poll_controller(struct net_device *dev)
  *  a proprietary structure used to pass information to the driver.
  *  @cmd: IOCTL command
  *  Description:
- *  Currently there are no special functionality supported in IOCTL, just the
- *  phy_mii_ioctl(...) can be invoked.
+ *  Currently it supports the phy_mii_ioctl(...) and HW time stamping.
  */
 static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        struct stmmac_priv *priv = netdev_priv(dev);
-       int ret;
+       int ret = -EOPNOTSUPP;
 
        if (!netif_running(dev))
                return -EINVAL;
 
-       if (!priv->phydev)
-               return -EINVAL;
-
-       ret = phy_mii_ioctl(priv->phydev, rq, cmd);
+       switch (cmd) {
+       case SIOCGMIIPHY:
+       case SIOCGMIIREG:
+       case SIOCSMIIREG:
+               if (!priv->phydev)
+                       return -EINVAL;
+               ret = phy_mii_ioctl(priv->phydev, rq, cmd);
+               break;
+       case SIOCSHWTSTAMP:
+               ret = stmmac_hwtstamp_ioctl(dev, rq);
+               break;
+       default:
+               break;
+       }
 
        return ret;
 }
@@ -1679,40 +2339,51 @@ static struct dentry *stmmac_fs_dir;
 static struct dentry *stmmac_rings_status;
 static struct dentry *stmmac_dma_cap;
 
-static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
+static void sysfs_display_ring(void *head, int size, int extend_desc,
+                              struct seq_file *seq)
 {
-       struct tmp_s {
-               u64 a;
-               unsigned int b;
-               unsigned int c;
-       };
        int i;
-       struct net_device *dev = seq->private;
-       struct stmmac_priv *priv = netdev_priv(dev);
+       struct dma_extended_desc *ep = (struct dma_extended_desc *)head;
+       struct dma_desc *p = (struct dma_desc *)head;
 
-       seq_printf(seq, "=======================\n");
-       seq_printf(seq, " RX descriptor ring\n");
-       seq_printf(seq, "=======================\n");
-
-       for (i = 0; i < priv->dma_rx_size; i++) {
-               struct tmp_s *x = (struct tmp_s *)(priv->dma_rx + i);
-               seq_printf(seq, "[%d] DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x",
-                          i, (unsigned int)(x->a),
-                          (unsigned int)((x->a) >> 32), x->b, x->c);
+       for (i = 0; i < size; i++) {
+               u64 x;
+               if (extend_desc) {
+                       x = *(u64 *) ep;
+                       seq_printf(seq, "%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n",
+                                  i, (unsigned int)virt_to_phys(ep),
+                                  (unsigned int)x, (unsigned int)(x >> 32),
+                                  ep->basic.des2, ep->basic.des3);
+                       ep++;
+               } else {
+                       x = *(u64 *) p;
+                       seq_printf(seq, "%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n",
+                                  i, (unsigned int)virt_to_phys(ep),
+                                  (unsigned int)x, (unsigned int)(x >> 32),
+                                  p->des2, p->des3);
+                       p++;
+               }
                seq_printf(seq, "\n");
        }
+}
 
-       seq_printf(seq, "\n");
-       seq_printf(seq, "=======================\n");
-       seq_printf(seq, "  TX descriptor ring\n");
-       seq_printf(seq, "=======================\n");
+static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
+{
+       struct net_device *dev = seq->private;
+       struct stmmac_priv *priv = netdev_priv(dev);
+       unsigned int txsize = priv->dma_tx_size;
+       unsigned int rxsize = priv->dma_rx_size;
 
-       for (i = 0; i < priv->dma_tx_size; i++) {
-               struct tmp_s *x = (struct tmp_s *)(priv->dma_tx + i);
-               seq_printf(seq, "[%d] DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x",
-                          i, (unsigned int)(x->a),
-                          (unsigned int)((x->a) >> 32), x->b, x->c);
-               seq_printf(seq, "\n");
+       if (priv->extend_desc) {
+               seq_printf(seq, "Extended RX descriptor ring:\n");
+               sysfs_display_ring((void *)priv->dma_erx, rxsize, 1, seq);
+               seq_printf(seq, "Extended TX descriptor ring:\n");
+               sysfs_display_ring((void *)priv->dma_etx, txsize, 1, seq);
+       } else {
+               seq_printf(seq, "RX descriptor ring:\n");
+               sysfs_display_ring((void *)priv->dma_rx, rxsize, 0, seq);
+               seq_printf(seq, "TX descriptor ring:\n");
+               sysfs_display_ring((void *)priv->dma_tx, txsize, 0, seq);
        }
 
        return 0;
@@ -1817,8 +2488,8 @@ static int stmmac_init_fs(struct net_device *dev)
 
        /* Entry to report DMA RX/TX rings */
        stmmac_rings_status = debugfs_create_file("descriptors_status",
-                                          S_IRUGO, stmmac_fs_dir, dev,
-                                          &stmmac_rings_status_fops);
+                                                 S_IRUGO, stmmac_fs_dir, dev,
+                                                 &stmmac_rings_status_fops);
 
        if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) {
                pr_info("ERROR creating stmmac ring debugfs file\n");
@@ -1868,7 +2539,7 @@ static const struct net_device_ops stmmac_netdev_ops = {
 
 /**
  *  stmmac_hw_init - Init the MAC device
- *  @priv : pointer to the private device structure.
+ *  @priv: driver private structure
  *  Description: this function detects which MAC device
  *  (GMAC/MAC10-100) has to attached, checks the HW capability
  *  (if supported) and sets the driver's features (for example
@@ -1877,7 +2548,7 @@ static const struct net_device_ops stmmac_netdev_ops = {
  */
 static int stmmac_hw_init(struct stmmac_priv *priv)
 {
-       int ret = 0;
+       int ret;
        struct mac_device_info *mac;
 
        /* Identify the MAC HW device */
@@ -1892,12 +2563,23 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
 
        priv->hw = mac;
 
-       /* To use the chained or ring mode */
-       priv->hw->ring = &ring_mode_ops;
-
        /* Get and dump the chip ID */
        priv->synopsys_id = stmmac_get_synopsys_id(priv);
 
+       /* To use alternate (extended) or normal descriptor structures */
+       stmmac_selec_desc_mode(priv);
+
+       /* To use the chained or ring mode */
+       if (chain_mode) {
+               priv->hw->chain = &chain_mode_ops;
+               pr_info(" Chain mode enabled\n");
+               priv->mode = STMMAC_CHAIN_MODE;
+       } else {
+               priv->hw->ring = &ring_mode_ops;
+               pr_info(" Ring mode enabled\n");
+               priv->mode = STMMAC_RING_MODE;
+       }
+
        /* Get the HW capability (new GMAC newer than 3.50a) */
        priv->hw_cap_support = stmmac_get_hw_features(priv);
        if (priv->hw_cap_support) {
@@ -1921,14 +2603,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
        } else
                pr_info(" No HW DMA feature register supported");
 
-       /* Select the enhnaced/normal descriptor structures */
-       stmmac_selec_desc_mode(priv);
-
-       /* Enable the IPC (Checksum Offload) and check if the feature has been
-        * enabled during the core configuration. */
        ret = priv->hw->mac->rx_ipc(priv->ioaddr);
        if (!ret) {
-               pr_warning(" RX IPC Checksum Offload not configured.\n");
+               pr_warn(" RX IPC Checksum Offload not configured.\n");
                priv->plat->rx_coe = STMMAC_RX_COE_NONE;
        }
 
@@ -1943,7 +2620,7 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
                device_set_wakeup_capable(priv->device, 1);
        }
 
-       return ret;
+       return 0;
 }
 
 /**
@@ -1984,12 +2661,15 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
        stmmac_verify_args();
 
        /* Override with kernel parameters if supplied XXX CRS XXX
-        * this needs to have multiple instances */
+        * this needs to have multiple instances
+        */
        if ((phyaddr >= 0) && (phyaddr <= 31))
                priv->plat->phy_addr = phyaddr;
 
        /* Init MAC and get the capabilities */
-       stmmac_hw_init(priv);
+       ret = stmmac_hw_init(priv);
+       if (ret)
+               goto error_free_netdev;
 
        ndev->netdev_ops = &stmmac_netdev_ops;
 
@@ -1999,7 +2679,7 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
        ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
 #ifdef STMMAC_VLAN_TAG_USED
        /* Both mac100 and gmac support receive VLAN tag detection */
-       ndev->features |= NETIF_F_HW_VLAN_RX;
+       ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
 #endif
        priv->msg_enable = netif_msg_init(debug, default_msg_level);
 
@@ -2029,7 +2709,7 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
 
        priv->stmmac_clk = clk_get(priv->device, STMMAC_RESOURCE_NAME);
        if (IS_ERR(priv->stmmac_clk)) {
-               pr_warning("%s: warning: cannot get CSR clock\n", __func__);
+               pr_warn("%s: warning: cannot get CSR clock\n", __func__);
                goto error_clk_get;
        }
 
@@ -2044,12 +2724,17 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
        else
                priv->clk_csr = priv->plat->clk_csr;
 
-       /* MDIO bus Registration */
-       ret = stmmac_mdio_register(ndev);
-       if (ret < 0) {
-               pr_debug("%s: MDIO bus (id: %d) registration failed",
-                        __func__, priv->plat->bus_id);
-               goto error_mdio_register;
+       stmmac_check_pcs_mode(priv);
+
+       if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
+           priv->pcs != STMMAC_PCS_RTBI) {
+               /* MDIO bus Registration */
+               ret = stmmac_mdio_register(ndev);
+               if (ret < 0) {
+                       pr_debug("%s: MDIO bus (id: %d) registration failed",
+                                __func__, priv->plat->bus_id);
+                       goto error_mdio_register;
+               }
        }
 
        return priv;
@@ -2060,6 +2745,7 @@ error_clk_get:
        unregister_netdev(ndev);
 error_netdev_register:
        netif_napi_del(&priv->napi);
+error_free_netdev:
        free_netdev(ndev);
 
        return NULL;
@@ -2081,7 +2767,9 @@ int stmmac_dvr_remove(struct net_device *ndev)
        priv->hw->dma->stop_tx(priv->ioaddr);
 
        stmmac_set_mac(priv->ioaddr, false);
-       stmmac_mdio_unregister(ndev);
+       if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
+           priv->pcs != STMMAC_PCS_RTBI)
+               stmmac_mdio_unregister(ndev);
        netif_carrier_off(ndev);
        unregister_netdev(ndev);
        free_netdev(ndev);
@@ -2093,7 +2781,6 @@ int stmmac_dvr_remove(struct net_device *ndev)
 int stmmac_suspend(struct net_device *ndev)
 {
        struct stmmac_priv *priv = netdev_priv(ndev);
-       int dis_ic = 0;
        unsigned long flags;
 
        if (!ndev || !netif_running(ndev))
@@ -2107,18 +2794,13 @@ int stmmac_suspend(struct net_device *ndev)
        netif_device_detach(ndev);
        netif_stop_queue(ndev);
 
-       if (priv->use_riwt)
-               dis_ic = 1;
-
        napi_disable(&priv->napi);
 
        /* Stop TX/RX DMA */
        priv->hw->dma->stop_tx(priv->ioaddr);
        priv->hw->dma->stop_rx(priv->ioaddr);
-       /* Clear the Rx/Tx descriptors */
-       priv->hw->desc->init_rx_desc(priv->dma_rx, priv->dma_rx_size,
-                                    dis_ic);
-       priv->hw->desc->init_tx_desc(priv->dma_tx, priv->dma_tx_size);
+
+       stmmac_clear_descriptors(priv);
 
        /* Enable Power down mode by programming the PMT regs */
        if (device_may_wakeup(priv->device))
@@ -2146,7 +2828,8 @@ int stmmac_resume(struct net_device *ndev)
         * automatically as soon as a magic packet or a Wake-up frame
         * is received. Anyway, it's better to manually clear
         * this bit because it can generate problems while resuming
-        * from another devices (e.g. serial console). */
+        * from another devices (e.g. serial console).
+        */
        if (device_may_wakeup(priv->device))
                priv->hw->mac->pmt(priv->ioaddr, 0);
        else
@@ -2257,6 +2940,9 @@ static int __init stmmac_cmdline_opt(char *str)
                } else if (!strncmp(opt, "eee_timer:", 10)) {
                        if (kstrtoint(opt + 10, 0, &eee_timer))
                                goto err;
+               } else if (!strncmp(opt, "chain_mode:", 11)) {
+                       if (kstrtoint(opt + 11, 0, &chain_mode))
+                               goto err;
                }
        }
        return 0;
@@ -2267,7 +2953,7 @@ err:
 }
 
 __setup("stmmaceth=", stmmac_cmdline_opt);
-#endif
+#endif /* MODULE */
 
 MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver");
 MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
index 0b9829fe3eea7344886060d826730aa13e6eba3e..cc15039eaa4739a33e916b5f9506ce0b1c0f4ccc 100644 (file)
@@ -177,7 +177,7 @@ int stmmac_mdio_register(struct net_device *ndev)
        new_bus->write = &stmmac_mdio_write;
        new_bus->reset = &stmmac_mdio_reset;
        snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x",
-               new_bus->name, priv->plat->bus_id);
+                new_bus->name, priv->plat->bus_id);
        new_bus->priv = ndev;
        new_bus->irq = irqlist;
        new_bus->phy_mask = mdio_bus_data->phy_mask;
index 19b3a2567a469f0d27c94349ff117bbae79b5567..023b7c29cb2fc3408ff3e3a00c03b9656ddd2ab4 100644 (file)
@@ -88,7 +88,7 @@ static int stmmac_pci_probe(struct pci_dev *pdev,
                        continue;
                addr = pci_iomap(pdev, i, 0);
                if (addr == NULL) {
-                       pr_err("%s: ERROR: cannot map register memory, aborting",
+                       pr_err("%s: ERROR: cannot map register memory aborting",
                               __func__);
                        ret = -EIO;
                        goto err_out_map_failed;
index b43d68b40e50f6156ea10ccfad3f0924d04d1b01..1d3780f55ba2c9397641b1b5986b952680259e0c 100644 (file)
@@ -88,11 +88,9 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
        if (!res)
                return -ENODEV;
 
-       addr = devm_request_and_ioremap(dev, res);
-       if (!addr) {
-               pr_err("%s: ERROR: memory mapping failed", __func__);
-               return -ENOMEM;
-       }
+       addr = devm_ioremap_resource(dev, res);
+       if (IS_ERR(addr))
+               return PTR_ERR(addr);
 
        if (pdev->dev.of_node) {
                plat_dat = devm_kzalloc(&pdev->dev,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
new file mode 100644 (file)
index 0000000..b8b0eee
--- /dev/null
@@ -0,0 +1,211 @@
+/*******************************************************************************
+  PTP 1588 clock using the STMMAC.
+
+  Copyright (C) 2013  Vayavya Labs Pvt Ltd
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
+*******************************************************************************/
+#include "stmmac.h"
+#include "stmmac_ptp.h"
+
+/**
+ * stmmac_adjust_freq
+ *
+ * @ptp: pointer to ptp_clock_info structure
+ * @ppb: desired period change in parts ber billion
+ *
+ * Description: this function will adjust the frequency of hardware clock.
+ */
+static int stmmac_adjust_freq(struct ptp_clock_info *ptp, s32 ppb)
+{
+       struct stmmac_priv *priv =
+           container_of(ptp, struct stmmac_priv, ptp_clock_ops);
+       unsigned long flags;
+       u32 diff, addend;
+       int neg_adj = 0;
+       u64 adj;
+
+       if (ppb < 0) {
+               neg_adj = 1;
+               ppb = -ppb;
+       }
+
+       addend = priv->default_addend;
+       adj = addend;
+       adj *= ppb;
+       diff = div_u64(adj, 1000000000ULL);
+       addend = neg_adj ? (addend - diff) : (addend + diff);
+
+       spin_lock_irqsave(&priv->ptp_lock, flags);
+
+       priv->hw->ptp->config_addend(priv->ioaddr, addend);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
+/**
+ * stmmac_adjust_time
+ *
+ * @ptp: pointer to ptp_clock_info structure
+ * @delta: desired change in nanoseconds
+ *
+ * Description: this function will shift/adjust the hardware clock time.
+ */
+static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
+{
+       struct stmmac_priv *priv =
+           container_of(ptp, struct stmmac_priv, ptp_clock_ops);
+       unsigned long flags;
+       u32 sec, nsec;
+       u32 quotient, reminder;
+       int neg_adj = 0;
+
+       if (delta < 0) {
+               neg_adj = 1;
+               delta = -delta;
+       }
+
+       quotient = div_u64_rem(delta, 1000000000ULL, &reminder);
+       sec = quotient;
+       nsec = reminder;
+
+       spin_lock_irqsave(&priv->ptp_lock, flags);
+
+       priv->hw->ptp->adjust_systime(priv->ioaddr, sec, nsec, neg_adj);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
+/**
+ * stmmac_get_time
+ *
+ * @ptp: pointer to ptp_clock_info structure
+ * @ts: pointer to hold time/result
+ *
+ * Description: this function will read the current time from the
+ * hardware clock and store it in @ts.
+ */
+static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+       struct stmmac_priv *priv =
+           container_of(ptp, struct stmmac_priv, ptp_clock_ops);
+       unsigned long flags;
+       u64 ns;
+       u32 reminder;
+
+       spin_lock_irqsave(&priv->ptp_lock, flags);
+
+       ns = priv->hw->ptp->get_systime(priv->ioaddr);
+
+       spin_unlock_irqrestore(&priv->ptp_lock, flags);
+
+       ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &reminder);
+       ts->tv_nsec = reminder;
+
+       return 0;
+}
+
+/**
+ * stmmac_set_time
+ *
+ * @ptp: pointer to ptp_clock_info structure
+ * @ts: time value to set
+ *
+ * Description: this function will set the current time on the
+ * hardware clock.
+ */
+static int stmmac_set_time(struct ptp_clock_info *ptp,
+                          const struct timespec *ts)
+{
+       struct stmmac_priv *priv =
+           container_of(ptp, struct stmmac_priv, ptp_clock_ops);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->ptp_lock, flags);
+
+       priv->hw->ptp->init_systime(priv->ioaddr, ts->tv_sec, ts->tv_nsec);
+
+       spin_unlock_irqrestore(&priv->ptp_lock, flags);
+
+       return 0;
+}
+
+static int stmmac_enable(struct ptp_clock_info *ptp,
+                        struct ptp_clock_request *rq, int on)
+{
+       return -EOPNOTSUPP;
+}
+
+/* structure describing a PTP hardware clock */
+static struct ptp_clock_info stmmac_ptp_clock_ops = {
+       .owner = THIS_MODULE,
+       .name = "stmmac_ptp_clock",
+       .max_adj = 62500000,
+       .n_alarm = 0,
+       .n_ext_ts = 0,
+       .n_per_out = 0,
+       .pps = 0,
+       .adjfreq = stmmac_adjust_freq,
+       .adjtime = stmmac_adjust_time,
+       .gettime = stmmac_get_time,
+       .settime = stmmac_set_time,
+       .enable = stmmac_enable,
+};
+
+/**
+ * stmmac_ptp_register
+ * @priv: driver private structure
+ * Description: this function will register the ptp clock driver
+ * to kernel. It also does some house keeping work.
+ */
+int stmmac_ptp_register(struct stmmac_priv *priv)
+{
+       spin_lock_init(&priv->ptp_lock);
+       priv->ptp_clock_ops = stmmac_ptp_clock_ops;
+
+       priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops,
+                                            priv->device);
+       if (IS_ERR(priv->ptp_clock)) {
+               priv->ptp_clock = NULL;
+               pr_err("ptp_clock_register() failed on %s\n", priv->dev->name);
+       } else
+               pr_debug("Added PTP HW clock successfully on %s\n",
+                        priv->dev->name);
+
+       return 0;
+}
+
+/**
+ * stmmac_ptp_unregister
+ * @priv: driver private structure
+ * Description: this function will remove/unregister the ptp clock driver
+ * from the kernel.
+ */
+void stmmac_ptp_unregister(struct stmmac_priv *priv)
+{
+       if (priv->ptp_clock) {
+               ptp_clock_unregister(priv->ptp_clock);
+               pr_debug("Removed PTP HW clock successfully on %s\n",
+                        priv->dev->name);
+       }
+}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
new file mode 100644 (file)
index 0000000..3dbc047
--- /dev/null
@@ -0,0 +1,74 @@
+/******************************************************************************
+  PTP Header file
+
+  Copyright (C) 2013  Vayavya Labs Pvt Ltd
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
+******************************************************************************/
+
+#ifndef __STMMAC_PTP_H__
+#define __STMMAC_PTP_H__
+
+#define STMMAC_SYSCLOCK 62500000
+
+/* IEEE 1588 PTP register offsets */
+#define PTP_TCR                0x0700  /* Timestamp Control Reg */
+#define PTP_SSIR       0x0704  /* Sub-Second Increment Reg */
+#define PTP_STSR       0x0708  /* System Time â€“ Seconds Regr */
+#define PTP_STNSR      0x070C  /* System Time â€“ Nanoseconds Reg */
+#define PTP_STSUR      0x0710  /* System Time â€“ Seconds Update Reg */
+#define PTP_STNSUR     0x0714  /* System Time â€“ Nanoseconds Update Reg */
+#define PTP_TAR                0x0718  /* Timestamp Addend Reg */
+#define PTP_TTSR       0x071C  /* Target Time Seconds Reg */
+#define PTP_TTNSR      0x0720  /* Target Time Nanoseconds Reg */
+#define        PTP_STHWSR      0x0724  /* System Time - Higher Word Seconds Reg */
+#define PTP_TSR                0x0728  /* Timestamp Status */
+
+#define PTP_STNSUR_ADDSUB_SHIFT 31
+
+/* PTP TCR defines */
+#define PTP_TCR_TSENA          0x00000001 /* Timestamp Enable */
+#define PTP_TCR_TSCFUPDT       0x00000002 /* Timestamp Fine/Coarse Update */
+#define PTP_TCR_TSINIT         0x00000004 /* Timestamp Initialize */
+#define PTP_TCR_TSUPDT         0x00000008 /* Timestamp Update */
+/* Timestamp Interrupt Trigger Enable */
+#define PTP_TCR_TSTRIG         0x00000010
+#define PTP_TCR_TSADDREG       0x00000020 /* Addend Reg Update */
+#define PTP_TCR_TSENALL                0x00000100 /* Enable Timestamp for All Frames */
+/* Timestamp Digital or Binary Rollover Control */
+#define PTP_TCR_TSCTRLSSR      0x00000200
+
+/* Enable PTP packet Processing for Version 2 Format */
+#define PTP_TCR_TSVER2ENA      0x00000400
+/* Enable Processing of PTP over Ethernet Frames */
+#define PTP_TCR_TSIPENA                0x00000800
+/* Enable Processing of PTP Frames Sent over IPv6-UDP */
+#define PTP_TCR_TSIPV6ENA      0x00001000
+/* Enable Processing of PTP Frames Sent over IPv4-UDP */
+#define PTP_TCR_TSIPV4ENA      0x00002000
+/* Enable Timestamp Snapshot for Event Messages */
+#define PTP_TCR_TSEVNTENA      0x00004000
+/* Enable Snapshot for Messages Relevant to Master */
+#define PTP_TCR_TSMSTRENA      0x00008000
+/* Select PTP packets for Taking Snapshots */
+#define PTP_TCR_SNAPTYPSEL_1   0x00010000
+/* Enable MAC address for PTP Frame Filtering */
+#define PTP_TCR_TSENMACADDR    0x00040000
+
+#endif /* __STMMAC_PTP_H__ */
index e4c1c88e4c2af4b8faf8f0db5f964baa48167647..95cff98d8a34e2a4ba355b03554562bb76d23152 100644 (file)
@@ -6618,7 +6618,7 @@ static u64 niu_compute_tx_flags(struct sk_buff *skb, struct ethhdr *ehdr,
               (len << TXHDR_LEN_SHIFT) |
               ((l3off / 2) << TXHDR_L3START_SHIFT) |
               (ihl << TXHDR_IHL_SHIFT) |
-              ((eth_proto_inner < 1536) ? TXHDR_LLC : 0) |
+              ((eth_proto_inner < ETH_P_802_3_MIN) ? TXHDR_LLC : 0) |
               ((eth_proto == ETH_P_8021Q) ? TXHDR_VLAN : 0) |
               (ipv6 ? TXHDR_IP_VER : 0) |
               csum_bits);
index 5fafca0653051d3960fc5240341ad0271ffeb832..054975939a184cc48035009420e8d1663871b0f9 100644 (file)
@@ -1169,10 +1169,8 @@ static int bigmac_ether_init(struct platform_device *op,
        bp->bmac_block = dma_alloc_coherent(&bp->bigmac_op->dev,
                                            PAGE_SIZE,
                                            &bp->bblock_dvma, GFP_ATOMIC);
-       if (bp->bmac_block == NULL || bp->bblock_dvma == 0) {
-               printk(KERN_ERR "BIGMAC: Cannot allocate consistent DMA.\n");
+       if (bp->bmac_block == NULL || bp->bblock_dvma == 0)
                goto fail_and_cleanup;
-       }
 
        /* Get the board revision of this BigMAC. */
        bp->board_rev = of_getintprop_default(bp->bigmac_op->dev.of_node,
index a1bff49a815549e862bcaf0d938deb24558d0e54..436fa9d5a07190a555adc6a6b7119700845b2b73 100644 (file)
@@ -2752,10 +2752,8 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe)
                                             &hp->hblock_dvma,
                                             GFP_ATOMIC);
        err = -ENOMEM;
-       if (!hp->happy_block) {
-               printk(KERN_ERR "happymeal: Cannot allocate descriptors.\n");
+       if (!hp->happy_block)
                goto err_out_iounmap;
-       }
 
        /* Force check of the link first time we are brought up. */
        hp->linkcheck = 0;
@@ -3068,14 +3066,11 @@ static int happy_meal_pci_probe(struct pci_dev *pdev,
        hp->happy_bursts = DMA_BURSTBITS;
 #endif
 
-       hp->happy_block = (struct hmeal_init_block *)
-               dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &hp->hblock_dvma, GFP_KERNEL);
-
+       hp->happy_block = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+                                            &hp->hblock_dvma, GFP_KERNEL);
        err = -ENODEV;
-       if (!hp->happy_block) {
-               printk(KERN_ERR "happymeal(PCI): Cannot get hme init block.\n");
+       if (!hp->happy_block)
                goto err_out_iounmap;
-       }
 
        hp->linkcheck = 0;
        hp->timer_state = asleep;
index 49bf3e2eb652316757e2781ef7e703171c868414..8182591bc1876c77ddae935d6da64d9200a2dbcc 100644 (file)
@@ -414,7 +414,7 @@ static void qe_rx(struct sunqe *qep)
        struct qe_rxd *this;
        struct sunqe_buffers *qbufs = qep->buffers;
        __u32 qbufs_dvma = qep->buffers_dvma;
-       int elem = qep->rx_new, drops = 0;
+       int elem = qep->rx_new;
        u32 flags;
 
        this = &rxbase[elem];
@@ -436,7 +436,6 @@ static void qe_rx(struct sunqe *qep)
                } else {
                        skb = netdev_alloc_skb(dev, len + 2);
                        if (skb == NULL) {
-                               drops++;
                                dev->stats.rx_dropped++;
                        } else {
                                skb_reserve(skb, 2);
@@ -456,8 +455,6 @@ static void qe_rx(struct sunqe *qep)
                this = &rxbase[elem];
        }
        qep->rx_new = elem;
-       if (drops)
-               printk(KERN_NOTICE "%s: Memory squeeze, deferring packet.\n", qep->dev->name);
 }
 
 static void qe_tx_reclaim(struct sunqe *qep);
index e15cc71b826de613ad6e304480306f903e9e6e2c..571452e786d5103460180e54481cd11ae88e4927 100644 (file)
@@ -733,7 +733,7 @@ static void __bdx_vlan_rx_vid(struct net_device *ndev, uint16_t vid, int enable)
  * @ndev: network device
  * @vid:  VLAN vid to add
  */
-static int bdx_vlan_rx_add_vid(struct net_device *ndev, uint16_t vid)
+static int bdx_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)
 {
        __bdx_vlan_rx_vid(ndev, vid, 1);
        return 0;
@@ -744,7 +744,7 @@ static int bdx_vlan_rx_add_vid(struct net_device *ndev, uint16_t vid)
  * @ndev: network device
  * @vid:  VLAN vid to kill
  */
-static int bdx_vlan_rx_kill_vid(struct net_device *ndev, unsigned short vid)
+static int bdx_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid)
 {
        __bdx_vlan_rx_vid(ndev, vid, 0);
        return 0;
@@ -1102,10 +1102,9 @@ static void bdx_rx_alloc_skbs(struct bdx_priv *priv, struct rxf_fifo *f)
        dno = bdx_rxdb_available(db) - 1;
        while (dno > 0) {
                skb = netdev_alloc_skb(priv->ndev, f->m.pktsz + NET_IP_ALIGN);
-               if (!skb) {
-                       pr_err("NO MEM: netdev_alloc_skb failed\n");
+               if (!skb)
                        break;
-               }
+
                skb_reserve(skb, NET_IP_ALIGN);
 
                idx = bdx_rxdb_alloc_elem(db);
@@ -1149,7 +1148,7 @@ NETIF_RX_MUX(struct bdx_priv *priv, u32 rxd_val1, u16 rxd_vlan,
                    priv->ndev->name,
                    GET_RXD_VLAN_ID(rxd_vlan),
                    GET_RXD_VTAG(rxd_val1));
-               __vlan_hwaccel_put_tag(skb, GET_RXD_VLAN_TCI(rxd_vlan));
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), GET_RXD_VLAN_TCI(rxd_vlan));
        }
        netif_receive_skb(skb);
 }
@@ -2018,12 +2017,12 @@ bdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                 * so we can have them same for all ports of the board */
                ndev->if_port = port;
                ndev->features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO
-                   | NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
-                   NETIF_F_HW_VLAN_FILTER | NETIF_F_RXCSUM
+                   | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+                   NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXCSUM
                    /*| NETIF_F_FRAGLIST */
                    ;
                ndev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
-                       NETIF_F_TSO | NETIF_F_HW_VLAN_TX;
+                       NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX;
 
                if (pci_using_dac)
                        ndev->features |= NETIF_F_HIGHDMA;
index 4781d3d8e18204ad7fc11d06d102fd2eb6829da4..25c364209a2179a48312ff0c3a2045681b1be58c 100644 (file)
@@ -126,6 +126,13 @@ do {                                                               \
 #define CPSW_FIFO_DUAL_MAC_MODE                (1 << 15)
 #define CPSW_FIFO_RATE_LIMIT_MODE      (2 << 15)
 
+#define CPSW_INTPACEEN         (0x3f << 16)
+#define CPSW_INTPRESCALE_MASK  (0x7FF << 0)
+#define CPSW_CMINTMAX_CNT      63
+#define CPSW_CMINTMIN_CNT      2
+#define CPSW_CMINTMAX_INTVL    (1000 / CPSW_CMINTMIN_CNT)
+#define CPSW_CMINTMIN_INTVL    ((1000 / CPSW_CMINTMAX_CNT) + 1)
+
 #define cpsw_enable_irq(priv)  \
        do {                    \
                u32 i;          \
@@ -139,6 +146,10 @@ do {                                                               \
                        disable_irq_nosync(priv->irqs_table[i]); \
        } while (0);
 
+#define cpsw_slave_index(priv)                         \
+               ((priv->data.dual_emac) ? priv->emac_port :     \
+               priv->data.active_slave)
+
 static int debug_level;
 module_param(debug_level, int, 0);
 MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)");
@@ -160,6 +171,15 @@ struct cpsw_wr_regs {
        u32     rx_en;
        u32     tx_en;
        u32     misc_en;
+       u32     mem_allign1[8];
+       u32     rx_thresh_stat;
+       u32     rx_stat;
+       u32     tx_stat;
+       u32     misc_stat;
+       u32     mem_allign2[8];
+       u32     rx_imax;
+       u32     tx_imax;
+
 };
 
 struct cpsw_ss_regs {
@@ -314,6 +334,8 @@ struct cpsw_priv {
        struct cpsw_host_regs __iomem   *host_port_regs;
        u32                             msg_enable;
        u32                             version;
+       u32                             coal_intvl;
+       u32                             bus_freq_mhz;
        struct net_device_stats         stats;
        int                             rx_packet_max;
        int                             host_port;
@@ -612,6 +634,77 @@ static void cpsw_adjust_link(struct net_device *ndev)
        }
 }
 
+static int cpsw_get_coalesce(struct net_device *ndev,
+                               struct ethtool_coalesce *coal)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+
+       coal->rx_coalesce_usecs = priv->coal_intvl;
+       return 0;
+}
+
+static int cpsw_set_coalesce(struct net_device *ndev,
+                               struct ethtool_coalesce *coal)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+       u32 int_ctrl;
+       u32 num_interrupts = 0;
+       u32 prescale = 0;
+       u32 addnl_dvdr = 1;
+       u32 coal_intvl = 0;
+
+       if (!coal->rx_coalesce_usecs)
+               return -EINVAL;
+
+       coal_intvl = coal->rx_coalesce_usecs;
+
+       int_ctrl =  readl(&priv->wr_regs->int_control);
+       prescale = priv->bus_freq_mhz * 4;
+
+       if (coal_intvl < CPSW_CMINTMIN_INTVL)
+               coal_intvl = CPSW_CMINTMIN_INTVL;
+
+       if (coal_intvl > CPSW_CMINTMAX_INTVL) {
+               /* Interrupt pacer works with 4us Pulse, we can
+                * throttle further by dilating the 4us pulse.
+                */
+               addnl_dvdr = CPSW_INTPRESCALE_MASK / prescale;
+
+               if (addnl_dvdr > 1) {
+                       prescale *= addnl_dvdr;
+                       if (coal_intvl > (CPSW_CMINTMAX_INTVL * addnl_dvdr))
+                               coal_intvl = (CPSW_CMINTMAX_INTVL
+                                               * addnl_dvdr);
+               } else {
+                       addnl_dvdr = 1;
+                       coal_intvl = CPSW_CMINTMAX_INTVL;
+               }
+       }
+
+       num_interrupts = (1000 * addnl_dvdr) / coal_intvl;
+       writel(num_interrupts, &priv->wr_regs->rx_imax);
+       writel(num_interrupts, &priv->wr_regs->tx_imax);
+
+       int_ctrl |= CPSW_INTPACEEN;
+       int_ctrl &= (~CPSW_INTPRESCALE_MASK);
+       int_ctrl |= (prescale & CPSW_INTPRESCALE_MASK);
+       writel(int_ctrl, &priv->wr_regs->int_control);
+
+       cpsw_notice(priv, timer, "Set coalesce to %d usecs.\n", coal_intvl);
+       if (priv->data.dual_emac) {
+               int i;
+
+               for (i = 0; i < priv->data.slaves; i++) {
+                       priv = netdev_priv(priv->slaves[i].ndev);
+                       priv->coal_intvl = coal_intvl;
+               }
+       } else {
+               priv->coal_intvl = coal_intvl;
+       }
+
+       return 0;
+}
+
 static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val)
 {
        static char *leader = "........................................";
@@ -834,6 +927,14 @@ static int cpsw_ndo_open(struct net_device *ndev)
                cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i);
        }
 
+       /* Enable Interrupt pacing if configured */
+       if (priv->coal_intvl != 0) {
+               struct ethtool_coalesce coal;
+
+               coal.rx_coalesce_usecs = (priv->coal_intvl << 4);
+               cpsw_set_coalesce(ndev, &coal);
+       }
+
        cpdma_ctlr_start(priv->dma);
        cpsw_intr_enable(priv);
        napi_enable(&priv->napi);
@@ -942,7 +1043,7 @@ static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags)
 
 static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
 {
-       struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave];
+       struct cpsw_slave *slave = &priv->slaves[priv->data.active_slave];
        u32 ts_en, seq_id;
 
        if (!priv->cpts->tx_enable && !priv->cpts->rx_enable) {
@@ -971,7 +1072,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
        if (priv->data.dual_emac)
                slave = &priv->slaves[priv->emac_port];
        else
-               slave = &priv->slaves[priv->data.cpts_active_slave];
+               slave = &priv->slaves[priv->data.active_slave];
 
        ctrl = slave_read(slave, CPSW2_CONTROL);
        ctrl &= ~CTRL_ALL_TS_MASK;
@@ -1056,14 +1157,26 @@ static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
 
 static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
 {
+       struct cpsw_priv *priv = netdev_priv(dev);
+       struct mii_ioctl_data *data = if_mii(req);
+       int slave_no = cpsw_slave_index(priv);
+
        if (!netif_running(dev))
                return -EINVAL;
 
+       switch (cmd) {
 #ifdef CONFIG_TI_CPTS
-       if (cmd == SIOCSHWTSTAMP)
+       case SIOCSHWTSTAMP:
                return cpsw_hwtstamp_ioctl(dev, req);
 #endif
-       return -ENOTSUPP;
+       case SIOCGMIIPHY:
+               data->phy_id = priv->slaves[slave_no].phy->addr;
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+
+       return 0;
 }
 
 static void cpsw_ndo_tx_timeout(struct net_device *ndev)
@@ -1138,7 +1251,7 @@ clean_vid:
 }
 
 static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
-               unsigned short vid)
+                                   __be16 proto, u16 vid)
 {
        struct cpsw_priv *priv = netdev_priv(ndev);
 
@@ -1150,7 +1263,7 @@ static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
 }
 
 static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev,
-               unsigned short vid)
+                                    __be16 proto, u16 vid)
 {
        struct cpsw_priv *priv = netdev_priv(ndev);
        int ret;
@@ -1244,12 +1357,39 @@ static int cpsw_get_ts_info(struct net_device *ndev,
        return 0;
 }
 
+static int cpsw_get_settings(struct net_device *ndev,
+                            struct ethtool_cmd *ecmd)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+       int slave_no = cpsw_slave_index(priv);
+
+       if (priv->slaves[slave_no].phy)
+               return phy_ethtool_gset(priv->slaves[slave_no].phy, ecmd);
+       else
+               return -EOPNOTSUPP;
+}
+
+static int cpsw_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+       int slave_no = cpsw_slave_index(priv);
+
+       if (priv->slaves[slave_no].phy)
+               return phy_ethtool_sset(priv->slaves[slave_no].phy, ecmd);
+       else
+               return -EOPNOTSUPP;
+}
+
 static const struct ethtool_ops cpsw_ethtool_ops = {
        .get_drvinfo    = cpsw_get_drvinfo,
        .get_msglevel   = cpsw_get_msglevel,
        .set_msglevel   = cpsw_set_msglevel,
        .get_link       = ethtool_op_get_link,
        .get_ts_info    = cpsw_get_ts_info,
+       .get_settings   = cpsw_get_settings,
+       .set_settings   = cpsw_set_settings,
+       .get_coalesce   = cpsw_get_coalesce,
+       .set_coalesce   = cpsw_set_coalesce,
 };
 
 static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
@@ -1282,12 +1422,12 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
        }
        data->slaves = prop;
 
-       if (of_property_read_u32(node, "cpts_active_slave", &prop)) {
-               pr_err("Missing cpts_active_slave property in the DT.\n");
+       if (of_property_read_u32(node, "active_slave", &prop)) {
+               pr_err("Missing active_slave property in the DT.\n");
                ret = -EINVAL;
                goto error_ret;
        }
-       data->cpts_active_slave = prop;
+       data->active_slave = prop;
 
        if (of_property_read_u32(node, "cpts_clock_mult", &prop)) {
                pr_err("Missing cpts_clock_mult property in the DT.\n");
@@ -1437,6 +1577,9 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
        priv_sl2->slaves = priv->slaves;
        priv_sl2->clk = priv->clk;
 
+       priv_sl2->coal_intvl = 0;
+       priv_sl2->bus_freq_mhz = priv->bus_freq_mhz;
+
        priv_sl2->cpsw_res = priv->cpsw_res;
        priv_sl2->regs = priv->regs;
        priv_sl2->host_port = priv->host_port;
@@ -1456,7 +1599,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
                priv_sl2->num_irqs = priv->num_irqs;
        }
 
-       ndev->features |= NETIF_F_HW_VLAN_FILTER;
+       ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
        SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
@@ -1546,6 +1689,8 @@ static int cpsw_probe(struct platform_device *pdev)
                ret = -ENODEV;
                goto clean_slave_ret;
        }
+       priv->coal_intvl = 0;
+       priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000;
 
        priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!priv->cpsw_res) {
@@ -1692,7 +1837,7 @@ static int cpsw_probe(struct platform_device *pdev)
                k++;
        }
 
-       ndev->features |= NETIF_F_HW_VLAN_FILTER;
+       ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
        SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
index 72300bc9e3783842ef3608be24f0f33d2ed0f69b..6a0b47715a84d0b0ddd38d7894f357d84bdb00ff 100644 (file)
@@ -1438,7 +1438,7 @@ static int emac_poll(struct napi_struct *napi, int budget)
  * Polled functionality used by netconsole and others in non interrupt mode
  *
  */
-void emac_poll_controller(struct net_device *ndev)
+static void emac_poll_controller(struct net_device *ndev)
 {
        struct emac_priv *priv = netdev_priv(ndev);
 
@@ -1865,21 +1865,18 @@ static int davinci_emac_probe(struct platform_device *pdev)
 
 
        /* obtain emac clock from kernel */
-       emac_clk = clk_get(&pdev->dev, NULL);
+       emac_clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(emac_clk)) {
                dev_err(&pdev->dev, "failed to get EMAC clock\n");
                return -EBUSY;
        }
        emac_bus_frequency = clk_get_rate(emac_clk);
-       clk_put(emac_clk);
 
        /* TODO: Probe PHY here if possible */
 
        ndev = alloc_etherdev(sizeof(struct emac_priv));
-       if (!ndev) {
-               rc = -ENOMEM;
-               goto no_ndev;
-       }
+       if (!ndev)
+               return -ENOMEM;
 
        platform_set_drvdata(pdev, ndev);
        priv = netdev_priv(ndev);
@@ -1893,7 +1890,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
        if (!pdata) {
                dev_err(&pdev->dev, "no platform data\n");
                rc = -ENODEV;
-               goto probe_quit;
+               goto no_pdata;
        }
 
        /* MAC addr and PHY mask , RMII enable info from platform_data */
@@ -1913,23 +1910,23 @@ static int davinci_emac_probe(struct platform_device *pdev)
        if (!res) {
                dev_err(&pdev->dev,"error getting res\n");
                rc = -ENOENT;
-               goto probe_quit;
+               goto no_pdata;
        }
 
        priv->emac_base_phys = res->start + pdata->ctrl_reg_offset;
        size = resource_size(res);
-       if (!request_mem_region(res->start, size, ndev->name)) {
+       if (!devm_request_mem_region(&pdev->dev, res->start,
+                                    size, ndev->name)) {
                dev_err(&pdev->dev, "failed request_mem_region() for regs\n");
                rc = -ENXIO;
-               goto probe_quit;
+               goto no_pdata;
        }
 
-       priv->remap_addr = ioremap(res->start, size);
+       priv->remap_addr = devm_ioremap(&pdev->dev, res->start, size);
        if (!priv->remap_addr) {
                dev_err(&pdev->dev, "unable to map IO\n");
                rc = -ENOMEM;
-               release_mem_region(res->start, size);
-               goto probe_quit;
+               goto no_pdata;
        }
        priv->emac_base = priv->remap_addr + pdata->ctrl_reg_offset;
        ndev->base_addr = (unsigned long)priv->remap_addr;
@@ -1962,7 +1959,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
        if (!priv->dma) {
                dev_err(&pdev->dev, "error initializing DMA\n");
                rc = -ENOMEM;
-               goto no_dma;
+               goto no_pdata;
        }
 
        priv->txchan = cpdma_chan_create(priv->dma, tx_chan_num(EMAC_DEF_TX_CH),
@@ -1971,14 +1968,14 @@ static int davinci_emac_probe(struct platform_device *pdev)
                                       emac_rx_handler);
        if (WARN_ON(!priv->txchan || !priv->rxchan)) {
                rc = -ENOMEM;
-               goto no_irq_res;
+               goto no_cpdma_chan;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!res) {
                dev_err(&pdev->dev, "error getting irq res\n");
                rc = -ENOENT;
-               goto no_irq_res;
+               goto no_cpdma_chan;
        }
        ndev->irq = res->start;
 
@@ -2000,7 +1997,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
        if (rc) {
                dev_err(&pdev->dev, "error in register_netdev\n");
                rc = -ENODEV;
-               goto no_irq_res;
+               goto no_cpdma_chan;
        }
 
 
@@ -2015,20 +2012,14 @@ static int davinci_emac_probe(struct platform_device *pdev)
 
        return 0;
 
-no_irq_res:
+no_cpdma_chan:
        if (priv->txchan)
                cpdma_chan_destroy(priv->txchan);
        if (priv->rxchan)
                cpdma_chan_destroy(priv->rxchan);
        cpdma_ctlr_destroy(priv->dma);
-no_dma:
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, resource_size(res));
-       iounmap(priv->remap_addr);
-
-probe_quit:
+no_pdata:
        free_netdev(ndev);
-no_ndev:
        return rc;
 }
 
@@ -2041,14 +2032,12 @@ no_ndev:
  */
 static int davinci_emac_remove(struct platform_device *pdev)
 {
-       struct resource *res;
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct emac_priv *priv = netdev_priv(ndev);
 
        dev_notice(&ndev->dev, "DaVinci EMAC: davinci_emac_remove()\n");
 
        platform_set_drvdata(pdev, NULL);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        if (priv->txchan)
                cpdma_chan_destroy(priv->txchan);
@@ -2056,10 +2045,7 @@ static int davinci_emac_remove(struct platform_device *pdev)
                cpdma_chan_destroy(priv->rxchan);
        cpdma_ctlr_destroy(priv->dma);
 
-       release_mem_region(res->start, resource_size(res));
-
        unregister_netdev(ndev);
-       iounmap(priv->remap_addr);
        free_netdev(ndev);
 
        return 0;
index 22725386c5de78fd94afb6715f30ed0bf24821b0..60c400f6d01ff540b6a2e132c3520972336adb1a 100644 (file)
@@ -320,6 +320,7 @@ static void tlan_remove_one(struct pci_dev *pdev)
        free_netdev(dev);
 
        pci_set_drvdata(pdev, NULL);
+       cancel_work_sync(&priv->tlan_tqueue);
 }
 
 static void tlan_start(struct net_device *dev)
@@ -1911,10 +1912,8 @@ static void tlan_reset_lists(struct net_device *dev)
                list->frame_size = TLAN_MAX_FRAME_SIZE;
                list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
                skb = netdev_alloc_skb_ip_align(dev, TLAN_MAX_FRAME_SIZE + 5);
-               if (!skb) {
-                       netdev_err(dev, "Out of memory for received data\n");
+               if (!skb)
                        break;
-               }
 
                list->buffer[0].address = pci_map_single(priv->pci_dev,
                                                         skb->data,
index 445c0595c997e4e75316bcaca11fd7c1b3dbd9ce..ad32af67e618c05ee67ec0505c161ec0c2415d13 100644 (file)
@@ -58,13 +58,6 @@ MODULE_DESCRIPTION("Gelic Network driver");
 MODULE_LICENSE("GPL");
 
 
-static inline void gelic_card_enable_rxdmac(struct gelic_card *card);
-static inline void gelic_card_disable_rxdmac(struct gelic_card *card);
-static inline void gelic_card_disable_txdmac(struct gelic_card *card);
-static inline void gelic_card_reset_chain(struct gelic_card *card,
-                                         struct gelic_descr_chain *chain,
-                                         struct gelic_descr *start_descr);
-
 /* set irq_mask */
 int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask)
 {
@@ -78,12 +71,12 @@ int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask)
        return status;
 }
 
-static inline void gelic_card_rx_irq_on(struct gelic_card *card)
+static void gelic_card_rx_irq_on(struct gelic_card *card)
 {
        card->irq_mask |= GELIC_CARD_RXINT;
        gelic_card_set_irq_mask(card, card->irq_mask);
 }
-static inline void gelic_card_rx_irq_off(struct gelic_card *card)
+static void gelic_card_rx_irq_off(struct gelic_card *card)
 {
        card->irq_mask &= ~GELIC_CARD_RXINT;
        gelic_card_set_irq_mask(card, card->irq_mask);
@@ -127,6 +120,120 @@ static int gelic_card_set_link_mode(struct gelic_card *card, int mode)
        return 0;
 }
 
+/**
+ * gelic_card_disable_txdmac - disables the transmit DMA controller
+ * @card: card structure
+ *
+ * gelic_card_disable_txdmac terminates processing on the DMA controller by
+ * turing off DMA and issuing a force end
+ */
+static void gelic_card_disable_txdmac(struct gelic_card *card)
+{
+       int status;
+
+       /* this hvc blocks until the DMA in progress really stopped */
+       status = lv1_net_stop_tx_dma(bus_id(card), dev_id(card));
+       if (status)
+               dev_err(ctodev(card),
+                       "lv1_net_stop_tx_dma failed, status=%d\n", status);
+}
+
+/**
+ * gelic_card_enable_rxdmac - enables the receive DMA controller
+ * @card: card structure
+ *
+ * gelic_card_enable_rxdmac enables the DMA controller by setting RX_DMA_EN
+ * in the GDADMACCNTR register
+ */
+static void gelic_card_enable_rxdmac(struct gelic_card *card)
+{
+       int status;
+
+#ifdef DEBUG
+       if (gelic_descr_get_status(card->rx_chain.head) !=
+           GELIC_DESCR_DMA_CARDOWNED) {
+               printk(KERN_ERR "%s: status=%x\n", __func__,
+                      be32_to_cpu(card->rx_chain.head->dmac_cmd_status));
+               printk(KERN_ERR "%s: nextphy=%x\n", __func__,
+                      be32_to_cpu(card->rx_chain.head->next_descr_addr));
+               printk(KERN_ERR "%s: head=%p\n", __func__,
+                      card->rx_chain.head);
+       }
+#endif
+       status = lv1_net_start_rx_dma(bus_id(card), dev_id(card),
+                               card->rx_chain.head->bus_addr, 0);
+       if (status)
+               dev_info(ctodev(card),
+                        "lv1_net_start_rx_dma failed, status=%d\n", status);
+}
+
+/**
+ * gelic_card_disable_rxdmac - disables the receive DMA controller
+ * @card: card structure
+ *
+ * gelic_card_disable_rxdmac terminates processing on the DMA controller by
+ * turing off DMA and issuing a force end
+ */
+static void gelic_card_disable_rxdmac(struct gelic_card *card)
+{
+       int status;
+
+       /* this hvc blocks until the DMA in progress really stopped */
+       status = lv1_net_stop_rx_dma(bus_id(card), dev_id(card));
+       if (status)
+               dev_err(ctodev(card),
+                       "lv1_net_stop_rx_dma failed, %d\n", status);
+}
+
+/**
+ * gelic_descr_set_status -- sets the status of a descriptor
+ * @descr: descriptor to change
+ * @status: status to set in the descriptor
+ *
+ * changes the status to the specified value. Doesn't change other bits
+ * in the status
+ */
+static void gelic_descr_set_status(struct gelic_descr *descr,
+                                  enum gelic_descr_dma_status status)
+{
+       descr->dmac_cmd_status = cpu_to_be32(status |
+                       (be32_to_cpu(descr->dmac_cmd_status) &
+                        ~GELIC_DESCR_DMA_STAT_MASK));
+       /*
+        * dma_cmd_status field is used to indicate whether the descriptor
+        * is valid or not.
+        * Usually caller of this function wants to inform that to the
+        * hardware, so we assure here the hardware sees the change.
+        */
+       wmb();
+}
+
+/**
+ * gelic_card_reset_chain - reset status of a descriptor chain
+ * @card: card structure
+ * @chain: address of chain
+ * @start_descr: address of descriptor array
+ *
+ * Reset the status of dma descriptors to ready state
+ * and re-initialize the hardware chain for later use
+ */
+static void gelic_card_reset_chain(struct gelic_card *card,
+                                  struct gelic_descr_chain *chain,
+                                  struct gelic_descr *start_descr)
+{
+       struct gelic_descr *descr;
+
+       for (descr = start_descr; start_descr != descr->next; descr++) {
+               gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED);
+               descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr);
+       }
+
+       chain->head = start_descr;
+       chain->tail = (descr - 1);
+
+       (descr - 1)->next_descr_addr = 0;
+}
+
 void gelic_card_up(struct gelic_card *card)
 {
        pr_debug("%s: called\n", __func__);
@@ -182,29 +289,6 @@ gelic_descr_get_status(struct gelic_descr *descr)
        return be32_to_cpu(descr->dmac_cmd_status) & GELIC_DESCR_DMA_STAT_MASK;
 }
 
-/**
- * gelic_descr_set_status -- sets the status of a descriptor
- * @descr: descriptor to change
- * @status: status to set in the descriptor
- *
- * changes the status to the specified value. Doesn't change other bits
- * in the status
- */
-static void gelic_descr_set_status(struct gelic_descr *descr,
-                                  enum gelic_descr_dma_status status)
-{
-       descr->dmac_cmd_status = cpu_to_be32(status |
-                       (be32_to_cpu(descr->dmac_cmd_status) &
-                        ~GELIC_DESCR_DMA_STAT_MASK));
-       /*
-        * dma_cmd_status field is used to indicate whether the descriptor
-        * is valid or not.
-        * Usually caller of this function wants to inform that to the
-        * hardware, so we assure here the hardware sees the change.
-        */
-       wmb();
-}
-
 /**
  * gelic_card_free_chain - free descriptor chain
  * @card: card structure
@@ -285,31 +369,6 @@ iommu_error:
        return -ENOMEM;
 }
 
-/**
- * gelic_card_reset_chain - reset status of a descriptor chain
- * @card: card structure
- * @chain: address of chain
- * @start_descr: address of descriptor array
- *
- * Reset the status of dma descriptors to ready state
- * and re-initialize the hardware chain for later use
- */
-static void gelic_card_reset_chain(struct gelic_card *card,
-                                  struct gelic_descr_chain *chain,
-                                  struct gelic_descr *start_descr)
-{
-       struct gelic_descr *descr;
-
-       for (descr = start_descr; start_descr != descr->next; descr++) {
-               gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED);
-               descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr);
-       }
-
-       chain->head = start_descr;
-       chain->tail = (descr - 1);
-
-       (descr - 1)->next_descr_addr = 0;
-}
 /**
  * gelic_descr_prepare_rx - reinitializes a rx descriptor
  * @card: card structure
@@ -598,71 +657,6 @@ void gelic_net_set_multi(struct net_device *netdev)
        }
 }
 
-/**
- * gelic_card_enable_rxdmac - enables the receive DMA controller
- * @card: card structure
- *
- * gelic_card_enable_rxdmac enables the DMA controller by setting RX_DMA_EN
- * in the GDADMACCNTR register
- */
-static inline void gelic_card_enable_rxdmac(struct gelic_card *card)
-{
-       int status;
-
-#ifdef DEBUG
-       if (gelic_descr_get_status(card->rx_chain.head) !=
-           GELIC_DESCR_DMA_CARDOWNED) {
-               printk(KERN_ERR "%s: status=%x\n", __func__,
-                      be32_to_cpu(card->rx_chain.head->dmac_cmd_status));
-               printk(KERN_ERR "%s: nextphy=%x\n", __func__,
-                      be32_to_cpu(card->rx_chain.head->next_descr_addr));
-               printk(KERN_ERR "%s: head=%p\n", __func__,
-                      card->rx_chain.head);
-       }
-#endif
-       status = lv1_net_start_rx_dma(bus_id(card), dev_id(card),
-                               card->rx_chain.head->bus_addr, 0);
-       if (status)
-               dev_info(ctodev(card),
-                        "lv1_net_start_rx_dma failed, status=%d\n", status);
-}
-
-/**
- * gelic_card_disable_rxdmac - disables the receive DMA controller
- * @card: card structure
- *
- * gelic_card_disable_rxdmac terminates processing on the DMA controller by
- * turing off DMA and issuing a force end
- */
-static inline void gelic_card_disable_rxdmac(struct gelic_card *card)
-{
-       int status;
-
-       /* this hvc blocks until the DMA in progress really stopped */
-       status = lv1_net_stop_rx_dma(bus_id(card), dev_id(card));
-       if (status)
-               dev_err(ctodev(card),
-                       "lv1_net_stop_rx_dma failed, %d\n", status);
-}
-
-/**
- * gelic_card_disable_txdmac - disables the transmit DMA controller
- * @card: card structure
- *
- * gelic_card_disable_txdmac terminates processing on the DMA controller by
- * turing off DMA and issuing a force end
- */
-static inline void gelic_card_disable_txdmac(struct gelic_card *card)
-{
-       int status;
-
-       /* this hvc blocks until the DMA in progress really stopped */
-       status = lv1_net_stop_tx_dma(bus_id(card), dev_id(card));
-       if (status)
-               dev_err(ctodev(card),
-                       "lv1_net_stop_tx_dma failed, status=%d\n", status);
-}
-
 /**
  * gelic_net_stop - called upon ifconfig down
  * @netdev: interface device structure
@@ -746,7 +740,7 @@ static void gelic_descr_set_tx_cmdstat(struct gelic_descr *descr,
        }
 }
 
-static inline struct sk_buff *gelic_put_vlan_tag(struct sk_buff *skb,
+static struct sk_buff *gelic_put_vlan_tag(struct sk_buff *skb,
                                                 unsigned short tag)
 {
        struct vlan_ethhdr *veth;
index f1b91fd7e41c9b088de874e98dc3101c6942af61..c655fe60121ee3694297f289ef3d4529d3081a9d 100644 (file)
@@ -352,8 +352,7 @@ spider_net_init_chain(struct spider_net_card *card,
        alloc_size = chain->num_desc * sizeof(struct spider_net_hw_descr);
 
        chain->hwring = dma_alloc_coherent(&card->pdev->dev, alloc_size,
-               &chain->dma_addr, GFP_KERNEL);
-
+                                          &chain->dma_addr, GFP_KERNEL);
        if (!chain->hwring)
                return -ENOMEM;
 
@@ -2330,8 +2329,8 @@ spider_net_setup_netdev(struct spider_net_card *card)
        if (SPIDER_NET_RX_CSUM_DEFAULT)
                netdev->features |= NETIF_F_RXCSUM;
        netdev->features |= NETIF_F_IP_CSUM | NETIF_F_LLTX;
-       /* some time: NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
-        *              NETIF_F_HW_VLAN_FILTER */
+       /* some time: NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+        *              NETIF_F_HW_VLAN_CTAG_FILTER */
 
        netdev->irq = card->pdev->irq;
        card->num_rx_ints = 0;
index 8fa947a2d9290f2dd7dc612968199aa0f46fdde1..3c69a04608324c8c16bc33cee78f03f524329018 100644 (file)
@@ -1308,27 +1308,16 @@ static int tsi108_open(struct net_device *dev)
                       data->id, dev->irq, dev->name);
        }
 
-       data->rxring = dma_alloc_coherent(NULL, rxring_size,
-                       &data->rxdma, GFP_KERNEL);
-
-       if (!data->rxring) {
-               printk(KERN_DEBUG
-                      "TSI108_ETH: failed to allocate memory for rxring!\n");
+       data->rxring = dma_alloc_coherent(NULL, rxring_size, &data->rxdma,
+                                         GFP_KERNEL | __GFP_ZERO);
+       if (!data->rxring)
                return -ENOMEM;
-       } else {
-               memset(data->rxring, 0, rxring_size);
-       }
-
-       data->txring = dma_alloc_coherent(NULL, txring_size,
-                       &data->txdma, GFP_KERNEL);
 
+       data->txring = dma_alloc_coherent(NULL, txring_size, &data->txdma,
+                                         GFP_KERNEL | __GFP_ZERO);
        if (!data->txring) {
-               printk(KERN_DEBUG
-                      "TSI108_ETH: failed to allocate memory for txring!\n");
                pci_free_consistent(0, rxring_size, data->rxring, data->rxdma);
                return -ENOMEM;
-       } else {
-               memset(data->txring, 0, txring_size);
        }
 
        for (i = 0; i < TSI108_RXRING_LEN; i++) {
index 185c721c52d7ba544e613020dcf619cede479e94..ca98acabf1b43b29b6f27c547fbbb0c5277b2e8a 100644 (file)
@@ -508,8 +508,10 @@ static struct rtnl_link_stats64 *rhine_get_stats64(struct net_device *dev,
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static const struct ethtool_ops netdev_ethtool_ops;
 static int  rhine_close(struct net_device *dev);
-static int rhine_vlan_rx_add_vid(struct net_device *dev, unsigned short vid);
-static int rhine_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid);
+static int rhine_vlan_rx_add_vid(struct net_device *dev,
+                                __be16 proto, u16 vid);
+static int rhine_vlan_rx_kill_vid(struct net_device *dev,
+                                 __be16 proto, u16 vid);
 static void rhine_restart_tx(struct net_device *dev);
 
 static void rhine_wait_bit(struct rhine_private *rp, u8 reg, u8 mask, bool low)
@@ -1026,8 +1028,9 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev->features |= NETIF_F_SG|NETIF_F_HW_CSUM;
 
        if (pdev->revision >= VT6105M)
-               dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
-               NETIF_F_HW_VLAN_FILTER;
+               dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
+                                NETIF_F_HW_VLAN_CTAG_RX |
+                                NETIF_F_HW_VLAN_CTAG_FILTER;
 
        /* dev->name not defined before register_netdev()! */
        rc = register_netdev(dev);
@@ -1414,7 +1417,7 @@ static void rhine_update_vcam(struct net_device *dev)
        rhine_set_vlan_cam_mask(ioaddr, vCAMmask);
 }
 
-static int rhine_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+static int rhine_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct rhine_private *rp = netdev_priv(dev);
 
@@ -1425,7 +1428,7 @@ static int rhine_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
        return 0;
 }
 
-static int rhine_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+static int rhine_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct rhine_private *rp = netdev_priv(dev);
 
@@ -1933,7 +1936,7 @@ static int rhine_rx(struct net_device *dev, int limit)
                        skb->protocol = eth_type_trans(skb, dev);
 
                        if (unlikely(desc_length & DescTag))
-                               __vlan_hwaccel_put_tag(skb, vlan_tci);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci);
                        netif_receive_skb(skb);
 
                        u64_stats_update_begin(&rp->rx_stats.syncp);
index 1bc7f9fd25836643771c58bc2977eab640ee021f..fb6248956ee26ec55c8c4cbcd853fc721c74e1d7 100644 (file)
@@ -525,7 +525,8 @@ static void velocity_init_cam_filter(struct velocity_info *vptr)
        mac_set_vlan_cam_mask(regs, vptr->vCAMmask);
 }
 
-static int velocity_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+static int velocity_vlan_rx_add_vid(struct net_device *dev,
+                                   __be16 proto, u16 vid)
 {
        struct velocity_info *vptr = netdev_priv(dev);
 
@@ -536,7 +537,8 @@ static int velocity_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
        return 0;
 }
 
-static int velocity_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+static int velocity_vlan_rx_kill_vid(struct net_device *dev,
+                                    __be16 proto, u16 vid)
 {
        struct velocity_info *vptr = netdev_priv(dev);
 
@@ -2078,7 +2080,7 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
        if (rd->rdesc0.RSR & RSR_DETAG) {
                u16 vid = swab16(le16_to_cpu(rd->rdesc1.PQTAG));
 
-               __vlan_hwaccel_put_tag(skb, vid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
        }
        netif_rx(skb);
 
@@ -2810,9 +2812,10 @@ static int velocity_found1(struct pci_dev *pdev,
        dev->ethtool_ops = &velocity_ethtool_ops;
        netif_napi_add(dev, &vptr->napi, velocity_poll, VELOCITY_NAPI_WEIGHT);
 
-       dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HW_VLAN_TX;
-       dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER |
-               NETIF_F_HW_VLAN_RX | NETIF_F_IP_CSUM;
+       dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
+                          NETIF_F_HW_VLAN_CTAG_TX;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER |
+                        NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_IP_CSUM;
 
        ret = register_netdev(dev);
        if (ret < 0)
index 545043cc4c0b4c54feb26787940ed4951f33974f..a518dcab396e69cdbd96b8133bef82e237c5daf3 100644 (file)
@@ -754,7 +754,7 @@ static int w5100_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int w5100_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -787,7 +787,7 @@ static int w5100_resume(struct device *dev)
        }
        return 0;
 }
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
 
 static SIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume);
 
index 7cbd0e6fc6f3b1e6fbe612efd7233df4557f4cdc..6e00e3f94ce4e4909d9b624b710f888039f68ded 100644 (file)
@@ -666,7 +666,7 @@ static int w5300_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int w5300_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -699,7 +699,7 @@ static int w5300_resume(struct device *dev)
        }
        return 0;
 }
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
 
 static SIMPLE_DEV_PM_OPS(w5300_pm_ops, w5300_suspend, w5300_resume);
 
index 9fc2ada4c3c2f22b001021264564ceca323f62ea..57c2e5ef2804ec5a89841557bb4e867c11cde5ec 100644 (file)
@@ -245,39 +245,30 @@ static int temac_dma_bd_init(struct net_device *ndev)
        /* returns a virtual address and a physical address. */
        lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent,
                                         sizeof(*lp->tx_bd_v) * TX_BD_NUM,
-                                        &lp->tx_bd_p, GFP_KERNEL);
-       if (!lp->tx_bd_v) {
-               dev_err(&ndev->dev,
-                               "unable to allocate DMA TX buffer descriptors");
+                                        &lp->tx_bd_p, GFP_KERNEL | __GFP_ZERO);
+       if (!lp->tx_bd_v)
                goto out;
-       }
+
        lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent,
                                         sizeof(*lp->rx_bd_v) * RX_BD_NUM,
-                                        &lp->rx_bd_p, GFP_KERNEL);
-       if (!lp->rx_bd_v) {
-               dev_err(&ndev->dev,
-                               "unable to allocate DMA RX buffer descriptors");
+                                        &lp->rx_bd_p, GFP_KERNEL | __GFP_ZERO);
+       if (!lp->rx_bd_v)
                goto out;
-       }
 
-       memset(lp->tx_bd_v, 0, sizeof(*lp->tx_bd_v) * TX_BD_NUM);
        for (i = 0; i < TX_BD_NUM; i++) {
                lp->tx_bd_v[i].next = lp->tx_bd_p +
                                sizeof(*lp->tx_bd_v) * ((i + 1) % TX_BD_NUM);
        }
 
-       memset(lp->rx_bd_v, 0, sizeof(*lp->rx_bd_v) * RX_BD_NUM);
        for (i = 0; i < RX_BD_NUM; i++) {
                lp->rx_bd_v[i].next = lp->rx_bd_p +
                                sizeof(*lp->rx_bd_v) * ((i + 1) % RX_BD_NUM);
 
                skb = netdev_alloc_skb_ip_align(ndev,
                                                XTE_MAX_JUMBO_FRAME_SIZE);
-
-               if (skb == 0) {
-                       dev_err(&ndev->dev, "alloc_skb error %d\n", i);
+               if (!skb)
                        goto out;
-               }
+
                lp->rx_skb[i] = skb;
                /* returns physical address of skb->data */
                lp->rx_bd_v[i].phys = dma_map_single(ndev->dev.parent,
@@ -789,9 +780,7 @@ static void ll_temac_recv(struct net_device *ndev)
 
                new_skb = netdev_alloc_skb_ip_align(ndev,
                                                XTE_MAX_JUMBO_FRAME_SIZE);
-
-               if (new_skb == 0) {
-                       dev_err(&ndev->dev, "no memory for new sk_buff\n");
+               if (!new_skb) {
                        spin_unlock_irqrestore(&lp->rx_lock, flags);
                        return;
                }
@@ -1029,9 +1018,9 @@ static int temac_of_probe(struct platform_device *op)
        ndev->features |= NETIF_F_HW_CSUM; /* Can checksum all the packets. */
        ndev->features |= NETIF_F_IPV6_CSUM; /* Can checksum IPV6 TCP/UDP */
        ndev->features |= NETIF_F_HIGHDMA; /* Can DMA to high memory. */
-       ndev->features |= NETIF_F_HW_VLAN_TX; /* Transmit VLAN hw accel */
-       ndev->features |= NETIF_F_HW_VLAN_RX; /* Receive VLAN hw acceleration */
-       ndev->features |= NETIF_F_HW_VLAN_FILTER; /* Receive VLAN filtering */
+       ndev->features |= NETIF_F_HW_VLAN_CTAG_TX; /* Transmit VLAN hw accel */
+       ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; /* Receive VLAN hw acceleration */
+       ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; /* Receive VLAN filtering */
        ndev->features |= NETIF_F_VLAN_CHALLENGED; /* cannot handle VLAN pkts */
        ndev->features |= NETIF_F_GSO; /* Enable software GSO. */
        ndev->features |= NETIF_F_MULTI_QUEUE; /* Has multiple TX/RX queues */
index 278c9db3b5b853e4cb41caf83a6990db517a899d..24748e8367a1ad346a245bc890eb27bc8b659b0a 100644 (file)
@@ -204,41 +204,31 @@ static int axienet_dma_bd_init(struct net_device *ndev)
        lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent,
                                         sizeof(*lp->tx_bd_v) * TX_BD_NUM,
                                         &lp->tx_bd_p,
-                                        GFP_KERNEL);
-       if (!lp->tx_bd_v) {
-               dev_err(&ndev->dev, "unable to allocate DMA Tx buffer "
-                       "descriptors");
+                                        GFP_KERNEL | __GFP_ZERO);
+       if (!lp->tx_bd_v)
                goto out;
-       }
 
        lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent,
                                         sizeof(*lp->rx_bd_v) * RX_BD_NUM,
                                         &lp->rx_bd_p,
-                                        GFP_KERNEL);
-       if (!lp->rx_bd_v) {
-               dev_err(&ndev->dev, "unable to allocate DMA Rx buffer "
-                       "descriptors");
+                                        GFP_KERNEL | __GFP_ZERO);
+       if (!lp->rx_bd_v)
                goto out;
-       }
 
-       memset(lp->tx_bd_v, 0, sizeof(*lp->tx_bd_v) * TX_BD_NUM);
        for (i = 0; i < TX_BD_NUM; i++) {
                lp->tx_bd_v[i].next = lp->tx_bd_p +
                                      sizeof(*lp->tx_bd_v) *
                                      ((i + 1) % TX_BD_NUM);
        }
 
-       memset(lp->rx_bd_v, 0, sizeof(*lp->rx_bd_v) * RX_BD_NUM);
        for (i = 0; i < RX_BD_NUM; i++) {
                lp->rx_bd_v[i].next = lp->rx_bd_p +
                                      sizeof(*lp->rx_bd_v) *
                                      ((i + 1) % RX_BD_NUM);
 
                skb = netdev_alloc_skb_ip_align(ndev, lp->max_frm_size);
-               if (!skb) {
-                       dev_err(&ndev->dev, "alloc_skb error %d\n", i);
+               if (!skb)
                        goto out;
-               }
 
                lp->rx_bd_v[i].sw_id_offset = (u32) skb;
                lp->rx_bd_v[i].phys = dma_map_single(ndev->dev.parent,
@@ -777,10 +767,9 @@ static void axienet_recv(struct net_device *ndev)
                packets++;
 
                new_skb = netdev_alloc_skb_ip_align(ndev, lp->max_frm_size);
-               if (!new_skb) {
-                       dev_err(&ndev->dev, "no memory for new sk_buff\n");
+               if (!new_skb)
                        return;
-               }
+
                cur_p->phys = dma_map_single(ndev->dev.parent, new_skb->data,
                                             lp->max_frm_size,
                                             DMA_FROM_DEVICE);
index 98e09d0d3ce22e153603efc37f486dfdf55174ac..76210abf2e9b5b7d62ec2effc884c1fce979fe03 100644 (file)
@@ -1041,7 +1041,6 @@ xirc2ps_interrupt(int irq, void *dev_id)
            /* 1 extra so we can use insw */
            skb = netdev_alloc_skb(dev, pktlen + 3);
            if (!skb) {
-               pr_notice("low memory, packet dropped (size=%u)\n", pktlen);
                dev->stats.rx_dropped++;
            } else { /* okay get the packet */
                skb_reserve(skb, 2);
index 502c8ff1d985b80dd96423040e54fc54dedabaeb..4c8ddc944d51f5d412b85bdbd8a29413512b9878 100644 (file)
@@ -1070,13 +1070,10 @@ static int dfx_driver_init(struct net_device *dev, const char *print_name,
                                        (PI_ALIGN_K_DESC_BLK - 1);
        bp->kmalloced = top_v = dma_alloc_coherent(bp->bus_dev, alloc_size,
                                                   &bp->kmalloced_dma,
-                                                  GFP_ATOMIC);
-       if (top_v == NULL) {
-               printk("%s: Could not allocate memory for host buffers "
-                      "and structures!\n", print_name);
+                                                  GFP_ATOMIC | __GFP_ZERO);
+       if (top_v == NULL)
                return DFX_K_FAILURE;
-       }
-       memset(top_v, 0, alloc_size);   /* zero out memory before continuing */
+
        top_p = bp->kmalloced_dma;      /* get physical address of buffer */
 
        /*
index 4cf8f1017aad40bcb613c1922bdc9519f976f399..b2d863f2ea4255a2f9cc61bda6a9fdff8ab5623c 100644 (file)
@@ -866,7 +866,7 @@ static int yam_open(struct net_device *dev)
 
        printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq);
 
-       if (!dev || !yp->bitrate)
+       if (!yp->bitrate)
                return -ENXIO;
        if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT ||
                dev->irq < 2 || dev->irq > 15) {
index 8341b62e552166a4f796f7977b6f31a2b945bcdd..088c554961918beeec45dd9c6d22d2508409e92c 100644 (file)
@@ -429,7 +429,7 @@ static int netvsc_probe(struct hv_device *dev,
 
        /* TODO: Add GSO and Checksum offload */
        net->hw_features = NETIF_F_SG;
-       net->features = NETIF_F_SG | NETIF_F_HW_VLAN_TX;
+       net->features = NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_TX;
 
        SET_ETHTOOL_OPS(net, &ethtool_ops);
        SET_NETDEV_DEV(net, &dev->device);
index fc1687ea4a42438ff096d1c71dd2ce9355693cd5..6f10b496472662c2550d943db16dac9dcb6dda4d 100644 (file)
@@ -51,7 +51,7 @@ struct at86rf230_local {
        struct ieee802154_dev *dev;
 
        spinlock_t lock;
-       bool irq_disabled;
+       bool irq_busy;
        bool is_tx;
 };
 
@@ -219,6 +219,9 @@ struct at86rf230_local {
 #define IRQ_PLL_UNL    (1 << 1)
 #define IRQ_PLL_LOCK   (1 << 0)
 
+#define IRQ_ACTIVE_HIGH        0
+#define IRQ_ACTIVE_LOW 1
+
 #define STATE_P_ON             0x00    /* BUSY */
 #define STATE_BUSY_RX          0x01
 #define STATE_BUSY_TX          0x02
@@ -233,8 +236,8 @@ struct at86rf230_local {
 #define STATE_SLEEP            0x0F
 #define STATE_BUSY_RX_AACK     0x11
 #define STATE_BUSY_TX_ARET     0x12
-#define STATE_BUSY_RX_AACK_ON  0x16
-#define STATE_BUSY_TX_ARET_ON  0x19
+#define STATE_RX_AACK_ON       0x16
+#define STATE_TX_ARET_ON       0x19
 #define STATE_RX_ON_NOCLK      0x1C
 #define STATE_RX_AACK_ON_NOCLK 0x1D
 #define STATE_BUSY_RX_AACK_NOCLK 0x1E
@@ -544,7 +547,7 @@ at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
        unsigned long flags;
 
        spin_lock(&lp->lock);
-       if  (lp->irq_disabled) {
+       if  (lp->irq_busy) {
                spin_unlock(&lp->lock);
                return -EBUSY;
        }
@@ -619,6 +622,52 @@ err:
        return -EINVAL;
 }
 
+static int
+at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
+                          struct ieee802154_hw_addr_filt *filt,
+                          unsigned long changed)
+{
+       struct at86rf230_local *lp = dev->priv;
+
+       if (changed & IEEE802515_AFILT_SADDR_CHANGED) {
+               dev_vdbg(&lp->spi->dev,
+                       "at86rf230_set_hw_addr_filt called for saddr\n");
+               __at86rf230_write(lp, RG_SHORT_ADDR_0, filt->short_addr);
+               __at86rf230_write(lp, RG_SHORT_ADDR_1, filt->short_addr >> 8);
+       }
+
+       if (changed & IEEE802515_AFILT_PANID_CHANGED) {
+               dev_vdbg(&lp->spi->dev,
+                       "at86rf230_set_hw_addr_filt called for pan id\n");
+               __at86rf230_write(lp, RG_PAN_ID_0, filt->pan_id);
+               __at86rf230_write(lp, RG_PAN_ID_1, filt->pan_id >> 8);
+       }
+
+       if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) {
+               dev_vdbg(&lp->spi->dev,
+                       "at86rf230_set_hw_addr_filt called for IEEE addr\n");
+               at86rf230_write_subreg(lp, SR_IEEE_ADDR_0, filt->ieee_addr[7]);
+               at86rf230_write_subreg(lp, SR_IEEE_ADDR_1, filt->ieee_addr[6]);
+               at86rf230_write_subreg(lp, SR_IEEE_ADDR_2, filt->ieee_addr[5]);
+               at86rf230_write_subreg(lp, SR_IEEE_ADDR_3, filt->ieee_addr[4]);
+               at86rf230_write_subreg(lp, SR_IEEE_ADDR_4, filt->ieee_addr[3]);
+               at86rf230_write_subreg(lp, SR_IEEE_ADDR_5, filt->ieee_addr[2]);
+               at86rf230_write_subreg(lp, SR_IEEE_ADDR_6, filt->ieee_addr[1]);
+               at86rf230_write_subreg(lp, SR_IEEE_ADDR_7, filt->ieee_addr[0]);
+       }
+
+       if (changed & IEEE802515_AFILT_PANC_CHANGED) {
+               dev_vdbg(&lp->spi->dev,
+                       "at86rf230_set_hw_addr_filt called for panc change\n");
+               if (filt->pan_coord)
+                       at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 1);
+               else
+                       at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 0);
+       }
+
+       return 0;
+}
+
 static struct ieee802154_ops at86rf230_ops = {
        .owner = THIS_MODULE,
        .xmit = at86rf230_xmit,
@@ -626,6 +675,7 @@ static struct ieee802154_ops at86rf230_ops = {
        .set_channel = at86rf230_channel,
        .start = at86rf230_start,
        .stop = at86rf230_stop,
+       .set_hw_addr_filt = at86rf230_set_hw_addr_filt,
 };
 
 static void at86rf230_irqwork(struct work_struct *work)
@@ -658,8 +708,16 @@ static void at86rf230_irqwork(struct work_struct *work)
        }
 
        spin_lock_irqsave(&lp->lock, flags);
-       lp->irq_disabled = 0;
+       lp->irq_busy = 0;
        spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static void at86rf230_irqwork_level(struct work_struct *work)
+{
+       struct at86rf230_local *lp =
+               container_of(work, struct at86rf230_local, irqwork);
+
+       at86rf230_irqwork(work);
 
        enable_irq(lp->spi->irq);
 }
@@ -668,10 +726,8 @@ static irqreturn_t at86rf230_isr(int irq, void *data)
 {
        struct at86rf230_local *lp = data;
 
-       disable_irq_nosync(irq);
-
        spin_lock(&lp->lock);
-       lp->irq_disabled = 1;
+       lp->irq_busy = 1;
        spin_unlock(&lp->lock);
 
        schedule_work(&lp->irqwork);
@@ -679,11 +735,23 @@ static irqreturn_t at86rf230_isr(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t at86rf230_isr_level(int irq, void *data)
+{
+       disable_irq_nosync(irq);
+
+       return at86rf230_isr(irq, data);
+}
+
+static int at86rf230_irq_polarity(struct at86rf230_local *lp, int pol)
+{
+       return at86rf230_write_subreg(lp, SR_IRQ_POLARITY, pol);
+}
 
 static int at86rf230_hw_init(struct at86rf230_local *lp)
 {
+       struct at86rf230_platform_data *pdata = lp->spi->dev.platform_data;
+       int rc, irq_pol;
        u8 status;
-       int rc;
 
        rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
        if (rc)
@@ -701,12 +769,17 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
                dev_info(&lp->spi->dev, "Status: %02x\n", status);
        }
 
-       rc = at86rf230_write_subreg(lp, SR_IRQ_MASK, 0xff); /* IRQ_TRX_UR |
-                                                            * IRQ_CCA_ED |
-                                                            * IRQ_TRX_END |
-                                                            * IRQ_PLL_UNL |
-                                                            * IRQ_PLL_LOCK
-                                                            */
+       /* configure irq polarity, defaults to high active */
+       if (pdata->irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
+               irq_pol = IRQ_ACTIVE_LOW;
+       else
+               irq_pol = IRQ_ACTIVE_HIGH;
+
+       rc = at86rf230_irq_polarity(lp, irq_pol);
+       if (rc)
+               return rc;
+
+       rc = at86rf230_write_subreg(lp, SR_IRQ_MASK, IRQ_TRX_END);
        if (rc)
                return rc;
 
@@ -751,37 +824,38 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
        return 0;
 }
 
-static int at86rf230_fill_data(struct spi_device *spi)
+static void at86rf230_fill_data(struct spi_device *spi)
 {
        struct at86rf230_local *lp = spi_get_drvdata(spi);
        struct at86rf230_platform_data *pdata = spi->dev.platform_data;
 
-       if (!pdata) {
-               dev_err(&spi->dev, "no platform_data\n");
-               return -EINVAL;
-       }
-
        lp->rstn = pdata->rstn;
        lp->slp_tr = pdata->slp_tr;
        lp->dig2 = pdata->dig2;
-
-       return 0;
 }
 
 static int at86rf230_probe(struct spi_device *spi)
 {
+       struct at86rf230_platform_data *pdata;
        struct ieee802154_dev *dev;
        struct at86rf230_local *lp;
-       u8 man_id_0, man_id_1;
-       int rc;
+       u8 man_id_0, man_id_1, status;
+       irq_handler_t irq_handler;
+       work_func_t irq_worker;
+       int rc, supported = 0;
        const char *chip;
-       int supported = 0;
 
        if (!spi->irq) {
                dev_err(&spi->dev, "no IRQ specified\n");
                return -EINVAL;
        }
 
+       pdata = spi->dev.platform_data;
+       if (!pdata) {
+               dev_err(&spi->dev, "no platform_data\n");
+               return -EINVAL;
+       }
+
        dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops);
        if (!dev)
                return -ENOMEM;
@@ -791,23 +865,28 @@ static int at86rf230_probe(struct spi_device *spi)
 
        lp->spi = spi;
 
-       dev->priv = lp;
        dev->parent = &spi->dev;
        dev->extra_tx_headroom = 0;
        /* We do support only 2.4 Ghz */
        dev->phy->channels_supported[0] = 0x7FFF800;
        dev->flags = IEEE802154_HW_OMIT_CKSUM;
 
+       if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+               irq_worker = at86rf230_irqwork;
+               irq_handler = at86rf230_isr;
+       } else {
+               irq_worker = at86rf230_irqwork_level;
+               irq_handler = at86rf230_isr_level;
+       }
+
        mutex_init(&lp->bmux);
-       INIT_WORK(&lp->irqwork, at86rf230_irqwork);
+       INIT_WORK(&lp->irqwork, irq_worker);
        spin_lock_init(&lp->lock);
        init_completion(&lp->tx_complete);
 
        spi_set_drvdata(spi, lp);
 
-       rc = at86rf230_fill_data(spi);
-       if (rc)
-               goto err_fill;
+       at86rf230_fill_data(spi);
 
        rc = gpio_request(lp->rstn, "rstn");
        if (rc)
@@ -882,18 +961,23 @@ static int at86rf230_probe(struct spi_device *spi)
        if (rc)
                goto err_gpio_dir;
 
-       rc = request_irq(spi->irq, at86rf230_isr, IRQF_SHARED,
+       rc = request_irq(spi->irq, irq_handler,
+                        IRQF_SHARED | pdata->irq_type,
                         dev_name(&spi->dev), lp);
        if (rc)
                goto err_gpio_dir;
 
+       /* Read irq status register to reset irq line */
+       rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
+       if (rc)
+               goto err_irq;
+
        rc = ieee802154_register_device(lp->dev);
        if (rc)
                goto err_irq;
 
        return rc;
 
-       ieee802154_unregister_device(lp->dev);
 err_irq:
        free_irq(spi->irq, lp);
        flush_work(&lp->irqwork);
@@ -903,7 +987,6 @@ err_gpio_dir:
 err_slp_tr:
        gpio_free(lp->rstn);
 err_rstn:
-err_fill:
        spi_set_drvdata(spi, NULL);
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
index 8f1c25676d44f2aab087d85e06576d9037d3f87b..bf0d55e2dd635613c3712b5b73ab528217e5cff7 100644 (file)
@@ -105,26 +105,6 @@ static u8 fake_get_dsn(const struct net_device *dev)
        return 0x00; /* DSN are implemented in HW, so return just 0 */
 }
 
-/**
- * fake_get_bsn - Retrieve the BSN of the device.
- * @dev: The network device to retrieve the BSN for.
- *
- * Returns the IEEE 802.15.4 BSN for the network device.
- * The BSN is the sequence number which will be added to each
- * beacon frame sent by the MAC.
- *
- * BSN means 'Beacon Sequence Number'.
- *
- * Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
- *       document.
- */
-static u8 fake_get_bsn(const struct net_device *dev)
-{
-       BUG_ON(dev->type != ARPHRD_IEEE802154);
-
-       return 0x00; /* BSN are implemented in HW, so return just 0 */
-}
-
 /**
  * fake_assoc_req - Make an association request to the HW.
  * @dev: The network device which we are associating to a network.
@@ -264,7 +244,6 @@ static struct ieee802154_mlme_ops fake_mlme = {
        .get_pan_id = fake_get_pan_id,
        .get_short_addr = fake_get_short_addr,
        .get_dsn = fake_get_dsn,
-       .get_bsn = fake_get_bsn,
 };
 
 static int ieee802154_fake_open(struct net_device *dev)
index 3f2c7aaf28c4be64356cfc0149dfc486389ae380..ede3ce4912f92162b9411167fb8cab5ea248c479 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
 #include <net/wpan-phy.h>
 #include <net/mac802154.h>
+#include <net/ieee802154.h>
 
 /* MRF24J40 Short Address Registers */
 #define REG_RXMCR    0x00  /* Receive MAC control */
@@ -91,9 +93,8 @@ struct mrf24j40 {
 #define MRF24J40_READLONG(reg) (1 << 15 | (reg) << 5)
 #define MRF24J40_WRITELONG(reg) (1 << 15 | (reg) << 5 | 1 << 4)
 
-/* Maximum speed to run the device at. TODO: Get the real max value from
- * someone at Microchip since it isn't in the datasheet. */
-#define MAX_SPI_SPEED_HZ 1000000
+/* The datasheet indicates the theoretical maximum for SCK to be 10MHz */
+#define MAX_SPI_SPEED_HZ 10000000
 
 #define printdev(X) (&X->spi->dev)
 
@@ -349,7 +350,9 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (ret)
                goto err;
        val |= 0x1;
-       val &= ~0x4;
+       /* Set TXNACKREQ if the ACK bit is set in the packet. */
+       if (skb->data[0] & IEEE802154_FC_ACK_REQ)
+               val |= 0x4;
        write_short_reg(devrec, REG_TXNCON, val);
 
        INIT_COMPLETION(devrec->tx_complete);
@@ -361,6 +364,7 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (ret == -ERESTARTSYS)
                goto err;
        if (ret == 0) {
+               dev_warn(printdev(devrec), "Timeout waiting for TX interrupt\n");
                ret = -ETIMEDOUT;
                goto err;
        }
@@ -370,7 +374,7 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (ret)
                goto err;
        if (val & 0x1) {
-               dev_err(printdev(devrec), "Error Sending. Retry count exceeded\n");
+               dev_dbg(printdev(devrec), "Error Sending. Retry count exceeded\n");
                ret = -ECOMM; /* TODO: Better error code ? */
        } else
                dev_dbg(printdev(devrec), "Packet Sent\n");
@@ -477,7 +481,7 @@ static int mrf24j40_filter(struct ieee802154_dev *dev,
                int i;
                for (i = 0; i < 8; i++)
                        write_short_reg(devrec, REG_EADR0+i,
-                                       filt->ieee_addr[i]);
+                                       filt->ieee_addr[7-i]);
 
 #ifdef DEBUG
                printk(KERN_DEBUG "Set long addr to: ");
@@ -623,6 +627,7 @@ static int mrf24j40_probe(struct spi_device *spi)
        int ret = -ENOMEM;
        u8 val;
        struct mrf24j40 *devrec;
+       struct pinctrl *pinctrl;
 
        printk(KERN_INFO "mrf24j40: probe(). IRQ: %d\n", spi->irq);
 
@@ -633,6 +638,11 @@ static int mrf24j40_probe(struct spi_device *spi)
        if (!devrec->buf)
                goto err_buf;
 
+       pinctrl = devm_pinctrl_get_select_default(&spi->dev);
+       if (IS_ERR(pinctrl))
+               dev_warn(&spi->dev,
+                       "pinctrl pins are not configured from the driver");
+
        spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */
        if (spi->max_speed_hz > MAX_SPI_SPEED_HZ)
                spi->max_speed_hz = MAX_SPI_SPEED_HZ;
@@ -641,7 +651,7 @@ static int mrf24j40_probe(struct spi_device *spi)
        init_completion(&devrec->tx_complete);
        INIT_WORK(&devrec->irqwork, mrf24j40_isrwork);
        devrec->spi = spi;
-       dev_set_drvdata(&spi->dev, devrec);
+       spi_set_drvdata(spi, devrec);
 
        /* Register with the 802154 subsystem */
 
@@ -713,7 +723,7 @@ err_devrec:
 
 static int mrf24j40_remove(struct spi_device *spi)
 {
-       struct mrf24j40 *devrec = dev_get_drvdata(&spi->dev);
+       struct mrf24j40 *devrec = spi_get_drvdata(spi);
 
        dev_dbg(printdev(devrec), "remove\n");
 
@@ -725,7 +735,7 @@ static int mrf24j40_remove(struct spi_device *spi)
         * complete? */
 
        /* Clean up the SPI stuff. */
-       dev_set_drvdata(&spi->dev, NULL);
+       spi_set_drvdata(spi, NULL);
        kfree(devrec->buf);
        kfree(devrec);
        return 0;
@@ -749,18 +759,7 @@ static struct spi_driver mrf24j40_driver = {
        .remove = mrf24j40_remove,
 };
 
-static int __init mrf24j40_init(void)
-{
-       return spi_register_driver(&mrf24j40_driver);
-}
-
-static void __exit mrf24j40_exit(void)
-{
-       spi_unregister_driver(&mrf24j40_driver);
-}
-
-module_init(mrf24j40_init);
-module_exit(mrf24j40_exit);
+module_spi_driver(mrf24j40_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Alan Ott");
index 82164381f778fb604b07f5cc2956a16e76d3f563..dc9f6a45515dacaa76fabb419ed82daa89d9a3d3 100644 (file)
@@ -166,7 +166,8 @@ static const struct net_device_ops ifb_netdev_ops = {
 
 #define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG  | NETIF_F_FRAGLIST | \
                      NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6      | \
-                     NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_TX)
+                     NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX         | \
+                     NETIF_F_HW_VLAN_STAG_TX)
 
 static void ifb_setup(struct net_device *dev)
 {
index 9cea451a60818428bfbdcc08e16dab1a09a89bb2..3adb43ce138fa1b8d3bda8a31d25f14f3aa1c696 100644 (file)
@@ -352,21 +352,19 @@ static int ali_ircc_open(int i, chipio_t *info)
        /* Allocate memory if needed */
        self->rx_buff.head =
                dma_alloc_coherent(NULL, self->rx_buff.truesize,
-                                  &self->rx_buff_dma, GFP_KERNEL);
+                                  &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO);
        if (self->rx_buff.head == NULL) {
                err = -ENOMEM;
                goto err_out2;
        }
-       memset(self->rx_buff.head, 0, self->rx_buff.truesize);
        
        self->tx_buff.head =
                dma_alloc_coherent(NULL, self->tx_buff.truesize,
-                                  &self->tx_buff_dma, GFP_KERNEL);
+                                  &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO);
        if (self->tx_buff.head == NULL) {
                err = -ENOMEM;
                goto err_out3;
        }
-       memset(self->tx_buff.head, 0, self->tx_buff.truesize);
 
        self->rx_buff.in_frame = FALSE;
        self->rx_buff.state = OUTSIDE_FRAME;
index b5151e4ced612b0847abe72327d6f483d13757fe..7a1f684edcb597b5cdd499d54517708f598a1e7a 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/types.h>
+#include <linux/ioport.h>
 
 #include <net/irda/irda.h>
 #include <net/irda/irmod.h>
@@ -882,12 +883,12 @@ static int au1k_irda_probe(struct platform_device *pdev)
                goto out;
 
        err = -EBUSY;
-       aup->ioarea = request_mem_region(r->start, r->end - r->start + 1,
+       aup->ioarea = request_mem_region(r->start, resource_size(r),
                                         pdev->name);
        if (!aup->ioarea)
                goto out;
 
-       aup->iobase = ioremap_nocache(r->start, r->end - r->start + 1);
+       aup->iobase = ioremap_nocache(r->start, resource_size(r));
        if (!aup->iobase)
                goto out2;
 
@@ -952,18 +953,7 @@ static struct platform_driver au1k_irda_driver = {
        .remove         = au1k_irda_remove,
 };
 
-static int __init au1k_irda_load(void)
-{
-       return platform_driver_register(&au1k_irda_driver);
-}
-
-static void __exit au1k_irda_unload(void)
-{
-       return platform_driver_unregister(&au1k_irda_driver);
-}
+module_platform_driver(au1k_irda_driver);
 
 MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>");
 MODULE_DESCRIPTION("Au1000 IrDA Device Driver");
-
-module_init(au1k_irda_load);
-module_exit(au1k_irda_unload);
index fed4a05d55c77bfd520494b5af8fcaaaffbcce39..a06fca61c9a08f308cb0ef82f1d086cf59d0ad33 100644 (file)
@@ -389,7 +389,8 @@ static int bfin_sir_startup(struct bfin_sir_port *port, struct net_device *dev)
        set_dma_callback(port->rx_dma_channel, bfin_sir_dma_rx_int, dev);
        set_dma_callback(port->tx_dma_channel, bfin_sir_dma_tx_int, dev);
 
-       port->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &dma_handle, GFP_DMA);
+       port->rx_dma_buf.buf = dma_alloc_coherent(NULL, PAGE_SIZE,
+                                                 &dma_handle, GFP_DMA);
        port->rx_dma_buf.head = 0;
        port->rx_dma_buf.tail = 0;
        port->rx_dma_nrows = 0;
index 2a4f2f153244fd86e3991a290cd98a2cbaefd3da..9cf836b57c4920c40a1455514b0158ecb584f509 100644 (file)
@@ -431,22 +431,20 @@ static int __init nsc_ircc_open(chipio_t *info)
        /* Allocate memory if needed */
        self->rx_buff.head =
                dma_alloc_coherent(NULL, self->rx_buff.truesize,
-                                  &self->rx_buff_dma, GFP_KERNEL);
+                                  &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO);
        if (self->rx_buff.head == NULL) {
                err = -ENOMEM;
                goto out2;
 
        }
-       memset(self->rx_buff.head, 0, self->rx_buff.truesize);
        
        self->tx_buff.head =
                dma_alloc_coherent(NULL, self->tx_buff.truesize,
-                                  &self->tx_buff_dma, GFP_KERNEL);
+                                  &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO);
        if (self->tx_buff.head == NULL) {
                err = -ENOMEM;
                goto out3;
        }
-       memset(self->tx_buff.head, 0, self->tx_buff.truesize);
 
        self->rx_buff.in_frame = FALSE;
        self->rx_buff.state = OUTSIDE_FRAME;
index 858de05bdb7dc171012374ac02998c1fd111349c..964b116a0ab79ad79120c4d537cdb5701e000152 100644 (file)
@@ -700,12 +700,12 @@ static int pxa_irda_start(struct net_device *dev)
 
        err = -ENOMEM;
        si->dma_rx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT,
-                                            &si->dma_rx_buff_phy, GFP_KERNEL );
+                                            &si->dma_rx_buff_phy, GFP_KERNEL);
        if (!si->dma_rx_buff)
                goto err_dma_rx_buff;
 
        si->dma_tx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT,
-                                            &si->dma_tx_buff_phy, GFP_KERNEL );
+                                            &si->dma_tx_buff_phy, GFP_KERNEL);
        if (!si->dma_tx_buff)
                goto err_dma_tx_buff;
 
index 5290952b60c25235f13f308f5041b68795083971..aa05dad75335f4cbbd986758a4230ad5050d49c4 100644 (file)
@@ -563,24 +563,15 @@ static int smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma,
 
        self->rx_buff.head =
                dma_alloc_coherent(NULL, self->rx_buff.truesize,
-                                  &self->rx_buff_dma, GFP_KERNEL);
-       if (self->rx_buff.head == NULL) {
-               IRDA_ERROR("%s, Can't allocate memory for receive buffer!\n",
-                          driver_name);
+                                  &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO);
+       if (self->rx_buff.head == NULL)
                goto err_out2;
-       }
 
        self->tx_buff.head =
                dma_alloc_coherent(NULL, self->tx_buff.truesize,
-                                  &self->tx_buff_dma, GFP_KERNEL);
-       if (self->tx_buff.head == NULL) {
-               IRDA_ERROR("%s, Can't allocate memory for transmit buffer!\n",
-                          driver_name);
+                                  &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO);
+       if (self->tx_buff.head == NULL)
                goto err_out3;
-       }
-
-       memset(self->rx_buff.head, 0, self->rx_buff.truesize);
-       memset(self->tx_buff.head, 0, self->tx_buff.truesize);
 
        self->rx_buff.in_frame = FALSE;
        self->rx_buff.state = OUTSIDE_FRAME;
index f9033c6a888c8ab7f14a4c1db884d35e14b42923..51f2bc37610188b44de5b8546062d16142571505 100644 (file)
@@ -364,21 +364,19 @@ static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, unsigned int id)
        /* Allocate memory if needed */
        self->rx_buff.head =
                dma_alloc_coherent(&pdev->dev, self->rx_buff.truesize,
-                                  &self->rx_buff_dma, GFP_KERNEL);
+                                  &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO);
        if (self->rx_buff.head == NULL) {
                err = -ENOMEM;
                goto err_out2;
        }
-       memset(self->rx_buff.head, 0, self->rx_buff.truesize);
 
        self->tx_buff.head =
                dma_alloc_coherent(&pdev->dev, self->tx_buff.truesize,
-                                  &self->tx_buff_dma, GFP_KERNEL);
+                                  &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO);
        if (self->tx_buff.head == NULL) {
                err = -ENOMEM;
                goto err_out3;
        }
-       memset(self->tx_buff.head, 0, self->tx_buff.truesize);
 
        self->rx_buff.in_frame = FALSE;
        self->rx_buff.state = OUTSIDE_FRAME;
index f5bb92f15880dffb947f387577460451bb774068..bb8857a158a61efb8f5db3e0473899cf52855448 100644 (file)
@@ -216,22 +216,19 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq,
        /* Allocate memory if needed */
        self->rx_buff.head =
                dma_alloc_coherent(NULL, self->rx_buff.truesize,
-                                  &self->rx_buff_dma, GFP_KERNEL);
+                                  &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO);
        if (self->rx_buff.head == NULL) {
                err = -ENOMEM;
                goto err_out1;
        }
 
-       memset(self->rx_buff.head, 0, self->rx_buff.truesize);
-       
        self->tx_buff.head =
                dma_alloc_coherent(NULL, self->tx_buff.truesize,
-                                  &self->tx_buff_dma, GFP_KERNEL);
+                                  &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO);
        if (self->tx_buff.head == NULL) {
                err = -ENOMEM;
                goto err_out2;
        }
-       memset(self->tx_buff.head, 0, self->tx_buff.truesize);
 
        self->rx_buff.in_frame = FALSE;
        self->rx_buff.state = OUTSIDE_FRAME;
index 73abbc1655d5c7b793601f93027dad37d2923150..d5a141c7c4e786b06746c73668407c110e74611e 100644 (file)
@@ -46,9 +46,16 @@ struct macvlan_port {
 
 static void macvlan_port_destroy(struct net_device *dev);
 
-#define macvlan_port_get_rcu(dev) \
-       ((struct macvlan_port *) rcu_dereference(dev->rx_handler_data))
-#define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data)
+static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev)
+{
+       return rcu_dereference(dev->rx_handler_data);
+}
+
+static struct macvlan_port *macvlan_port_get_rtnl(const struct net_device *dev)
+{
+       return rtnl_dereference(dev->rx_handler_data);
+}
+
 #define macvlan_port_exists(dev) (dev->priv_flags & IFF_MACVLAN_PORT)
 
 static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port,
@@ -464,7 +471,7 @@ static struct lock_class_key macvlan_netdev_addr_lock_key;
        (NETIF_F_SG | NETIF_F_ALL_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_FILTER)
+        NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER)
 
 #define MACVLAN_STATE_MASK \
        ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT))
@@ -560,21 +567,21 @@ static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev,
 }
 
 static int macvlan_vlan_rx_add_vid(struct net_device *dev,
-                                   unsigned short vid)
+                                  __be16 proto, u16 vid)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct net_device *lowerdev = vlan->lowerdev;
 
-       return vlan_vid_add(lowerdev, vid);
+       return vlan_vid_add(lowerdev, proto, vid);
 }
 
 static int macvlan_vlan_rx_kill_vid(struct net_device *dev,
-                                    unsigned short vid)
+                                   __be16 proto, u16 vid)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct net_device *lowerdev = vlan->lowerdev;
 
-       vlan_vid_del(lowerdev, vid);
+       vlan_vid_del(lowerdev, proto, vid);
        return 0;
 }
 
@@ -703,7 +710,7 @@ static int macvlan_port_create(struct net_device *dev)
 
 static void macvlan_port_destroy(struct net_device *dev)
 {
-       struct macvlan_port *port = macvlan_port_get(dev);
+       struct macvlan_port *port = macvlan_port_get_rtnl(dev);
 
        dev->priv_flags &= ~IFF_MACVLAN_PORT;
        netdev_rx_handler_unregister(dev);
@@ -772,7 +779,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
                if (err < 0)
                        return err;
        }
-       port = macvlan_port_get(lowerdev);
+       port = macvlan_port_get_rtnl(lowerdev);
 
        /* Only 1 macvlan device can be created in passthru mode */
        if (port->passthru)
@@ -921,7 +928,7 @@ static int macvlan_device_event(struct notifier_block *unused,
        if (!macvlan_port_exists(dev))
                return NOTIFY_DONE;
 
-       port = macvlan_port_get(dev);
+       port = macvlan_port_get_rtnl(dev);
 
        switch (event) {
        case NETDEV_CHANGE:
index a449439bd65366e5790037c21a4d3ce3f0e1ce97..59e9605de316809a702951a5c6de3dbee3a43a69 100644 (file)
@@ -725,6 +725,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
                        goto err_kfree;
        }
 
+       skb_probe_transport_header(skb, ETH_HLEN);
+
        rcu_read_lock_bh();
        vlan = rcu_dereference_bh(q->vlan);
        /* copy skb_ubuf_info for callback when skb has no error */
index ec40ba882f612dba77a72b0a4a9e5689c7898ce5..ff2e45e9cb54c0505d163ec93f07566f2ae58ab8 100644 (file)
@@ -159,7 +159,7 @@ static int lxt973a2_update_link(struct phy_device *phydev)
        return 0;
 }
 
-int lxt973a2_read_status(struct phy_device *phydev)
+static int lxt973a2_read_status(struct phy_device *phydev)
 {
        int adv;
        int err;
index 22dec9c7ef05d7480e87b575605e2240f4476a20..202fe1ff1987950effe6e0a0426894a0ad8dea78 100644 (file)
@@ -7,6 +7,8 @@
  *
  * Copyright (c) 2004 Freescale Semiconductor, Inc.
  *
+ * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de>
+ *
  * 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
 #define MII_88E1318S_PHY_MSCR1_REG     16
 #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6)
 
+/* Copper Specific Interrupt Enable Register */
+#define MII_88E1318S_PHY_CSIER                              0x12
+/* WOL Event Interrupt Enable */
+#define MII_88E1318S_PHY_CSIER_WOL_EIE                      BIT(7)
+
+/* LED Timer Control Register */
+#define MII_88E1318S_PHY_LED_PAGE                           0x03
+#define MII_88E1318S_PHY_LED_TCR                            0x12
+#define MII_88E1318S_PHY_LED_TCR_FORCE_INT                  BIT(15)
+#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE                BIT(7)
+#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW             BIT(11)
+
+/* Magic Packet MAC address registers */
+#define MII_88E1318S_PHY_MAGIC_PACKET_WORD2                 0x17
+#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1                 0x18
+#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0                 0x19
+
+#define MII_88E1318S_PHY_WOL_PAGE                           0x11
+#define MII_88E1318S_PHY_WOL_CTRL                           0x10
+#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS          BIT(12)
+#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14)
+
 #define MII_88E1121_PHY_LED_CTRL       16
 #define MII_88E1121_PHY_LED_PAGE       3
 #define MII_88E1121_PHY_LED_DEF                0x0030
@@ -696,6 +720,107 @@ static int m88e1121_did_interrupt(struct phy_device *phydev)
        return 0;
 }
 
+static void m88e1318_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+{
+       wol->supported = WAKE_MAGIC;
+       wol->wolopts = 0;
+
+       if (phy_write(phydev, MII_MARVELL_PHY_PAGE,
+                     MII_88E1318S_PHY_WOL_PAGE) < 0)
+               return;
+
+       if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) &
+           MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
+               wol->wolopts |= WAKE_MAGIC;
+
+       if (phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00) < 0)
+               return;
+}
+
+static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+{
+       int err, oldpage, temp;
+
+       oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
+
+       if (wol->wolopts & WAKE_MAGIC) {
+               /* Explicitly switch to page 0x00, just to be sure */
+               err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00);
+               if (err < 0)
+                       return err;
+
+               /* Enable the WOL interrupt */
+               temp = phy_read(phydev, MII_88E1318S_PHY_CSIER);
+               temp |= MII_88E1318S_PHY_CSIER_WOL_EIE;
+               err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp);
+               if (err < 0)
+                       return err;
+
+               err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
+                               MII_88E1318S_PHY_LED_PAGE);
+               if (err < 0)
+                       return err;
+
+               /* Setup LED[2] as interrupt pin (active low) */
+               temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR);
+               temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT;
+               temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE;
+               temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW;
+               err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp);
+               if (err < 0)
+                       return err;
+
+               err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
+                               MII_88E1318S_PHY_WOL_PAGE);
+               if (err < 0)
+                       return err;
+
+               /* Store the device address for the magic packet */
+               err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
+                               ((phydev->attached_dev->dev_addr[5] << 8) |
+                                phydev->attached_dev->dev_addr[4]));
+               if (err < 0)
+                       return err;
+               err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
+                               ((phydev->attached_dev->dev_addr[3] << 8) |
+                                phydev->attached_dev->dev_addr[2]));
+               if (err < 0)
+                       return err;
+               err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
+                               ((phydev->attached_dev->dev_addr[1] << 8) |
+                                phydev->attached_dev->dev_addr[0]));
+               if (err < 0)
+                       return err;
+
+               /* Clear WOL status and enable magic packet matching */
+               temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
+               temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
+               temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
+               err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
+               if (err < 0)
+                       return err;
+       } else {
+               err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
+                               MII_88E1318S_PHY_WOL_PAGE);
+               if (err < 0)
+                       return err;
+
+               /* Clear WOL status and disable magic packet matching */
+               temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
+               temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
+               temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
+               err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
+               if (err < 0)
+                       return err;
+       }
+
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
 static struct phy_driver marvell_drivers[] = {
        {
                .phy_id = MARVELL_PHY_ID_88E1101,
@@ -772,6 +897,8 @@ static struct phy_driver marvell_drivers[] = {
                .ack_interrupt = &marvell_ack_interrupt,
                .config_intr = &marvell_config_intr,
                .did_interrupt = &m88e1121_did_interrupt,
+               .get_wol = &m88e1318_get_wol,
+               .set_wol = &m88e1318_set_wol,
                .driver = { .owner = THIS_MODULE },
        },
        {
index 27274986ab56eb7580788bd376f1b3893388d173..a47f9236d9663cf4f6c0ec652be979e05ccd45c1 100644 (file)
@@ -235,17 +235,7 @@ static struct platform_driver mdio_gpio_driver = {
        },
 };
 
-static int __init mdio_gpio_init(void)
-{
-       return platform_driver_register(&mdio_gpio_driver);
-}
-module_init(mdio_gpio_init);
-
-static void __exit mdio_gpio_exit(void)
-{
-       platform_driver_unregister(&mdio_gpio_driver);
-}
-module_exit(mdio_gpio_exit);
+module_platform_driver(mdio_gpio_driver);
 
 MODULE_ALIAS("platform:mdio-gpio");
 MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas");
index 09297fe05ae5fd6e1d666d500ac06b6ec4d9a3fc..b51fa1f469b0ad7a664b0f39b575464a6b9ccf3b 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2009,2011 Cavium, Inc.
+ * Copyright (C) 2009-2012 Cavium, Inc.
  */
 
 #include <linux/platform_device.h>
 #define SMI_CLK                0x18
 #define SMI_EN         0x20
 
+enum octeon_mdiobus_mode {
+       UNINIT = 0,
+       C22,
+       C45
+};
+
 struct octeon_mdiobus {
        struct mii_bus *mii_bus;
        u64 register_base;
        resource_size_t mdio_phys;
        resource_size_t regsize;
+       enum octeon_mdiobus_mode mode;
        int phy_irq[PHY_MAX_ADDR];
 };
 
+static void octeon_mdiobus_set_mode(struct octeon_mdiobus *p,
+                                   enum octeon_mdiobus_mode m)
+{
+       union cvmx_smix_clk smi_clk;
+
+       if (m == p->mode)
+               return;
+
+       smi_clk.u64 = cvmx_read_csr(p->register_base + SMI_CLK);
+       smi_clk.s.mode = (m == C45) ? 1 : 0;
+       smi_clk.s.preamble = 1;
+       cvmx_write_csr(p->register_base + SMI_CLK, smi_clk.u64);
+       p->mode = m;
+}
+
+static int octeon_mdiobus_c45_addr(struct octeon_mdiobus *p,
+                                  int phy_id, int regnum)
+{
+       union cvmx_smix_cmd smi_cmd;
+       union cvmx_smix_wr_dat smi_wr;
+       int timeout = 1000;
+
+       octeon_mdiobus_set_mode(p, C45);
+
+       smi_wr.u64 = 0;
+       smi_wr.s.dat = regnum & 0xffff;
+       cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64);
+
+       regnum = (regnum >> 16) & 0x1f;
+
+       smi_cmd.u64 = 0;
+       smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
+       smi_cmd.s.phy_adr = phy_id;
+       smi_cmd.s.reg_adr = regnum;
+       cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
+
+       do {
+               /* Wait 1000 clocks so we don't saturate the RSL bus
+                * doing reads.
+                */
+               __delay(1000);
+               smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT);
+       } while (smi_wr.s.pending && --timeout);
+
+       if (timeout <= 0)
+               return -EIO;
+       return 0;
+}
+
 static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
 {
        struct octeon_mdiobus *p = bus->priv;
        union cvmx_smix_cmd smi_cmd;
        union cvmx_smix_rd_dat smi_rd;
+       unsigned int op = 1; /* MDIO_CLAUSE_22_READ */
        int timeout = 1000;
 
+       if (regnum & MII_ADDR_C45) {
+               int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
+               if (r < 0)
+                       return r;
+
+               regnum = (regnum >> 16) & 0x1f;
+               op = 3; /* MDIO_CLAUSE_45_READ */
+       } else {
+               octeon_mdiobus_set_mode(p, C22);
+       }
+
+
        smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */
+       smi_cmd.s.phy_op = op;
        smi_cmd.s.phy_adr = phy_id;
        smi_cmd.s.reg_adr = regnum;
        cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
 
        do {
-               /*
-                * Wait 1000 clocks so we don't saturate the RSL bus
+               /* Wait 1000 clocks so we don't saturate the RSL bus
                 * doing reads.
                 */
                __delay(1000);
@@ -69,21 +137,33 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
        struct octeon_mdiobus *p = bus->priv;
        union cvmx_smix_cmd smi_cmd;
        union cvmx_smix_wr_dat smi_wr;
+       unsigned int op = 0; /* MDIO_CLAUSE_22_WRITE */
        int timeout = 1000;
 
+
+       if (regnum & MII_ADDR_C45) {
+               int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
+               if (r < 0)
+                       return r;
+
+               regnum = (regnum >> 16) & 0x1f;
+               op = 1; /* MDIO_CLAUSE_45_WRITE */
+       } else {
+               octeon_mdiobus_set_mode(p, C22);
+       }
+
        smi_wr.u64 = 0;
        smi_wr.s.dat = val;
        cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64);
 
        smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */
+       smi_cmd.s.phy_op = op;
        smi_cmd.s.phy_adr = phy_id;
        smi_cmd.s.reg_adr = regnum;
        cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
 
        do {
-               /*
-                * Wait 1000 clocks so we don't saturate the RSL bus
+               /* Wait 1000 clocks so we don't saturate the RSL bus
                 * doing reads.
                 */
                __delay(1000);
@@ -197,18 +277,7 @@ void octeon_mdiobus_force_mod_depencency(void)
 }
 EXPORT_SYMBOL(octeon_mdiobus_force_mod_depencency);
 
-static int __init octeon_mdiobus_mod_init(void)
-{
-       return platform_driver_register(&octeon_mdiobus_driver);
-}
-
-static void __exit octeon_mdiobus_mod_exit(void)
-{
-       platform_driver_unregister(&octeon_mdiobus_driver);
-}
-
-module_init(octeon_mdiobus_mod_init);
-module_exit(octeon_mdiobus_mod_exit);
+module_platform_driver(octeon_mdiobus_driver);
 
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
 MODULE_VERSION(DRV_VERSION);
index abf7b6153d00b8527b997e4e947b48a63449af4b..2510435f34edfd5e10cc4252c6e7dcfa35e95e62 100644 (file)
 #define KS8737_CTRL_INT_ACTIVE_HIGH            (1 << 14)
 #define KSZ8051_RMII_50MHZ_CLK                 (1 << 7)
 
+static int ksz_config_flags(struct phy_device *phydev)
+{
+       int regval;
+
+       if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) {
+               regval = phy_read(phydev, MII_KSZPHY_CTRL);
+               regval |= KSZ8051_RMII_50MHZ_CLK;
+               return phy_write(phydev, MII_KSZPHY_CTRL, regval);
+       }
+       return 0;
+}
+
 static int kszphy_ack_interrupt(struct phy_device *phydev)
 {
        /* bit[7..0] int status, which is a read and clear register. */
@@ -114,22 +126,19 @@ static int kszphy_config_init(struct phy_device *phydev)
 
 static int ksz8021_config_init(struct phy_device *phydev)
 {
+       int rc;
        const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
        phy_write(phydev, MII_KSZPHY_OMSO, val);
-       return 0;
+       rc = ksz_config_flags(phydev);
+       return rc < 0 ? rc : 0;
 }
 
 static int ks8051_config_init(struct phy_device *phydev)
 {
-       int regval;
-
-       if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) {
-               regval = phy_read(phydev, MII_KSZPHY_CTRL);
-               regval |= KSZ8051_RMII_50MHZ_CLK;
-               phy_write(phydev, MII_KSZPHY_CTRL, regval);
-       }
+       int rc;
 
-       return 0;
+       rc = ksz_config_flags(phydev);
+       return rc < 0 ? rc : 0;
 }
 
 #define KSZ8873MLL_GLOBAL_CONTROL_4    0x06
@@ -191,6 +200,19 @@ static struct phy_driver ksphy_driver[] = {
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
        .driver         = { .owner = THIS_MODULE,},
+}, {
+       .phy_id         = PHY_ID_KSZ8031,
+       .phy_id_mask    = 0x00ffffff,
+       .name           = "Micrel KSZ8031",
+       .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+                          SUPPORTED_Asym_Pause),
+       .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+       .config_init    = ksz8021_config_init,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
+       .ack_interrupt  = kszphy_ack_interrupt,
+       .config_intr    = kszphy_config_intr,
+       .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8041,
        .phy_id_mask    = 0x00fffff0,
@@ -325,6 +347,7 @@ static struct mdio_device_id __maybe_unused micrel_tbl[] = {
        { PHY_ID_KSZ8001, 0x00ffffff },
        { PHY_ID_KS8737, 0x00fffff0 },
        { PHY_ID_KSZ8021, 0x00ffffff },
+       { PHY_ID_KSZ8031, 0x00ffffff },
        { PHY_ID_KSZ8041, 0x00fffff0 },
        { PHY_ID_KSZ8051, 0x00fffff0 },
        { PHY_ID_KSZ8061, 0x00fffff0 },
index ef9ea924822349217f536ad41d124a791aecd2fb..c14f14741b3f7a8c4a6634d2f8b4b0b9c254d5df 100644 (file)
@@ -462,33 +462,6 @@ void phy_stop_machine(struct phy_device *phydev)
        phydev->adjust_state = NULL;
 }
 
-/**
- * phy_force_reduction - reduce PHY speed/duplex settings by one step
- * @phydev: target phy_device struct
- *
- * Description: Reduces the speed/duplex settings by one notch,
- *   in this order--
- *   1000/FULL, 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF.
- *   The function bottoms out at 10/HALF.
- */
-static void phy_force_reduction(struct phy_device *phydev)
-{
-       int idx;
-
-       idx = phy_find_setting(phydev->speed, phydev->duplex);
-       
-       idx++;
-
-       idx = phy_find_valid(idx, phydev->supported);
-
-       phydev->speed = settings[idx].speed;
-       phydev->duplex = settings[idx].duplex;
-
-       pr_info("Trying %d/%s\n",
-               phydev->speed, DUPLEX_FULL == phydev->duplex ? "FULL" : "HALF");
-}
-
-
 /**
  * phy_error - enter HALTED state for this PHY device
  * @phydev: target phy_device struct
@@ -818,30 +791,11 @@ void phy_state_machine(struct work_struct *work)
                                phydev->adjust_link(phydev->attached_dev);
 
                        } else if (0 == phydev->link_timeout--) {
-                               int idx;
-
                                needs_aneg = 1;
                                /* If we have the magic_aneg bit,
                                 * we try again */
                                if (phydev->drv->flags & PHY_HAS_MAGICANEG)
                                        break;
-
-                               /* The timer expired, and we still
-                                * don't have a setting, so we try
-                                * forcing it until we find one that
-                                * works, starting from the fastest speed,
-                                * and working our way down */
-                               idx = phy_find_valid(0, phydev->supported);
-
-                               phydev->speed = settings[idx].speed;
-                               phydev->duplex = settings[idx].duplex;
-
-                               phydev->autoneg = AUTONEG_DISABLE;
-
-                               pr_info("Trying %d/%s\n",
-                                       phydev->speed,
-                                       DUPLEX_FULL == phydev->duplex ?
-                                       "FULL" : "HALF");
                        }
                        break;
                case PHY_NOLINK:
@@ -866,10 +820,8 @@ void phy_state_machine(struct work_struct *work)
                                phydev->state = PHY_RUNNING;
                                netif_carrier_on(phydev->attached_dev);
                        } else {
-                               if (0 == phydev->link_timeout--) {
-                                       phy_force_reduction(phydev);
+                               if (0 == phydev->link_timeout--)
                                        needs_aneg = 1;
-                               }
                        }
 
                        phydev->adjust_link(phydev->attached_dev);
@@ -1188,3 +1140,19 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
        return 0;
 }
 EXPORT_SYMBOL(phy_ethtool_set_eee);
+
+int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+{
+       if (phydev->drv->set_wol)
+               return phydev->drv->set_wol(phydev, wol);
+
+       return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(phy_ethtool_set_wol);
+
+void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+{
+       if (phydev->drv->get_wol)
+               phydev->drv->get_wol(phydev, wol);
+}
+EXPORT_SYMBOL(phy_ethtool_get_wol);
index 5c87eef40bf9786ee27fae3980a33b6b0d0dfd1b..d11c93e69e03fafac4efee44cad6c47e55380dda 100644 (file)
@@ -281,7 +281,7 @@ static int ks8995_probe(struct spi_device *spi)
        mutex_init(&ks->lock);
        ks->pdata = pdata;
        ks->spi = spi_dev_get(spi);
-       dev_set_drvdata(&spi->dev, ks);
+       spi_set_drvdata(spi, ks);
 
        spi->mode = SPI_MODE_0;
        spi->bits_per_word = 8;
@@ -325,7 +325,7 @@ static int ks8995_probe(struct spi_device *spi)
        return 0;
 
 err_drvdata:
-       dev_set_drvdata(&spi->dev, NULL);
+       spi_set_drvdata(spi, NULL);
        kfree(ks);
        return err;
 }
@@ -334,10 +334,10 @@ static int ks8995_remove(struct spi_device *spi)
 {
        struct ks8995_data      *ks8995;
 
-       ks8995 = dev_get_drvdata(&spi->dev);
+       ks8995 = spi_get_drvdata(spi);
        sysfs_remove_bin_file(&spi->dev.kobj, &ks8995_registers_attr);
 
-       dev_set_drvdata(&spi->dev, NULL);
+       spi_set_drvdata(spi, NULL);
        kfree(ks8995);
 
        return 0;
index 2585c383e623550e5142288e898812ba6aadd27c..3492b53912731eae9407fe6dbb15468d35429263 100644 (file)
@@ -61,7 +61,7 @@ MODULE_DESCRIPTION("Vitesse PHY driver");
 MODULE_AUTHOR("Kriston Carson");
 MODULE_LICENSE("GPL");
 
-int vsc824x_add_skew(struct phy_device *phydev)
+static int vsc824x_add_skew(struct phy_device *phydev)
 {
        int err;
        int extcon;
@@ -81,7 +81,6 @@ int vsc824x_add_skew(struct phy_device *phydev)
 
        return err;
 }
-EXPORT_SYMBOL(vsc824x_add_skew);
 
 static int vsc824x_config_init(struct phy_device *phydev)
 {
index bed62d9c53c868ec607fa37ffc4b8697a1ca61a0..1f7bef90b46757a6ce2485c9aefd5d197464b7b1 100644 (file)
@@ -560,7 +560,7 @@ static __be16 plip_type_trans(struct sk_buff *skb, struct net_device *dev)
         *      so don't forget to remove it.
         */
 
-       if (ntohs(eth->h_proto) >= 1536)
+       if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
                return eth->h_proto;
 
        rawp = skb->data;
index 1a12033d2efafa83d2d118ead1a80262f961a441..090c834d7dbd2ebee45136a4c3266c9d06e7e375 100644 (file)
@@ -105,64 +105,15 @@ static const struct ppp_channel_ops sync_ops = {
 };
 
 /*
- * Utility procedures to print a buffer in hex/ascii
+ * Utility procedure to print a buffer in hex/ascii
  */
-static void
-ppp_print_hex (register __u8 * out, const __u8 * in, int count)
-{
-       register __u8 next_ch;
-       static const char hex[] = "0123456789ABCDEF";
-
-       while (count-- > 0) {
-               next_ch = *in++;
-               *out++ = hex[(next_ch >> 4) & 0x0F];
-               *out++ = hex[next_ch & 0x0F];
-               ++out;
-       }
-}
-
-static void
-ppp_print_char (register __u8 * out, const __u8 * in, int count)
-{
-       register __u8 next_ch;
-
-       while (count-- > 0) {
-               next_ch = *in++;
-
-               if (next_ch < 0x20 || next_ch > 0x7e)
-                       *out++ = '.';
-               else {
-                       *out++ = next_ch;
-                       if (next_ch == '%')   /* printk/syslogd has a bug !! */
-                               *out++ = '%';
-               }
-       }
-       *out = '\0';
-}
-
 static void
 ppp_print_buffer (const char *name, const __u8 *buf, int count)
 {
-       __u8 line[44];
-
        if (name != NULL)
                printk(KERN_DEBUG "ppp_synctty: %s, count = %d\n", name, count);
 
-       while (count > 8) {
-               memset (line, 32, 44);
-               ppp_print_hex (line, buf, 8);
-               ppp_print_char (&line[8 * 3], buf, 8);
-               printk(KERN_DEBUG "%s\n", line);
-               count -= 8;
-               buf += 8;
-       }
-
-       if (count > 0) {
-               memset (line, 32, 44);
-               ppp_print_hex (line, buf, count);
-               ppp_print_char (&line[8 * 3], buf, count);
-               printk(KERN_DEBUG "%s\n", line);
-       }
+       print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, count);
 }
 
 
index c3011af68e91c74787b3131d0919c2a4a8593e11..c853d84fd99fc89cae37398b65a945684ac7c9d6 100644 (file)
@@ -37,6 +37,18 @@ config NET_TEAM_MODE_ROUNDROBIN
          To compile this team mode as a module, choose M here: the module
          will be called team_mode_roundrobin.
 
+config NET_TEAM_MODE_RANDOM
+       tristate "Random mode support"
+       depends on NET_TEAM
+       ---help---
+         Basic mode where port used for transmitting packets is selected
+         randomly.
+
+         All added ports are setup to have team's device address.
+
+         To compile this team mode as a module, choose M here: the module
+         will be called team_mode_random.
+
 config NET_TEAM_MODE_ACTIVEBACKUP
        tristate "Active-backup mode support"
        depends on NET_TEAM
index 975763014e5a36ebff0a5838a9657b43ac3941f9..c57e85889751d06964ea0d9b13c651c8861620a7 100644 (file)
@@ -5,5 +5,6 @@
 obj-$(CONFIG_NET_TEAM) += team.o
 obj-$(CONFIG_NET_TEAM_MODE_BROADCAST) += team_mode_broadcast.o
 obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
+obj-$(CONFIG_NET_TEAM_MODE_RANDOM) += team_mode_random.o
 obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o
 obj-$(CONFIG_NET_TEAM_MODE_LOADBALANCE) += team_mode_loadbalance.o
index bf3419297875b102da599162c0852ecfd9eb71eb..7c43261975bd1d57e93a4b35382e42c92891cfb1 100644 (file)
@@ -73,11 +73,24 @@ static int team_port_set_orig_dev_addr(struct team_port *port)
        return __set_port_dev_addr(port->dev, port->orig.dev_addr);
 }
 
-int team_port_set_team_dev_addr(struct team_port *port)
+static int team_port_set_team_dev_addr(struct team *team,
+                                      struct team_port *port)
+{
+       return __set_port_dev_addr(port->dev, team->dev->dev_addr);
+}
+
+int team_modeop_port_enter(struct team *team, struct team_port *port)
+{
+       return team_port_set_team_dev_addr(team, port);
+}
+EXPORT_SYMBOL(team_modeop_port_enter);
+
+void team_modeop_port_change_dev_addr(struct team *team,
+                                     struct team_port *port)
 {
-       return __set_port_dev_addr(port->dev, port->team->dev->dev_addr);
+       team_port_set_team_dev_addr(team, port);
 }
-EXPORT_SYMBOL(team_port_set_team_dev_addr);
+EXPORT_SYMBOL(team_modeop_port_change_dev_addr);
 
 static void team_refresh_port_linkup(struct team_port *port)
 {
@@ -490,9 +503,9 @@ static bool team_dummy_transmit(struct team *team, struct sk_buff *skb)
        return false;
 }
 
-rx_handler_result_t team_dummy_receive(struct team *team,
-                                      struct team_port *port,
-                                      struct sk_buff *skb)
+static rx_handler_result_t team_dummy_receive(struct team *team,
+                                             struct team_port *port,
+                                             struct sk_buff *skb)
 {
        return RX_HANDLER_ANOTHER;
 }
@@ -1491,8 +1504,8 @@ static void team_set_rx_mode(struct net_device *dev)
 
        rcu_read_lock();
        list_for_each_entry_rcu(port, &team->port_list, list) {
-               dev_uc_sync(port->dev, dev);
-               dev_mc_sync(port->dev, dev);
+               dev_uc_sync_multiple(port->dev, dev);
+               dev_mc_sync_multiple(port->dev, dev);
        }
        rcu_read_unlock();
 }
@@ -1585,7 +1598,7 @@ team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
        return stats;
 }
 
-static int team_vlan_rx_add_vid(struct net_device *dev, uint16_t vid)
+static int team_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct team *team = netdev_priv(dev);
        struct team_port *port;
@@ -1597,7 +1610,7 @@ static int team_vlan_rx_add_vid(struct net_device *dev, uint16_t vid)
         */
        mutex_lock(&team->lock);
        list_for_each_entry(port, &team->port_list, list) {
-               err = vlan_vid_add(port->dev, vid);
+               err = vlan_vid_add(port->dev, proto, vid);
                if (err)
                        goto unwind;
        }
@@ -1607,20 +1620,20 @@ static int team_vlan_rx_add_vid(struct net_device *dev, uint16_t vid)
 
 unwind:
        list_for_each_entry_continue_reverse(port, &team->port_list, list)
-               vlan_vid_del(port->dev, vid);
+               vlan_vid_del(port->dev, proto, vid);
        mutex_unlock(&team->lock);
 
        return err;
 }
 
-static int team_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
+static int team_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct team *team = netdev_priv(dev);
        struct team_port *port;
 
        rcu_read_lock();
        list_for_each_entry_rcu(port, &team->port_list, list)
-               vlan_vid_del(port->dev, vid);
+               vlan_vid_del(port->dev, proto, vid);
        rcu_read_unlock();
 
        return 0;
@@ -1828,9 +1841,9 @@ static void team_setup(struct net_device *dev)
        dev->features |= NETIF_F_LLTX;
        dev->features |= NETIF_F_GRO;
        dev->hw_features = TEAM_VLAN_FEATURES |
-                          NETIF_F_HW_VLAN_TX |
-                          NETIF_F_HW_VLAN_RX |
-                          NETIF_F_HW_VLAN_FILTER;
+                          NETIF_F_HW_VLAN_CTAG_TX |
+                          NETIF_F_HW_VLAN_CTAG_RX |
+                          NETIF_F_HW_VLAN_CTAG_FILTER;
 
        dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM);
        dev->features |= dev->hw_features;
index c5db428e73fa2200f3ef8a5ea5124ceeebb6ef78..c366cd299c06a6cf4e0351c5614b066c8e18ee91 100644 (file)
@@ -46,20 +46,10 @@ static bool bc_transmit(struct team *team, struct sk_buff *skb)
        return sum_ret;
 }
 
-static int bc_port_enter(struct team *team, struct team_port *port)
-{
-       return team_port_set_team_dev_addr(port);
-}
-
-static void bc_port_change_dev_addr(struct team *team, struct team_port *port)
-{
-       team_port_set_team_dev_addr(port);
-}
-
 static const struct team_mode_ops bc_mode_ops = {
        .transmit               = bc_transmit,
-       .port_enter             = bc_port_enter,
-       .port_change_dev_addr   = bc_port_change_dev_addr,
+       .port_enter             = team_modeop_port_enter,
+       .port_change_dev_addr   = team_modeop_port_change_dev_addr,
 };
 
 static const struct team_mode bc_mode = {
diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mode_random.c
new file mode 100644 (file)
index 0000000..9eabfaa
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * drivers/net/team/team_mode_random.c - Random mode for team
+ * Copyright (c) 2013 Jiri Pirko <jiri@resnulli.us>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/reciprocal_div.h>
+#include <linux/if_team.h>
+
+static u32 random_N(unsigned int N)
+{
+       return reciprocal_divide(random32(), N);
+}
+
+static bool rnd_transmit(struct team *team, struct sk_buff *skb)
+{
+       struct team_port *port;
+       int port_index;
+
+       port_index = random_N(team->en_port_count);
+       port = team_get_port_by_index_rcu(team, port_index);
+       port = team_get_first_port_txable_rcu(team, port);
+       if (unlikely(!port))
+               goto drop;
+       if (team_dev_queue_xmit(team, port, skb))
+               return false;
+       return true;
+
+drop:
+       dev_kfree_skb_any(skb);
+       return false;
+}
+
+static const struct team_mode_ops rnd_mode_ops = {
+       .transmit               = rnd_transmit,
+       .port_enter             = team_modeop_port_enter,
+       .port_change_dev_addr   = team_modeop_port_change_dev_addr,
+};
+
+static const struct team_mode rnd_mode = {
+       .kind           = "random",
+       .owner          = THIS_MODULE,
+       .ops            = &rnd_mode_ops,
+};
+
+static int __init rnd_init_module(void)
+{
+       return team_mode_register(&rnd_mode);
+}
+
+static void __exit rnd_cleanup_module(void)
+{
+       team_mode_unregister(&rnd_mode);
+}
+
+module_init(rnd_init_module);
+module_exit(rnd_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
+MODULE_DESCRIPTION("Random mode for team");
+MODULE_ALIAS("team-mode-random");
index 105135aa8f0586844927b641951087110a834cc1..d268e4de781b46b3cf2e3364770098f43f48d760 100644 (file)
@@ -25,26 +25,6 @@ static struct rr_priv *rr_priv(struct team *team)
        return (struct rr_priv *) &team->mode_priv;
 }
 
-static struct team_port *__get_first_port_up(struct team *team,
-                                            struct team_port *port)
-{
-       struct team_port *cur;
-
-       if (team_port_txable(port))
-               return port;
-       cur = port;
-       list_for_each_entry_continue_rcu(cur, &team->port_list, list)
-               if (team_port_txable(port))
-                       return cur;
-       list_for_each_entry_rcu(cur, &team->port_list, list) {
-               if (cur == port)
-                       break;
-               if (team_port_txable(port))
-                       return cur;
-       }
-       return NULL;
-}
-
 static bool rr_transmit(struct team *team, struct sk_buff *skb)
 {
        struct team_port *port;
@@ -52,7 +32,7 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb)
 
        port_index = rr_priv(team)->sent_packets++ % team->en_port_count;
        port = team_get_port_by_index_rcu(team, port_index);
-       port = __get_first_port_up(team, port);
+       port = team_get_first_port_txable_rcu(team, port);
        if (unlikely(!port))
                goto drop;
        if (team_dev_queue_xmit(team, port, skb))
@@ -64,20 +44,10 @@ drop:
        return false;
 }
 
-static int rr_port_enter(struct team *team, struct team_port *port)
-{
-       return team_port_set_team_dev_addr(port);
-}
-
-static void rr_port_change_dev_addr(struct team *team, struct team_port *port)
-{
-       team_port_set_team_dev_addr(port);
-}
-
 static const struct team_mode_ops rr_mode_ops = {
        .transmit               = rr_transmit,
-       .port_enter             = rr_port_enter,
-       .port_change_dev_addr   = rr_port_change_dev_addr,
+       .port_enter             = team_modeop_port_enter,
+       .port_change_dev_addr   = team_modeop_port_change_dev_addr,
 };
 
 static const struct team_mode rr_mode = {
index 729ed533bb33834fac05825be4b344a755e0e859..66109a2ad886270c0b726e51710d74fbf57aa8f1 100644 (file)
@@ -409,14 +409,12 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
 {
        struct tun_file *ntfile;
        struct tun_struct *tun;
-       struct net_device *dev;
 
        tun = rtnl_dereference(tfile->tun);
 
        if (tun && !tfile->detached) {
                u16 index = tfile->queue_index;
                BUG_ON(index >= tun->numqueues);
-               dev = tun->dev;
 
                rcu_assign_pointer(tun->tfiles[index],
                                   tun->tfiles[tun->numqueues - 1]);
@@ -1205,6 +1203,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
        }
 
        skb_reset_network_header(skb);
+       skb_probe_transport_header(skb, 0);
+
        rxhash = skb_get_rxhash(skb);
        netif_rx_ni(skb);
 
@@ -1656,6 +1656,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
                dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
                        TUN_USER_FEATURES;
                dev->features = dev->hw_features;
+               dev->vlan_features = dev->features;
 
                INIT_LIST_HEAD(&tun->disabled);
                err = tun_attach(tun, file);
index 709753469099c032aad55c3b98da120a0f930b2e..ad5d1e4384db7b3b6c3ab6f2406b5cd8075cd2d0 100644 (file)
@@ -55,11 +55,7 @@ static void asix_status(struct usbnet *dev, struct urb *urb)
        event = urb->transfer_buffer;
        link = event->link & 0x01;
        if (netif_carrier_ok(dev->net) != link) {
-               if (link) {
-                       netif_carrier_on(dev->net);
-                       usbnet_defer_kevent (dev, EVENT_LINK_RESET );
-               } else
-                       netif_carrier_off(dev->net);
+               usbnet_link_change(dev, link, 1);
                netdev_dbg(dev->net, "Link Status is: %d\n", link);
        }
 }
index 71c27d8d214fd3b08b9f974a90faa77bcf0e83e0..bd8758fa38c140cf93dd3c8b623a3d9b61a279fe 100644 (file)
@@ -352,11 +352,7 @@ static void ax88179_status(struct usbnet *dev, struct urb *urb)
        link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16;
 
        if (netif_carrier_ok(dev->net) != link) {
-               if (link)
-                       usbnet_defer_kevent(dev, EVENT_LINK_RESET);
-               else
-                       netif_carrier_off(dev->net);
-
+               usbnet_link_change(dev, link, 1);
                netdev_info(dev->net, "ax88179 - Link status is: %d\n", link);
        }
 }
@@ -455,7 +451,7 @@ static int ax88179_resume(struct usb_interface *intf)
        u16 tmp16;
        u8 tmp8;
 
-       netif_carrier_off(dev->net);
+       usbnet_link_change(dev, 0, 0);
 
        /* Power up ethernet PHY */
        tmp16 = 0;
@@ -1068,7 +1064,7 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
        /* Restart autoneg */
        mii_nway_restart(&dev->mii);
 
-       netif_carrier_off(dev->net);
+       usbnet_link_change(dev, 0, 0);
 
        return 0;
 }
@@ -1356,7 +1352,7 @@ static int ax88179_reset(struct usbnet *dev)
        /* Restart autoneg */
        mii_nway_restart(&dev->mii);
 
-       netif_carrier_off(dev->net);
+       usbnet_link_change(dev, 0, 0);
 
        return 0;
 }
index 57136dc1b887fde71fc9b1fc256b9e928d3e0a07..4ff71d619cd842b0b763aa9f0cbfb72823e4ef30 100644 (file)
@@ -406,10 +406,7 @@ void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
        case USB_CDC_NOTIFY_NETWORK_CONNECTION:
                netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
                          event->wValue ? "on" : "off");
-               if (event->wValue)
-                       netif_carrier_on(dev->net);
-               else
-                       netif_carrier_off(dev->net);
+               usbnet_link_change(dev, !!event->wValue, 0);
                break;
        case USB_CDC_NOTIFY_SPEED_CHANGE:       /* tx/rx rates */
                netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n",
index 6bd91676d2cbb9a46b43108ae1fa994d4fca84f9..c96454434f7b1bbebc4fb2f1914399af1e4da3be 100644 (file)
@@ -101,7 +101,7 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
        dev->net->flags |= IFF_NOARP;
 
        /* no need to put the VLAN tci in the packet headers */
-       dev->net->features |= NETIF_F_HW_VLAN_TX;
+       dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX;
 err:
        return ret;
 }
@@ -221,7 +221,7 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
 
        /* map MBIM session to VLAN */
        if (tci)
-               vlan_put_tag(skb, tci);
+               vlan_put_tag(skb, htons(ETH_P_8021Q), tci);
 err:
        return skb;
 }
index 4709fa3497cf2efcada053ccdefc06f7061971bc..43afde8f48d23e2cc3ee15e4cc55bd298b45dfe4 100644 (file)
@@ -362,8 +362,8 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
        u8 iface_no;
 
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
-       if (ctx == NULL)
-               return -ENODEV;
+       if (!ctx)
+               return -ENOMEM;
 
        hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        ctx->tx_timer.function = &cdc_ncm_tx_timer_cb;
@@ -610,7 +610,7 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
         * (carrier is OFF) during attach, so the IP network stack does not
         * start IPv6 negotiation and more.
         */
-       netif_carrier_off(dev->net);
+       usbnet_link_change(dev, 0, 0);
        return ret;
 }
 
@@ -1106,12 +1106,9 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                        " %sconnected\n",
                        ctx->netdev->name, ctx->connected ? "" : "dis");
 
-               if (ctx->connected)
-                       netif_carrier_on(dev->net);
-               else {
-                       netif_carrier_off(dev->net);
+               usbnet_link_change(dev, ctx->connected, 0);
+               if (!ctx->connected)
                        ctx->tx_speed = ctx->rx_speed = 0;
-               }
                break;
 
        case USB_CDC_NOTIFY_SPEED_CHANGE:
@@ -1124,8 +1121,9 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                break;
 
        default:
-               dev_err(&dev->udev->dev, "NCM: unexpected "
-                       "notification 0x%02x!\n", event->bNotificationType);
+               dev_dbg(&dev->udev->dev,
+                       "NCM: unexpected notification 0x%02x!\n",
+                       event->bNotificationType);
                break;
        }
 }
index 174e5ecea4cc9bb36dc880eb88096b122c0e63ef..2dbb9460349d3659fc738eabb35f01cb0022e049 100644 (file)
@@ -524,12 +524,7 @@ static void dm9601_status(struct usbnet *dev, struct urb *urb)
 
        link = !!(buf[0] & 0x40);
        if (netif_carrier_ok(dev->net) != link) {
-               if (link) {
-                       netif_carrier_on(dev->net);
-                       usbnet_defer_kevent (dev, EVENT_LINK_RESET);
-               }
-               else
-                       netif_carrier_off(dev->net);
+               usbnet_link_change(dev, link, 1);
                netdev_dbg(dev->net, "Link Status is: %d\n", link);
        }
 }
index 3f3f566afa0b7fb54a65481dbbae27ba8a168e5f..03832d3780aa6134059dc4768bb7614d71075b82 100644 (file)
@@ -576,11 +576,7 @@ static void mcs7830_status(struct usbnet *dev, struct urb *urb)
                 */
                if (data->link_counter > 20) {
                        data->link_counter = 0;
-                       if (link) {
-                               netif_carrier_on(dev->net);
-                               usbnet_defer_kevent(dev, EVENT_LINK_RESET);
-                       } else
-                               netif_carrier_off(dev->net);
+                       usbnet_link_change(dev, link, 0);
                        netdev_dbg(dev->net, "Link Status is: %d\n", link);
                }
        } else
index 79ab2435d9d3906a321332f8e72e961e35c3f1e0..a923d61c6fc54ab676ade398e8704f7c24a66a0d 100644 (file)
@@ -413,11 +413,10 @@ static void sierra_net_handle_lsi(struct usbnet *dev, char *data,
        if (link_up) {
                sierra_net_set_ctx_index(priv, hh->msgspecific.byte);
                priv->link_up = 1;
-               netif_carrier_on(dev->net);
        } else {
                priv->link_up = 0;
-               netif_carrier_off(dev->net);
        }
+       usbnet_link_change(dev, link_up, 0);
 }
 
 static void sierra_net_dosync(struct usbnet *dev)
index 51f3192f3931e0a179dcb619e4e714a0565656fc..1e5a9b72650e9946e23040f1169421bf87d18317 100644 (file)
@@ -938,6 +938,27 @@ static const struct ethtool_ops usbnet_ethtool_ops = {
 
 /*-------------------------------------------------------------------------*/
 
+static void __handle_link_change(struct usbnet *dev)
+{
+       if (!test_bit(EVENT_DEV_OPEN, &dev->flags))
+               return;
+
+       if (!netif_carrier_ok(dev->net)) {
+               /* kill URBs for reading packets to save bus bandwidth */
+               unlink_urbs(dev, &dev->rxq);
+
+               /*
+                * tx_timeout will unlink URBs for sending packets and
+                * tx queue is stopped by netcore after link becomes off
+                */
+       } else {
+               /* submitting URBs for reading packets */
+               tasklet_schedule(&dev->bh);
+       }
+
+       clear_bit(EVENT_LINK_CHANGE, &dev->flags);
+}
+
 /* work that cannot be done in interrupt context uses keventd.
  *
  * NOTE:  with 2.5 we could do more of this using completion callbacks,
@@ -1035,8 +1056,14 @@ skip_reset:
                } else {
                        usb_autopm_put_interface(dev->intf);
                }
+
+               /* handle link change from link resetting */
+               __handle_link_change(dev);
        }
 
+       if (test_bit (EVENT_LINK_CHANGE, &dev->flags))
+               __handle_link_change(dev);
+
        if (dev->flags)
                netdev_dbg(dev->net, "kevent done, flags = 0x%lx\n", dev->flags);
 }
@@ -1286,6 +1313,7 @@ static void usbnet_bh (unsigned long param)
        // or are we maybe short a few urbs?
        } else if (netif_running (dev->net) &&
                   netif_device_present (dev->net) &&
+                  netif_carrier_ok(dev->net) &&
                   !timer_pending (&dev->delay) &&
                   !test_bit (EVENT_RX_HALT, &dev->flags)) {
                int     temp = dev->rxq.qlen;
@@ -1521,7 +1549,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
        netif_device_attach (net);
 
        if (dev->driver_info->flags & FLAG_LINK_INTR)
-               netif_carrier_off(net);
+               usbnet_link_change(dev, 0, 0);
 
        return 0;
 
@@ -1653,6 +1681,21 @@ int usbnet_manage_power(struct usbnet *dev, int on)
 }
 EXPORT_SYMBOL(usbnet_manage_power);
 
+void usbnet_link_change(struct usbnet *dev, bool link, bool need_reset)
+{
+       /* update link after link is reseted */
+       if (link && !need_reset)
+               netif_carrier_on(dev->net);
+       else
+               netif_carrier_off(dev->net);
+
+       if (need_reset && link)
+               usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+       else
+               usbnet_defer_kevent(dev, EVENT_LINK_CHANGE);
+}
+EXPORT_SYMBOL(usbnet_link_change);
+
 /*-------------------------------------------------------------------------*/
 static int __usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
                             u16 value, u16 index, void *data, u16 size)
index 07a4af0aa3dcda800a9527e3ba01d9f65ea3feec..177f911f59462f6770df07ff11b49b12f998dfce 100644 (file)
@@ -255,7 +255,8 @@ static const struct net_device_ops veth_netdev_ops = {
 
 #define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO |    \
                       NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_HIGHDMA | \
-                      NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX)
+                      NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | \
+                      NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_VLAN_STAG_RX )
 
 static void veth_setup(struct net_device *dev)
 {
index 57ac4b0294bcad97461ba4c5744d38a9bee67aec..50077753a0e57b2b400a019c2755f48ba7b89021 100644 (file)
@@ -154,7 +154,7 @@ struct padded_vnet_hdr {
  */
 static int vq2txq(struct virtqueue *vq)
 {
-       return (virtqueue_get_queue_index(vq) - 1) / 2;
+       return (vq->index - 1) / 2;
 }
 
 static int txq2vq(int txq)
@@ -164,7 +164,7 @@ static int txq2vq(int txq)
 
 static int vq2rxq(struct virtqueue *vq)
 {
-       return virtqueue_get_queue_index(vq) / 2;
+       return vq->index / 2;
 }
 
 static int rxq2vq(int rxq)
@@ -1006,7 +1006,8 @@ static void virtnet_set_rx_mode(struct net_device *dev)
        kfree(buf);
 }
 
-static int virtnet_vlan_rx_add_vid(struct net_device *dev, u16 vid)
+static int virtnet_vlan_rx_add_vid(struct net_device *dev,
+                                  __be16 proto, u16 vid)
 {
        struct virtnet_info *vi = netdev_priv(dev);
        struct scatterlist sg;
@@ -1019,7 +1020,8 @@ static int virtnet_vlan_rx_add_vid(struct net_device *dev, u16 vid)
        return 0;
 }
 
-static int virtnet_vlan_rx_kill_vid(struct net_device *dev, u16 vid)
+static int virtnet_vlan_rx_kill_vid(struct net_device *dev,
+                                   __be16 proto, u16 vid)
 {
        struct virtnet_info *vi = netdev_priv(dev);
        struct scatterlist sg;
@@ -1376,7 +1378,7 @@ static int virtnet_find_vqs(struct virtnet_info *vi)
        if (vi->has_cvq) {
                vi->cvq = vqs[total_vqs - 1];
                if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN))
-                       vi->dev->features |= NETIF_F_HW_VLAN_FILTER;
+                       vi->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
        }
 
        for (i = 0; i < vi->max_queue_pairs; i++) {
@@ -1511,6 +1513,8 @@ static int virtnet_probe(struct virtio_device *vdev)
                /* (!csum && gso) case will be fixed by register_netdev() */
        }
 
+       dev->vlan_features = dev->features;
+
        /* Configuration may specify what MAC to use.  Otherwise random. */
        if (virtio_config_val_len(vdev, VIRTIO_NET_F_MAC,
                                  offsetof(struct virtio_net_config, mac),
index eae7a03d4f9bb7a5b69414d9c161bb7b0f777b77..55a62cae2cb4c976388a9b67dc20ad5c15832604 100644 (file)
@@ -1293,7 +1293,7 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
                        skb->protocol = eth_type_trans(skb, adapter->netdev);
 
                        if (unlikely(rcd->ts))
-                               __vlan_hwaccel_put_tag(skb, rcd->tci);
+                               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rcd->tci);
 
                        if (adapter->netdev->features & NETIF_F_LRO)
                                netif_receive_skb(skb);
@@ -1931,7 +1931,7 @@ vmxnet3_restore_vlan(struct vmxnet3_adapter *adapter)
 
 
 static int
-vmxnet3_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+vmxnet3_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct vmxnet3_adapter *adapter = netdev_priv(netdev);
 
@@ -1953,7 +1953,7 @@ vmxnet3_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
 
 
 static int
-vmxnet3_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+vmxnet3_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
        struct vmxnet3_adapter *adapter = netdev_priv(netdev);
 
@@ -2107,7 +2107,7 @@ vmxnet3_setup_driver_shared(struct vmxnet3_adapter *adapter)
                devRead->misc.uptFeatures |= UPT1_F_LRO;
                devRead->misc.maxNumRxSG = cpu_to_le16(1 + MAX_SKB_FRAGS);
        }
-       if (adapter->netdev->features & NETIF_F_HW_VLAN_RX)
+       if (adapter->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
                devRead->misc.uptFeatures |= UPT1_F_RXVLAN;
 
        devRead->misc.mtu = cpu_to_le32(adapter->netdev->mtu);
@@ -2669,14 +2669,15 @@ vmxnet3_declare_features(struct vmxnet3_adapter *adapter, bool dma64)
        struct net_device *netdev = adapter->netdev;
 
        netdev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM |
-               NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_TX |
-               NETIF_F_HW_VLAN_RX | NETIF_F_TSO | NETIF_F_TSO6 |
+               NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX |
+               NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 |
                NETIF_F_LRO;
        if (dma64)
                netdev->hw_features |= NETIF_F_HIGHDMA;
        netdev->vlan_features = netdev->hw_features &
-                               ~(NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
-       netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_FILTER;
+                               ~(NETIF_F_HW_VLAN_CTAG_TX |
+                                 NETIF_F_HW_VLAN_CTAG_RX);
+       netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
 }
 
 
index 63a124340cbecb19f3ad0d5f9b43f6fcfed6b4e0..600ab56c0008bab49251505e38ca911b611da2c1 100644 (file)
@@ -263,7 +263,8 @@ int vmxnet3_set_features(struct net_device *netdev, netdev_features_t features)
        unsigned long flags;
        netdev_features_t changed = features ^ netdev->features;
 
-       if (changed & (NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_HW_VLAN_RX)) {
+       if (changed & (NETIF_F_RXCSUM | NETIF_F_LRO |
+                      NETIF_F_HW_VLAN_CTAG_RX)) {
                if (features & NETIF_F_RXCSUM)
                        adapter->shared->devRead.misc.uptFeatures |=
                        UPT1_F_RXCSUM;
@@ -279,7 +280,7 @@ int vmxnet3_set_features(struct net_device *netdev, netdev_features_t features)
                        adapter->shared->devRead.misc.uptFeatures &=
                                                        ~UPT1_F_LRO;
 
-               if (features & NETIF_F_HW_VLAN_RX)
+               if (features & NETIF_F_HW_VLAN_CTAG_RX)
                        adapter->shared->devRead.misc.uptFeatures |=
                        UPT1_F_RXVLAN;
                else
index 7cee7a3068ec11ae9a7f91efa612748dfdd0463e..a7fd9a089a35297df3b34bc405adb96caf1147fa 100644 (file)
@@ -33,7 +33,7 @@
 #include <net/arp.h>
 #include <net/ndisc.h>
 #include <net/ip.h>
-#include <net/ipip.h>
+#include <net/ip_tunnels.h>
 #include <net/icmp.h>
 #include <net/udp.h>
 #include <net/rtnetlink.h>
@@ -81,35 +81,33 @@ struct vxlan_net {
        struct hlist_head vni_list[VNI_HASH_SIZE];
 };
 
+struct vxlan_rdst {
+       struct rcu_head          rcu;
+       __be32                   remote_ip;
+       __be16                   remote_port;
+       u32                      remote_vni;
+       u32                      remote_ifindex;
+       struct vxlan_rdst       *remote_next;
+};
+
 /* Forwarding table entry */
 struct vxlan_fdb {
        struct hlist_node hlist;        /* linked list of entries */
        struct rcu_head   rcu;
        unsigned long     updated;      /* jiffies */
        unsigned long     used;
-       __be32            remote_ip;
+       struct vxlan_rdst remote;
        u16               state;        /* see ndm_state */
+       u8                flags;        /* see ndm_flags */
        u8                eth_addr[ETH_ALEN];
 };
 
-/* Per-cpu network traffic stats */
-struct vxlan_stats {
-       u64                     rx_packets;
-       u64                     rx_bytes;
-       u64                     tx_packets;
-       u64                     tx_bytes;
-       struct u64_stats_sync   syncp;
-};
-
 /* Pseudo network device */
 struct vxlan_dev {
        struct hlist_node hlist;
        struct net_device *dev;
-       struct vxlan_stats __percpu *stats;
-       __u32             vni;          /* virtual network id */
-       __be32            gaddr;        /* multicast group */
+       struct vxlan_rdst default_dst;  /* default destination */
        __be32            saddr;        /* source address */
-       unsigned int      link;         /* link to multicast over */
        __u16             port_min;     /* source port range */
        __u16             port_max;
        __u8              tos;          /* TOS override */
@@ -147,7 +145,7 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id)
        struct vxlan_dev *vxlan;
 
        hlist_for_each_entry_rcu(vxlan, vni_head(net, id), hlist) {
-               if (vxlan->vni == id)
+               if (vxlan->default_dst.remote_vni == id)
                        return vxlan;
        }
 
@@ -157,7 +155,8 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id)
 /* Fill in neighbour message in skbuff. */
 static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
                           const struct vxlan_fdb *fdb,
-                          u32 portid, u32 seq, int type, unsigned int flags)
+                          u32 portid, u32 seq, int type, unsigned int flags,
+                          const struct vxlan_rdst *rdst)
 {
        unsigned long now = jiffies;
        struct nda_cacheinfo ci;
@@ -176,19 +175,29 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
 
        if (type == RTM_GETNEIGH) {
                ndm->ndm_family = AF_INET;
-               send_ip = fdb->remote_ip != 0;
+               send_ip = rdst->remote_ip != htonl(INADDR_ANY);
                send_eth = !is_zero_ether_addr(fdb->eth_addr);
        } else
                ndm->ndm_family = AF_BRIDGE;
        ndm->ndm_state = fdb->state;
        ndm->ndm_ifindex = vxlan->dev->ifindex;
-       ndm->ndm_flags = NTF_SELF;
+       ndm->ndm_flags = fdb->flags;
        ndm->ndm_type = NDA_DST;
 
        if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr))
                goto nla_put_failure;
 
-       if (send_ip && nla_put_be32(skb, NDA_DST, fdb->remote_ip))
+       if (send_ip && nla_put_be32(skb, NDA_DST, rdst->remote_ip))
+               goto nla_put_failure;
+
+       if (rdst->remote_port && rdst->remote_port != vxlan_port &&
+           nla_put_be16(skb, NDA_PORT, rdst->remote_port))
+               goto nla_put_failure;
+       if (rdst->remote_vni != vxlan->default_dst.remote_vni &&
+           nla_put_be32(skb, NDA_VNI, rdst->remote_vni))
+               goto nla_put_failure;
+       if (rdst->remote_ifindex &&
+           nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))
                goto nla_put_failure;
 
        ci.ndm_used      = jiffies_to_clock_t(now - fdb->used);
@@ -211,6 +220,9 @@ static inline size_t vxlan_nlmsg_size(void)
        return NLMSG_ALIGN(sizeof(struct ndmsg))
                + nla_total_size(ETH_ALEN) /* NDA_LLADDR */
                + nla_total_size(sizeof(__be32)) /* NDA_DST */
+               + nla_total_size(sizeof(__be32)) /* NDA_PORT */
+               + nla_total_size(sizeof(__be32)) /* NDA_VNI */
+               + nla_total_size(sizeof(__u32)) /* NDA_IFINDEX */
                + nla_total_size(sizeof(struct nda_cacheinfo));
 }
 
@@ -225,7 +237,7 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
        if (skb == NULL)
                goto errout;
 
-       err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0);
+       err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, &fdb->remote);
        if (err < 0) {
                /* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */
                WARN_ON(err == -EMSGSIZE);
@@ -247,7 +259,8 @@ static void vxlan_ip_miss(struct net_device *dev, __be32 ipa)
 
        memset(&f, 0, sizeof f);
        f.state = NUD_STALE;
-       f.remote_ip = ipa; /* goes to NDA_DST */
+       f.remote.remote_ip = ipa; /* goes to NDA_DST */
+       f.remote.remote_vni = VXLAN_N_VID;
 
        vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);
 }
@@ -300,10 +313,39 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
        return NULL;
 }
 
+/* Add/update destinations for multicast */
+static int vxlan_fdb_append(struct vxlan_fdb *f,
+                           __be32 ip, __u32 port, __u32 vni, __u32 ifindex)
+{
+       struct vxlan_rdst *rd_prev, *rd;
+
+       rd_prev = NULL;
+       for (rd = &f->remote; rd; rd = rd->remote_next) {
+               if (rd->remote_ip == ip &&
+                   rd->remote_port == port &&
+                   rd->remote_vni == vni &&
+                   rd->remote_ifindex == ifindex)
+                       return 0;
+               rd_prev = rd;
+       }
+       rd = kmalloc(sizeof(*rd), GFP_ATOMIC);
+       if (rd == NULL)
+               return -ENOBUFS;
+       rd->remote_ip = ip;
+       rd->remote_port = port;
+       rd->remote_vni = vni;
+       rd->remote_ifindex = ifindex;
+       rd->remote_next = NULL;
+       rd_prev->remote_next = rd;
+       return 1;
+}
+
 /* Add new entry to forwarding table -- assumes lock held */
 static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                            const u8 *mac, __be32 ip,
-                           __u16 state, __u16 flags)
+                           __u16 state, __u16 flags,
+                           __u32 port, __u32 vni, __u32 ifindex,
+                           __u8 ndm_flags)
 {
        struct vxlan_fdb *f;
        int notify = 0;
@@ -320,6 +362,19 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                        f->updated = jiffies;
                        notify = 1;
                }
+               if (f->flags != ndm_flags) {
+                       f->flags = ndm_flags;
+                       f->updated = jiffies;
+                       notify = 1;
+               }
+               if ((flags & NLM_F_APPEND) &&
+                   is_multicast_ether_addr(f->eth_addr)) {
+                       int rc = vxlan_fdb_append(f, ip, port, vni, ifindex);
+
+                       if (rc < 0)
+                               return rc;
+                       notify |= rc;
+               }
        } else {
                if (!(flags & NLM_F_CREATE))
                        return -ENOENT;
@@ -333,8 +388,13 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                        return -ENOMEM;
 
                notify = 1;
-               f->remote_ip = ip;
+               f->remote.remote_ip = ip;
+               f->remote.remote_port = port;
+               f->remote.remote_vni = vni;
+               f->remote.remote_ifindex = ifindex;
+               f->remote.remote_next = NULL;
                f->state = state;
+               f->flags = ndm_flags;
                f->updated = f->used = jiffies;
                memcpy(f->eth_addr, mac, ETH_ALEN);
 
@@ -349,6 +409,19 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
        return 0;
 }
 
+static void vxlan_fdb_free(struct rcu_head *head)
+{
+       struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu);
+
+       while (f->remote.remote_next) {
+               struct vxlan_rdst *rd = f->remote.remote_next;
+
+               f->remote.remote_next = rd->remote_next;
+               kfree(rd);
+       }
+       kfree(f);
+}
+
 static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f)
 {
        netdev_dbg(vxlan->dev,
@@ -358,7 +431,7 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f)
        vxlan_fdb_notify(vxlan, f, RTM_DELNEIGH);
 
        hlist_del_rcu(&f->hlist);
-       kfree_rcu(f, rcu);
+       call_rcu(&f->rcu, vxlan_fdb_free);
 }
 
 /* Add static entry (via netlink) */
@@ -367,7 +440,9 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                         const unsigned char *addr, u16 flags)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct net *net = dev_net(vxlan->dev);
        __be32 ip;
+       u32 port, vni, ifindex;
        int err;
 
        if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_REACHABLE))) {
@@ -384,8 +459,36 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 
        ip = nla_get_be32(tb[NDA_DST]);
 
+       if (tb[NDA_PORT]) {
+               if (nla_len(tb[NDA_PORT]) != sizeof(u32))
+                       return -EINVAL;
+               port = nla_get_u32(tb[NDA_PORT]);
+       } else
+               port = vxlan_port;
+
+       if (tb[NDA_VNI]) {
+               if (nla_len(tb[NDA_VNI]) != sizeof(u32))
+                       return -EINVAL;
+               vni = nla_get_u32(tb[NDA_VNI]);
+       } else
+               vni = vxlan->default_dst.remote_vni;
+
+       if (tb[NDA_IFINDEX]) {
+               struct net_device *tdev;
+
+               if (nla_len(tb[NDA_IFINDEX]) != sizeof(u32))
+                       return -EINVAL;
+               ifindex = nla_get_u32(tb[NDA_IFINDEX]);
+               tdev = dev_get_by_index(net, ifindex);
+               if (!tdev)
+                       return -EADDRNOTAVAIL;
+               dev_put(tdev);
+       } else
+               ifindex = 0;
+
        spin_lock_bh(&vxlan->hash_lock);
-       err = vxlan_fdb_create(vxlan, addr, ip, ndm->ndm_state, flags);
+       err = vxlan_fdb_create(vxlan, addr, ip, ndm->ndm_state, flags, port,
+               vni, ifindex, ndm->ndm_flags);
        spin_unlock_bh(&vxlan->hash_lock);
 
        return err;
@@ -423,18 +526,21 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
                int err;
 
                hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) {
-                       if (idx < cb->args[0])
-                               goto skip;
-
-                       err = vxlan_fdb_info(skb, vxlan, f,
-                                            NETLINK_CB(cb->skb).portid,
-                                            cb->nlh->nlmsg_seq,
-                                            RTM_NEWNEIGH,
-                                            NLM_F_MULTI);
-                       if (err < 0)
-                               break;
+                       struct vxlan_rdst *rd;
+                       for (rd = &f->remote; rd; rd = rd->remote_next) {
+                               if (idx < cb->args[0])
+                                       goto skip;
+
+                               err = vxlan_fdb_info(skb, vxlan, f,
+                                                    NETLINK_CB(cb->skb).portid,
+                                                    cb->nlh->nlmsg_seq,
+                                                    RTM_NEWNEIGH,
+                                                    NLM_F_MULTI, rd);
+                               if (err < 0)
+                                       break;
 skip:
-                       ++idx;
+                               ++idx;
+                       }
                }
        }
 
@@ -454,22 +560,25 @@ static void vxlan_snoop(struct net_device *dev,
        f = vxlan_find_mac(vxlan, src_mac);
        if (likely(f)) {
                f->used = jiffies;
-               if (likely(f->remote_ip == src_ip))
+               if (likely(f->remote.remote_ip == src_ip))
                        return;
 
                if (net_ratelimit())
                        netdev_info(dev,
                                    "%pM migrated from %pI4 to %pI4\n",
-                                   src_mac, &f->remote_ip, &src_ip);
+                                   src_mac, &f->remote.remote_ip, &src_ip);
 
-               f->remote_ip = src_ip;
+               f->remote.remote_ip = src_ip;
                f->updated = jiffies;
        } else {
                /* learned new entry */
                spin_lock(&vxlan->hash_lock);
                err = vxlan_fdb_create(vxlan, src_mac, src_ip,
                                       NUD_REACHABLE,
-                                      NLM_F_EXCL|NLM_F_CREATE);
+                                      NLM_F_EXCL|NLM_F_CREATE,
+                                      vxlan_port,
+                                      vxlan->default_dst.remote_vni,
+                                      0, NTF_SELF);
                spin_unlock(&vxlan->hash_lock);
        }
 }
@@ -490,7 +599,7 @@ static bool vxlan_group_used(struct vxlan_net *vn,
                        if (!netif_running(vxlan->dev))
                                continue;
 
-                       if (vxlan->gaddr == this->gaddr)
+                       if (vxlan->default_dst.remote_ip == this->default_dst.remote_ip)
                                return true;
                }
 
@@ -504,8 +613,8 @@ static int vxlan_join_group(struct net_device *dev)
        struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
        struct sock *sk = vn->sock->sk;
        struct ip_mreqn mreq = {
-               .imr_multiaddr.s_addr   = vxlan->gaddr,
-               .imr_ifindex            = vxlan->link,
+               .imr_multiaddr.s_addr   = vxlan->default_dst.remote_ip,
+               .imr_ifindex            = vxlan->default_dst.remote_ifindex,
        };
        int err;
 
@@ -532,8 +641,8 @@ static int vxlan_leave_group(struct net_device *dev)
        int err = 0;
        struct sock *sk = vn->sock->sk;
        struct ip_mreqn mreq = {
-               .imr_multiaddr.s_addr   = vxlan->gaddr,
-               .imr_ifindex            = vxlan->link,
+               .imr_multiaddr.s_addr   = vxlan->default_dst.remote_ip,
+               .imr_ifindex            = vxlan->default_dst.remote_ifindex,
        };
 
        /* Only leave group when last vxlan is done. */
@@ -556,7 +665,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
        struct iphdr *oip;
        struct vxlanhdr *vxh;
        struct vxlan_dev *vxlan;
-       struct vxlan_stats *stats;
+       struct pcpu_tstats *stats;
        __u32 vni;
        int err;
 
@@ -632,7 +741,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
                }
        }
 
-       stats = this_cpu_ptr(vxlan->stats);
+       stats = this_cpu_ptr(vxlan->dev->tstats);
        u64_stats_update_begin(&stats->syncp);
        stats->rx_packets++;
        stats->rx_bytes += skb->len;
@@ -691,7 +800,6 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
        n = neigh_lookup(&arp_tbl, &tip, dev);
 
        if (n) {
-               struct vxlan_dev *vxlan = netdev_priv(dev);
                struct vxlan_fdb *f;
                struct sk_buff  *reply;
 
@@ -701,7 +809,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
                }
 
                f = vxlan_find_mac(vxlan, n->ha);
-               if (f && f->remote_ip == 0) {
+               if (f && f->remote.remote_ip == htonl(INADDR_ANY)) {
                        /* bridge-local neighbor */
                        neigh_release(n);
                        goto out;
@@ -763,28 +871,6 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
        return false;
 }
 
-/* Extract dsfield from inner protocol */
-static inline u8 vxlan_get_dsfield(const struct iphdr *iph,
-                                  const struct sk_buff *skb)
-{
-       if (skb->protocol == htons(ETH_P_IP))
-               return iph->tos;
-       else if (skb->protocol == htons(ETH_P_IPV6))
-               return ipv6_get_dsfield((const struct ipv6hdr *)iph);
-       else
-               return 0;
-}
-
-/* Propogate ECN bits out */
-static inline u8 vxlan_ecn_encap(u8 tos,
-                                const struct iphdr *iph,
-                                const struct sk_buff *skb)
-{
-       u8 inner = vxlan_get_dsfield(iph, skb);
-
-       return INET_ECN_encapsulate(tos, inner);
-}
-
 static void vxlan_sock_free(struct sk_buff *skb)
 {
        sock_put(skb->sk);
@@ -820,68 +906,75 @@ static u16 vxlan_src_port(const struct vxlan_dev *vxlan, struct sk_buff *skb)
        return (((u64) hash * range) >> 32) + vxlan->port_min;
 }
 
-/* Transmit local packets over Vxlan
- *
- * Outer IP header inherits ECN and DF from inner header.
- * Outer UDP destination is the VXLAN assigned port.
- *           source port is based on hash of flow
- */
-static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
+static int handle_offloads(struct sk_buff *skb)
+{
+       if (skb_is_gso(skb)) {
+               int err = skb_unclone(skb, GFP_ATOMIC);
+               if (unlikely(err))
+                       return err;
+
+               skb_shinfo(skb)->gso_type |= (SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP);
+       } else if (skb->ip_summed != CHECKSUM_PARTIAL)
+               skb->ip_summed = CHECKSUM_NONE;
+
+       return 0;
+}
+
+/* Bypass encapsulation if the destination is local */
+static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
+                              struct vxlan_dev *dst_vxlan)
+{
+       struct pcpu_tstats *tx_stats = this_cpu_ptr(src_vxlan->dev->tstats);
+       struct pcpu_tstats *rx_stats = this_cpu_ptr(dst_vxlan->dev->tstats);
+
+       skb->pkt_type = PACKET_HOST;
+       skb->encapsulation = 0;
+       skb->dev = dst_vxlan->dev;
+       __skb_pull(skb, skb_network_offset(skb));
+
+       if (dst_vxlan->flags & VXLAN_F_LEARN)
+               vxlan_snoop(skb->dev, htonl(INADDR_LOOPBACK),
+                           eth_hdr(skb)->h_source);
+
+       u64_stats_update_begin(&tx_stats->syncp);
+       tx_stats->tx_packets++;
+       tx_stats->tx_bytes += skb->len;
+       u64_stats_update_end(&tx_stats->syncp);
+
+       if (netif_rx(skb) == NET_RX_SUCCESS) {
+               u64_stats_update_begin(&rx_stats->syncp);
+               rx_stats->rx_packets++;
+               rx_stats->rx_bytes += skb->len;
+               u64_stats_update_end(&rx_stats->syncp);
+       } else {
+               skb->dev->stats.rx_dropped++;
+       }
+}
+
+static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+                                 struct vxlan_rdst *rdst, bool did_rsc)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct rtable *rt;
        const struct iphdr *old_iph;
-       struct ethhdr *eth;
        struct iphdr *iph;
        struct vxlanhdr *vxh;
        struct udphdr *uh;
        struct flowi4 fl4;
-       unsigned int pkt_len = skb->len;
        __be32 dst;
-       __u16 src_port;
+       __u16 src_port, dst_port;
+        u32 vni;
        __be16 df = 0;
        __u8 tos, ttl;
-       int err;
-       bool did_rsc = false;
-       const struct vxlan_fdb *f;
-
-       skb_reset_mac_header(skb);
-       eth = eth_hdr(skb);
 
-       if ((vxlan->flags & VXLAN_F_PROXY) && ntohs(eth->h_proto) == ETH_P_ARP)
-               return arp_reduce(dev, skb);
-       else if ((vxlan->flags&VXLAN_F_RSC) && ntohs(eth->h_proto) == ETH_P_IP)
-               did_rsc = route_shortcircuit(dev, skb);
-
-       f = vxlan_find_mac(vxlan, eth->h_dest);
-       if (f == NULL) {
-               did_rsc = false;
-               dst = vxlan->gaddr;
-               if (!dst && (vxlan->flags & VXLAN_F_L2MISS) &&
-                   !is_multicast_ether_addr(eth->h_dest))
-                       vxlan_fdb_miss(vxlan, eth->h_dest);
-       } else
-               dst = f->remote_ip;
+       dst_port = rdst->remote_port ? rdst->remote_port : vxlan_port;
+       vni = rdst->remote_vni;
+       dst = rdst->remote_ip;
 
        if (!dst) {
                if (did_rsc) {
-                       __skb_pull(skb, skb_network_offset(skb));
-                       skb->ip_summed = CHECKSUM_NONE;
-                       skb->pkt_type = PACKET_HOST;
-
                        /* short-circuited back to local bridge */
-                       if (netif_rx(skb) == NET_RX_SUCCESS) {
-                               struct vxlan_stats *stats =
-                                               this_cpu_ptr(vxlan->stats);
-
-                               u64_stats_update_begin(&stats->syncp);
-                               stats->tx_packets++;
-                               stats->tx_bytes += pkt_len;
-                               u64_stats_update_end(&stats->syncp);
-                       } else {
-                               dev->stats.tx_errors++;
-                               dev->stats.tx_aborted_errors++;
-                       }
+                       vxlan_encap_bypass(skb, vxlan, vxlan);
                        return NETDEV_TX_OK;
                }
                goto drop;
@@ -904,12 +997,12 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 
        tos = vxlan->tos;
        if (tos == 1)
-               tos = vxlan_get_dsfield(old_iph, skb);
+               tos = ip_tunnel_get_dsfield(old_iph, skb);
 
        src_port = vxlan_src_port(vxlan, skb);
 
        memset(&fl4, 0, sizeof(fl4));
-       fl4.flowi4_oif = vxlan->link;
+       fl4.flowi4_oif = rdst->remote_ifindex;
        fl4.flowi4_tos = RT_TOS(tos);
        fl4.daddr = dst;
        fl4.saddr = vxlan->saddr;
@@ -928,6 +1021,19 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
                goto tx_error;
        }
 
+       /* Bypass encapsulation if the destination is local */
+       if (rt->rt_flags & RTCF_LOCAL &&
+           !(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) {
+               struct vxlan_dev *dst_vxlan;
+
+               ip_rt_put(rt);
+               dst_vxlan = vxlan_find_vni(dev_net(dev), vni);
+               if (!dst_vxlan)
+                       goto tx_error;
+               vxlan_encap_bypass(skb, vxlan, dst_vxlan);
+               return NETDEV_TX_OK;
+       }
+
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
        IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
                              IPSKB_REROUTED);
@@ -936,13 +1042,13 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 
        vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
        vxh->vx_flags = htonl(VXLAN_FLAGS);
-       vxh->vx_vni = htonl(vxlan->vni << 8);
+       vxh->vx_vni = htonl(vni << 8);
 
        __skb_push(skb, sizeof(*uh));
        skb_reset_transport_header(skb);
        uh = udp_hdr(skb);
 
-       uh->dest = htons(vxlan_port);
+       uh->dest = htons(dst_port);
        uh->source = htons(src_port);
 
        uh->len = htons(skb->len);
@@ -955,7 +1061,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
        iph->ihl        = sizeof(struct iphdr) >> 2;
        iph->frag_off   = df;
        iph->protocol   = IPPROTO_UDP;
-       iph->tos        = vxlan_ecn_encap(tos, old_iph, skb);
+       iph->tos        = ip_tunnel_ecn_encap(tos, old_iph, skb);
        iph->daddr      = dst;
        iph->saddr      = fl4.saddr;
        iph->ttl        = ttl ? : ip4_dst_hoplimit(&rt->dst);
@@ -965,22 +1071,10 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 
        vxlan_set_owner(dev, skb);
 
-       /* See iptunnel_xmit() */
-       if (skb->ip_summed != CHECKSUM_PARTIAL)
-               skb->ip_summed = CHECKSUM_NONE;
-
-       err = ip_local_out(skb);
-       if (likely(net_xmit_eval(err) == 0)) {
-               struct vxlan_stats *stats = this_cpu_ptr(vxlan->stats);
+       if (handle_offloads(skb))
+               goto drop;
 
-               u64_stats_update_begin(&stats->syncp);
-               stats->tx_packets++;
-               stats->tx_bytes += pkt_len;
-               u64_stats_update_end(&stats->syncp);
-       } else {
-               dev->stats.tx_errors++;
-               dev->stats.tx_aborted_errors++;
-       }
+       iptunnel_xmit(skb, dev);
        return NETDEV_TX_OK;
 
 drop:
@@ -994,6 +1088,65 @@ tx_free:
        return NETDEV_TX_OK;
 }
 
+/* Transmit local packets over Vxlan
+ *
+ * Outer IP header inherits ECN and DF from inner header.
+ * Outer UDP destination is the VXLAN assigned port.
+ *           source port is based on hash of flow
+ */
+static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct ethhdr *eth;
+       bool did_rsc = false;
+       struct vxlan_rdst *rdst0, *rdst;
+       struct vxlan_fdb *f;
+       int rc1, rc;
+
+       skb_reset_mac_header(skb);
+       eth = eth_hdr(skb);
+
+       if ((vxlan->flags & VXLAN_F_PROXY) && ntohs(eth->h_proto) == ETH_P_ARP)
+               return arp_reduce(dev, skb);
+
+       f = vxlan_find_mac(vxlan, eth->h_dest);
+       did_rsc = false;
+
+       if (f && (f->flags & NTF_ROUTER) && (vxlan->flags & VXLAN_F_RSC) &&
+           ntohs(eth->h_proto) == ETH_P_IP) {
+               did_rsc = route_shortcircuit(dev, skb);
+               if (did_rsc)
+                       f = vxlan_find_mac(vxlan, eth->h_dest);
+       }
+
+       if (f == NULL) {
+               rdst0 = &vxlan->default_dst;
+
+               if (rdst0->remote_ip == htonl(INADDR_ANY) &&
+                   (vxlan->flags & VXLAN_F_L2MISS) &&
+                   !is_multicast_ether_addr(eth->h_dest))
+                       vxlan_fdb_miss(vxlan, eth->h_dest);
+       } else
+               rdst0 = &f->remote;
+
+       rc = NETDEV_TX_OK;
+
+       /* if there are multiple destinations, send copies */
+       for (rdst = rdst0->remote_next; rdst; rdst = rdst->remote_next) {
+               struct sk_buff *skb1;
+
+               skb1 = skb_clone(skb, GFP_ATOMIC);
+               rc1 = vxlan_xmit_one(skb1, dev, rdst, did_rsc);
+               if (rc == NETDEV_TX_OK)
+                       rc = rc1;
+       }
+
+       rc1 = vxlan_xmit_one(skb, dev, rdst0, did_rsc);
+       if (rc == NETDEV_TX_OK)
+               rc = rc1;
+       return rc;
+}
+
 /* Walk the forwarding table and purge stale entries */
 static void vxlan_cleanup(unsigned long arg)
 {
@@ -1034,10 +1187,8 @@ static void vxlan_cleanup(unsigned long arg)
 /* Setup stats when device is created */
 static int vxlan_init(struct net_device *dev)
 {
-       struct vxlan_dev *vxlan = netdev_priv(dev);
-
-       vxlan->stats = alloc_percpu(struct vxlan_stats);
-       if (!vxlan->stats)
+       dev->tstats = alloc_percpu(struct pcpu_tstats);
+       if (!dev->tstats)
                return -ENOMEM;
 
        return 0;
@@ -1049,7 +1200,7 @@ static int vxlan_open(struct net_device *dev)
        struct vxlan_dev *vxlan = netdev_priv(dev);
        int err;
 
-       if (vxlan->gaddr) {
+       if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) {
                err = vxlan_join_group(dev);
                if (err)
                        return err;
@@ -1083,7 +1234,7 @@ static int vxlan_stop(struct net_device *dev)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
 
-       if (vxlan->gaddr)
+       if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip)))
                vxlan_leave_group(dev);
 
        del_timer_sync(&vxlan->age_timer);
@@ -1093,49 +1244,6 @@ static int vxlan_stop(struct net_device *dev)
        return 0;
 }
 
-/* Merge per-cpu statistics */
-static struct rtnl_link_stats64 *vxlan_stats64(struct net_device *dev,
-                                              struct rtnl_link_stats64 *stats)
-{
-       struct vxlan_dev *vxlan = netdev_priv(dev);
-       struct vxlan_stats tmp, sum = { 0 };
-       unsigned int cpu;
-
-       for_each_possible_cpu(cpu) {
-               unsigned int start;
-               const struct vxlan_stats *stats
-                       = per_cpu_ptr(vxlan->stats, cpu);
-
-               do {
-                       start = u64_stats_fetch_begin_bh(&stats->syncp);
-                       memcpy(&tmp, stats, sizeof(tmp));
-               } while (u64_stats_fetch_retry_bh(&stats->syncp, start));
-
-               sum.tx_bytes   += tmp.tx_bytes;
-               sum.tx_packets += tmp.tx_packets;
-               sum.rx_bytes   += tmp.rx_bytes;
-               sum.rx_packets += tmp.rx_packets;
-       }
-
-       stats->tx_bytes   = sum.tx_bytes;
-       stats->tx_packets = sum.tx_packets;
-       stats->rx_bytes   = sum.rx_bytes;
-       stats->rx_packets = sum.rx_packets;
-
-       stats->multicast = dev->stats.multicast;
-       stats->rx_length_errors = dev->stats.rx_length_errors;
-       stats->rx_frame_errors = dev->stats.rx_frame_errors;
-       stats->rx_errors = dev->stats.rx_errors;
-
-       stats->tx_dropped = dev->stats.tx_dropped;
-       stats->tx_carrier_errors  = dev->stats.tx_carrier_errors;
-       stats->tx_aborted_errors  = dev->stats.tx_aborted_errors;
-       stats->collisions  = dev->stats.collisions;
-       stats->tx_errors = dev->stats.tx_errors;
-
-       return stats;
-}
-
 /* Stub, nothing needs to be done. */
 static void vxlan_set_multicast_list(struct net_device *dev)
 {
@@ -1146,7 +1254,7 @@ static const struct net_device_ops vxlan_netdev_ops = {
        .ndo_open               = vxlan_open,
        .ndo_stop               = vxlan_stop,
        .ndo_start_xmit         = vxlan_xmit,
-       .ndo_get_stats64        = vxlan_stats64,
+       .ndo_get_stats64        = ip_tunnel_get_stats64,
        .ndo_set_rx_mode        = vxlan_set_multicast_list,
        .ndo_change_mtu         = eth_change_mtu,
        .ndo_validate_addr      = eth_validate_addr,
@@ -1163,9 +1271,7 @@ static struct device_type vxlan_type = {
 
 static void vxlan_free(struct net_device *dev)
 {
-       struct vxlan_dev *vxlan = netdev_priv(dev);
-
-       free_percpu(vxlan->stats);
+       free_percpu(dev->tstats);
        free_netdev(dev);
 }
 
@@ -1189,8 +1295,10 @@ static void vxlan_setup(struct net_device *dev)
        dev->features   |= NETIF_F_NETNS_LOCAL;
        dev->features   |= NETIF_F_SG | NETIF_F_HW_CSUM;
        dev->features   |= NETIF_F_RXCSUM;
+       dev->features   |= NETIF_F_GSO_SOFTWARE;
 
        dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+       dev->hw_features |= NETIF_F_GSO_SOFTWARE;
        dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
        dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
 
@@ -1212,7 +1320,7 @@ static void vxlan_setup(struct net_device *dev)
 
 static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = {
        [IFLA_VXLAN_ID]         = { .type = NLA_U32 },
-       [IFLA_VXLAN_GROUP]      = { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+       [IFLA_VXLAN_REMOTE]     = { .len = FIELD_SIZEOF(struct iphdr, daddr) },
        [IFLA_VXLAN_LINK]       = { .type = NLA_U32 },
        [IFLA_VXLAN_LOCAL]      = { .len = FIELD_SIZEOF(struct iphdr, saddr) },
        [IFLA_VXLAN_TOS]        = { .type = NLA_U8 },
@@ -1250,14 +1358,6 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[])
                        return -ERANGE;
        }
 
-       if (data[IFLA_VXLAN_GROUP]) {
-               __be32 gaddr = nla_get_be32(data[IFLA_VXLAN_GROUP]);
-               if (!IN_MULTICAST(ntohl(gaddr))) {
-                       pr_debug("group address is not IPv4 multicast\n");
-                       return -EADDRNOTAVAIL;
-               }
-       }
-
        if (data[IFLA_VXLAN_PORT_RANGE]) {
                const struct ifla_vxlan_port_range *p
                        = nla_data(data[IFLA_VXLAN_PORT_RANGE]);
@@ -1288,6 +1388,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
                         struct nlattr *tb[], struct nlattr *data[])
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_rdst *dst = &vxlan->default_dst;
        __u32 vni;
        int err;
 
@@ -1299,21 +1400,21 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
                pr_info("duplicate VNI %u\n", vni);
                return -EEXIST;
        }
-       vxlan->vni = vni;
+       dst->remote_vni = vni;
 
-       if (data[IFLA_VXLAN_GROUP])
-               vxlan->gaddr = nla_get_be32(data[IFLA_VXLAN_GROUP]);
+       if (data[IFLA_VXLAN_REMOTE])
+               dst->remote_ip = nla_get_be32(data[IFLA_VXLAN_REMOTE]);
 
        if (data[IFLA_VXLAN_LOCAL])
                vxlan->saddr = nla_get_be32(data[IFLA_VXLAN_LOCAL]);
 
        if (data[IFLA_VXLAN_LINK] &&
-           (vxlan->link = nla_get_u32(data[IFLA_VXLAN_LINK]))) {
+           (dst->remote_ifindex = nla_get_u32(data[IFLA_VXLAN_LINK]))) {
                struct net_device *lowerdev
-                        = __dev_get_by_index(net, vxlan->link);
+                        = __dev_get_by_index(net, dst->remote_ifindex);
 
                if (!lowerdev) {
-                       pr_info("ifindex %d does not exist\n", vxlan->link);
+                       pr_info("ifindex %d does not exist\n", dst->remote_ifindex);
                        return -ENODEV;
                }
 
@@ -1365,7 +1466,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
 
        err = register_netdevice(dev);
        if (!err)
-               hlist_add_head_rcu(&vxlan->hlist, vni_head(net, vxlan->vni));
+               hlist_add_head_rcu(&vxlan->hlist, vni_head(net, dst->remote_vni));
 
        return err;
 }
@@ -1383,7 +1484,7 @@ static size_t vxlan_get_size(const struct net_device *dev)
 {
 
        return nla_total_size(sizeof(__u32)) +  /* IFLA_VXLAN_ID */
-               nla_total_size(sizeof(__be32)) +/* IFLA_VXLAN_GROUP */
+               nla_total_size(sizeof(__be32)) +/* IFLA_VXLAN_REMOTE */
                nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LINK */
                nla_total_size(sizeof(__be32))+ /* IFLA_VXLAN_LOCAL */
                nla_total_size(sizeof(__u8)) +  /* IFLA_VXLAN_TTL */
@@ -1402,18 +1503,19 @@ static size_t vxlan_get_size(const struct net_device *dev)
 static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
 {
        const struct vxlan_dev *vxlan = netdev_priv(dev);
+       const struct vxlan_rdst *dst = &vxlan->default_dst;
        struct ifla_vxlan_port_range ports = {
                .low =  htons(vxlan->port_min),
                .high = htons(vxlan->port_max),
        };
 
-       if (nla_put_u32(skb, IFLA_VXLAN_ID, vxlan->vni))
+       if (nla_put_u32(skb, IFLA_VXLAN_ID, dst->remote_vni))
                goto nla_put_failure;
 
-       if (vxlan->gaddr && nla_put_be32(skb, IFLA_VXLAN_GROUP, vxlan->gaddr))
+       if (dst->remote_ip && nla_put_be32(skb, IFLA_VXLAN_REMOTE, dst->remote_ip))
                goto nla_put_failure;
 
-       if (vxlan->link && nla_put_u32(skb, IFLA_VXLAN_LINK, vxlan->link))
+       if (dst->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, dst->remote_ifindex))
                goto nla_put_failure;
 
        if (vxlan->saddr && nla_put_be32(skb, IFLA_VXLAN_LOCAL, vxlan->saddr))
@@ -1555,6 +1657,7 @@ static void __exit vxlan_cleanup_module(void)
 {
        rtnl_link_unregister(&vxlan_link_ops);
        unregister_pernet_device(&vxlan_net_ops);
+       rcu_barrier();
 }
 module_exit(vxlan_cleanup_module);
 
index 7157f7d311c5e001a2e071231d50755fc7a9c3cb..afd1e36d308faefdf20fd2f7f4b4c748ef72464a 100644 (file)
@@ -1091,7 +1091,7 @@ static int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        return ret;
 }
 
-static void ar5523_flush(struct ieee80211_hw *hw, bool drop)
+static void ar5523_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct ar5523 *ar = hw->priv;
 
index 3150def17193b72652bb068c8fde9ca99e0a6e5f..2d691b8b95b94bb3e57aedd26af2972cc128f683 100644 (file)
@@ -1523,7 +1523,8 @@ int ath5k_hw_dma_stop(struct ath5k_hw *ah);
 /* EEPROM access functions */
 int ath5k_eeprom_init(struct ath5k_hw *ah);
 void ath5k_eeprom_detach(struct ath5k_hw *ah);
-
+int ath5k_eeprom_mode_from_channel(struct ath5k_hw *ah,
+               struct ieee80211_channel *channel);
 
 /* Protocol Control Unit Functions */
 /* Helpers */
index b7e0258887e70cf9be193c70e73334e9e8fe0473..94d34ee02265d11fed55b014b84058f39177c905 100644 (file)
@@ -1779,7 +1779,8 @@ ath5k_eeprom_detach(struct ath5k_hw *ah)
 }
 
 int
-ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel)
+ath5k_eeprom_mode_from_channel(struct ath5k_hw *ah,
+               struct ieee80211_channel *channel)
 {
        switch (channel->hw_value) {
        case AR5K_MODE_11A:
@@ -1789,6 +1790,7 @@ ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel)
        case AR5K_MODE_11B:
                return AR5K_EEPROM_MODE_11B;
        default:
-               return -1;
+               ATH5K_WARN(ah, "channel is not A/B/G!");
+               return AR5K_EEPROM_MODE_11A;
        }
 }
index 94a9bbea6874c0daf10510e26fa2862f8ebbfa81..693296ee9693f80bb942e93049c377d33ac25801 100644 (file)
@@ -493,6 +493,3 @@ struct ath5k_eeprom_info {
        /* Antenna raw switch tables */
        u32     ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
 };
-
-int
-ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel);
index a78afa98c6509559f6cec5348d3c93648d520702..d6bc7cb61bfb9b31ffa478fc77aae84ef1c39905 100644 (file)
@@ -1612,11 +1612,7 @@ ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
 
        ah->ah_cal_mask |= AR5K_CALIBRATION_NF;
 
-       ee_mode = ath5k_eeprom_mode_from_channel(ah->ah_current_channel);
-       if (WARN_ON(ee_mode < 0)) {
-               ah->ah_cal_mask &= ~AR5K_CALIBRATION_NF;
-               return;
-       }
+       ee_mode = ath5k_eeprom_mode_from_channel(ah, ah->ah_current_channel);
 
        /* completed NF calibration, test threshold */
        nf = ath5k_hw_read_measured_noise_floor(ah);
@@ -2317,12 +2313,7 @@ ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode)
 
        def_ant = ah->ah_def_ant;
 
-       ee_mode = ath5k_eeprom_mode_from_channel(channel);
-       if (ee_mode < 0) {
-               ATH5K_ERR(ah,
-                       "invalid channel: %d\n", channel->center_freq);
-               return;
-       }
+       ee_mode = ath5k_eeprom_mode_from_channel(ah, channel);
 
        switch (ant_mode) {
        case AR5K_ANTMODE_DEFAULT:
@@ -3622,12 +3613,7 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                return -EINVAL;
        }
 
-       ee_mode = ath5k_eeprom_mode_from_channel(channel);
-       if (ee_mode < 0) {
-               ATH5K_ERR(ah,
-                       "invalid channel: %d\n", channel->center_freq);
-               return -EINVAL;
-       }
+       ee_mode = ath5k_eeprom_mode_from_channel(ah, channel);
 
        /* Initialize TX power table */
        switch (ah->ah_radio) {
index e2d8b2cf19eb52df95226ca8fb9661a427d92f95..a3399c4f13a9a85c070bebf1ecbcd7cc6eed24e6 100644 (file)
@@ -984,9 +984,7 @@ ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
        if (ah->ah_version == AR5K_AR5210)
                return;
 
-       ee_mode = ath5k_eeprom_mode_from_channel(channel);
-       if (WARN_ON(ee_mode < 0))
-               return;
+       ee_mode = ath5k_eeprom_mode_from_channel(ah, channel);
 
        /* Adjust power delta for channel 14 */
        if (channel->center_freq == 2484)
index 630c83db056e8cf45e2a7839b7f64345170217f5..e39e5860a2e9347a2d44318e69b07410c4bd2dc5 100644 (file)
@@ -30,6 +30,15 @@ config ATH6KL_DEBUG
        ---help---
          Enables debug support
 
+config ATH6KL_TRACING
+       bool "Atheros ath6kl tracing support"
+       depends on ATH6KL
+       depends on EVENT_TRACING
+       ---help---
+         Select this to ath6kl use tracing infrastructure.
+
+         If unsure, say Y to make it easier to debug problems.
+
 config ATH6KL_REGDOMAIN
        bool "Atheros ath6kl regdomain support"
        depends on ATH6KL
index cab0ec0d5380afde712ac83bf23a2eaab514cbb8..dc2b3b46781e190a59558b41d8cbd87b3e409df6 100644 (file)
@@ -35,10 +35,15 @@ ath6kl_core-y += txrx.o
 ath6kl_core-y += wmi.o
 ath6kl_core-y += core.o
 ath6kl_core-y += recovery.o
+
 ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o
+ath6kl_core-$(CONFIG_ATH6KL_TRACING) += trace.o
 
 obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o
 ath6kl_sdio-y += sdio.o
 
 obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o
 ath6kl_usb-y += usb.o
+
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
index 752ffc4f4166df35aaf782d7895ebb9e44fe3bdd..5c9736a94e544d0c9008480ac994c593d1b4d269 100644 (file)
@@ -402,7 +402,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
        if (type == NL80211_IFTYPE_STATION ||
            type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
                for (i = 0; i < ar->vif_max; i++) {
-                       if ((ar->avail_idx_map >> i) & BIT(0)) {
+                       if ((ar->avail_idx_map) & BIT(i)) {
                                *if_idx = i;
                                return true;
                        }
@@ -412,7 +412,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
        if (type == NL80211_IFTYPE_P2P_CLIENT ||
            type == NL80211_IFTYPE_P2P_GO) {
                for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
-                       if ((ar->avail_idx_map >> i) & BIT(0)) {
+                       if ((ar->avail_idx_map) & BIT(i)) {
                                *if_idx = i;
                                return true;
                        }
@@ -1535,7 +1535,9 @@ static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
 
        ath6kl_cfg80211_vif_stop(vif, test_bit(WMI_READY, &ar->flag));
 
+       rtnl_lock();
        ath6kl_cfg80211_vif_cleanup(vif);
+       rtnl_unlock();
 
        return 0;
 }
@@ -2990,13 +2992,15 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
 {
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
+       int err;
 
        if (vif->nw_type != AP_NETWORK)
                return -EOPNOTSUPP;
 
-       /* Use this only for authorizing/unauthorizing a station */
-       if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
-               return -EOPNOTSUPP;
+       err = cfg80211_check_station_change(wiphy, params,
+                                           CFG80211_STA_AP_MLME_CLIENT);
+       if (err)
+               return err;
 
        if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
                return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
@@ -3659,7 +3663,6 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
        vif->sme_state = SME_DISCONNECTED;
        set_bit(WLAN_ENABLED, &vif->flags);
        ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
-       set_bit(NETDEV_REGISTERED, &vif->flags);
 
        if (type == NL80211_IFTYPE_ADHOC)
                ar->ibss_if_active = true;
index 61b2f98b4e775afb9f0f4b17e4af8bed68baa59d..26b0f92424e16fb91beca55f3f07e9d907a9db04 100644 (file)
@@ -560,7 +560,6 @@ enum ath6kl_vif_state {
        WMM_ENABLED,
        NETQ_STOPPED,
        DTIM_EXPIRED,
-       NETDEV_REGISTERED,
        CLEAR_BSSFILTER_ON_BEACON,
        DTIM_PERIOD_AVAIL,
        WLAN_ENABLED,
@@ -936,8 +935,6 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid, u16 seq_no,
                             u8 win_sz);
 void ath6kl_wakeup_event(void *dev);
 
-void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
-                        bool wait_fot_compltn, bool cold_reset);
 void ath6kl_init_control_info(struct ath6kl_vif *vif);
 struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar);
 void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready);
index 15cfe30e54fdf1dfb8a8c8548a2c6826bc0de98e..fe38b836cb2651a77726af4e3d7772b591d46427 100644 (file)
@@ -56,6 +56,60 @@ int ath6kl_printk(const char *level, const char *fmt, ...)
 }
 EXPORT_SYMBOL(ath6kl_printk);
 
+int ath6kl_info(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath6kl_printk(KERN_INFO, "%pV", &vaf);
+       trace_ath6kl_log_info(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath6kl_info);
+
+int ath6kl_err(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath6kl_printk(KERN_ERR, "%pV", &vaf);
+       trace_ath6kl_log_err(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath6kl_err);
+
+int ath6kl_warn(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath6kl_printk(KERN_WARNING, "%pV", &vaf);
+       trace_ath6kl_log_warn(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath6kl_warn);
+
 #ifdef CONFIG_ATH6KL_DEBUG
 
 void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...)
@@ -63,15 +117,15 @@ void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...)
        struct va_format vaf;
        va_list args;
 
-       if (!(debug_mask & mask))
-               return;
-
        va_start(args, fmt);
 
        vaf.fmt = fmt;
        vaf.va = &args;
 
-       ath6kl_printk(KERN_DEBUG, "%pV", &vaf);
+       if (debug_mask & mask)
+               ath6kl_printk(KERN_DEBUG, "%pV", &vaf);
+
+       trace_ath6kl_log_dbg(mask, &vaf);
 
        va_end(args);
 }
@@ -87,6 +141,10 @@ void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
 
                print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
        }
+
+       /* tracing code doesn't like null strings :/ */
+       trace_ath6kl_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
+                                 buf, len);
 }
 EXPORT_SYMBOL(ath6kl_dbg_dump);
 
@@ -1752,8 +1810,10 @@ int ath6kl_debug_init_fs(struct ath6kl *ar)
        debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
                            &fops_tgt_stats);
 
-       debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar,
-                           &fops_credit_dist_stats);
+       if (ar->hif_type == ATH6KL_HIF_TYPE_SDIO)
+               debugfs_create_file("credit_dist_stats", S_IRUSR,
+                                   ar->debugfs_phy, ar,
+                                   &fops_credit_dist_stats);
 
        debugfs_create_file("endpoint_stats", S_IRUSR | S_IWUSR,
                            ar->debugfs_phy, ar, &fops_endpoint_stats);
index f97cd4ead543fdd239220372cc6846539343d1c6..74369de00fb57a40dcebd72ced2002d0cb571b24 100644 (file)
@@ -19,6 +19,7 @@
 #define DEBUG_H
 
 #include "hif.h"
+#include "trace.h"
 
 enum ATH6K_DEBUG_MASK {
        ATH6KL_DBG_CREDIT       = BIT(0),
@@ -51,13 +52,9 @@ enum ATH6K_DEBUG_MASK {
 extern unsigned int debug_mask;
 extern __printf(2, 3)
 int ath6kl_printk(const char *level, const char *fmt, ...);
-
-#define ath6kl_info(fmt, ...)                          \
-       ath6kl_printk(KERN_INFO, fmt, ##__VA_ARGS__)
-#define ath6kl_err(fmt, ...)                                   \
-       ath6kl_printk(KERN_ERR, fmt, ##__VA_ARGS__)
-#define ath6kl_warn(fmt, ...)                                  \
-       ath6kl_printk(KERN_WARNING, fmt, ##__VA_ARGS__)
+extern __printf(1, 2) int ath6kl_info(const char *fmt, ...);
+extern __printf(1, 2) int ath6kl_err(const char *fmt, ...);
+extern __printf(1, 2) int ath6kl_warn(const char *fmt, ...);
 
 enum ath6kl_war {
        ATH6KL_WAR_INVALID_RATE,
index a6b614421fa409814e1662a78df1507da904b970..fea7709b5dda59aa26fa0fbc5eb70caccea77918 100644 (file)
@@ -22,6 +22,7 @@
 #include "target.h"
 #include "hif-ops.h"
 #include "debug.h"
+#include "trace.h"
 
 #define MAILBOX_FOR_BLOCK_SIZE          1
 
@@ -436,6 +437,8 @@ static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
 
                ath6kl_dump_registers(dev, &dev->irq_proc_reg,
                                      &dev->irq_en_reg);
+               trace_ath6kl_sdio_irq(&dev->irq_en_reg,
+                                     sizeof(dev->irq_en_reg));
 
                /* Update only those registers that are enabled */
                host_int_status = dev->irq_proc_reg.host_int_status &
index fbb78dfe078f692ad94c34d80bf9dbf7ec2e3a06..65e5b719093d47943b3135e902cf68001eefd988 100644 (file)
@@ -19,6 +19,8 @@
 #include "hif.h"
 #include "debug.h"
 #include "hif-ops.h"
+#include "trace.h"
+
 #include <asm/unaligned.h>
 
 #define CALC_TXRX_PADDED_LEN(dev, len)  (__ALIGN_MASK((len), (dev)->block_mask))
@@ -537,6 +539,8 @@ static int ath6kl_htc_tx_issue(struct htc_target *target,
                                packet->buf, padded_len,
                                HIF_WR_ASYNC_BLOCK_INC, packet);
 
+       trace_ath6kl_htc_tx(status, packet->endpoint, packet->buf, send_len);
+
        return status;
 }
 
@@ -757,7 +761,8 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
 {
        struct htc_target *target = endpoint->target;
        struct hif_scatter_req *scat_req = NULL;
-       int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0;
+       int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0, i;
+       struct htc_packet *packet;
        int status;
        u32 txb_mask;
        u8 ac = WMM_NUM_AC;
@@ -832,6 +837,13 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
                ath6kl_dbg(ATH6KL_DBG_HTC,
                           "htc tx scatter bytes %d entries %d\n",
                           scat_req->len, scat_req->scat_entries);
+
+               for (i = 0; i < scat_req->scat_entries; i++) {
+                       packet = scat_req->scat_list[i].packet;
+                       trace_ath6kl_htc_tx(packet->status, packet->endpoint,
+                                           packet->buf, packet->act_len);
+               }
+
                ath6kl_hif_submit_scat_req(target->dev, scat_req, false);
 
                if (status)
@@ -1903,6 +1915,7 @@ static void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint,
                ath6kl_dbg(ATH6KL_DBG_HTC,
                           "htc rx complete ep %d packet 0x%p\n",
                           endpoint->eid, packet);
+
                endpoint->ep_cb.rx(endpoint->target, packet);
 }
 
@@ -2011,6 +2024,9 @@ static int ath6kl_htc_rx_process_packets(struct htc_target *target,
        list_for_each_entry_safe(packet, tmp_pkt, comp_pktq, list) {
                ep = &target->endpoint[packet->endpoint];
 
+               trace_ath6kl_htc_rx(packet->status, packet->endpoint,
+                                   packet->buf, packet->act_len);
+
                /* process header for each of the recv packet */
                status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds,
                                                   n_lk_ahd);
@@ -2291,6 +2307,9 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target)
        if (ath6kl_htc_rx_packet(target, packet, packet->act_len))
                goto fail_ctrl_rx;
 
+       trace_ath6kl_htc_rx(packet->status, packet->endpoint,
+                           packet->buf, packet->act_len);
+
        /* process receive header */
        packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL);
 
index 281390178e3d9d062bb7c8afbf8e06de6364f39e..67aa924ed8b317f9d5240ad1560a820553790c24 100644 (file)
@@ -988,8 +988,6 @@ static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb,
 
        htc_hdr = (struct htc_frame_hdr *) netdata;
 
-       ep = &target->endpoint[htc_hdr->eid];
-
        if (htc_hdr->eid >= ENDPOINT_MAX) {
                ath6kl_dbg(ATH6KL_DBG_HTC,
                           "HTC Rx: invalid EndpointID=%d\n",
@@ -997,6 +995,7 @@ static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb,
                status = -EINVAL;
                goto free_skb;
        }
+       ep = &target->endpoint[htc_hdr->eid];
 
        payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len));
 
@@ -1168,8 +1167,8 @@ static int htc_wait_recv_ctrl_message(struct htc_target *target)
        }
 
        if (count <= 0) {
-               ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Timeout!\n", __func__);
-               return -ECOMM;
+               ath6kl_warn("htc pipe control receive timeout!\n");
+               return -ETIMEDOUT;
        }
 
        return 0;
@@ -1582,16 +1581,16 @@ static int ath6kl_htc_pipe_wait_target(struct htc_target *target)
                return status;
 
        if (target->pipe.ctrl_response_len < sizeof(*ready_msg)) {
-               ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg len:%d!\n",
-                          target->pipe.ctrl_response_len);
+               ath6kl_warn("invalid htc pipe ready msg len: %d\n",
+                           target->pipe.ctrl_response_len);
                return -ECOMM;
        }
 
        ready_msg = (struct htc_ready_ext_msg *) target->pipe.ctrl_response_buf;
 
        if (ready_msg->ver2_0_info.msg_id != cpu_to_le16(HTC_MSG_READY_ID)) {
-               ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg : 0x%X !\n",
-                          ready_msg->ver2_0_info.msg_id);
+               ath6kl_warn("invalid htc pipe ready msg: 0x%x\n",
+                           ready_msg->ver2_0_info.msg_id);
                return -ECOMM;
        }
 
index 5d434cf88f35adb52e4d28cd37cf7ea1cc2c50e0..40ffee6184fd3b680037da63e0d35abcf44b4b6e 100644 (file)
@@ -201,8 +201,8 @@ struct sk_buff *ath6kl_buf_alloc(int size)
        u16 reserved;
 
        /* Add chacheline space at front and back of buffer */
-       reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET +
-                  sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES;
+       reserved = roundup((2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET +
+                  sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES, 4);
        skb = dev_alloc_skb(size + reserved);
 
        if (skb)
@@ -1549,10 +1549,89 @@ static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type)
        return NULL;
 }
 
+
+static const struct fw_capa_str_map {
+       int id;
+       const char *name;
+} fw_capa_map[] = {
+       { ATH6KL_FW_CAPABILITY_HOST_P2P, "host-p2p" },
+       { ATH6KL_FW_CAPABILITY_SCHED_SCAN, "sched-scan" },
+       { ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, "sta-p2pdev-duplex" },
+       { ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, "inactivity-timeout" },
+       { ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, "rsn-cap-override" },
+       { ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, "wow-mc-filter" },
+       { ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, "bmiss-enhance" },
+       { ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, "sscan-match-list" },
+       { ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, "rssi-scan-thold" },
+       { ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, "custom-mac-addr" },
+       { ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, "tx-err-notify" },
+       { ATH6KL_FW_CAPABILITY_REGDOMAIN, "regdomain" },
+       { ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, "sched-scan-v2" },
+       { ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, "hb-poll" },
+};
+
+static const char *ath6kl_init_get_fw_capa_name(unsigned int id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(fw_capa_map); i++) {
+               if (fw_capa_map[i].id == id)
+                       return fw_capa_map[i].name;
+       }
+
+       return "<unknown>";
+}
+
+static void ath6kl_init_get_fwcaps(struct ath6kl *ar, char *buf, size_t buf_len)
+{
+       u8 *data = (u8 *) ar->fw_capabilities;
+       size_t trunc_len, len = 0;
+       int i, index, bit;
+       char *trunc = "...";
+
+       for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) {
+               index = i / 8;
+               bit = i % 8;
+
+               if (index >= sizeof(ar->fw_capabilities) * 4)
+                       break;
+
+               if (buf_len - len < 4) {
+                       ath6kl_warn("firmware capability buffer too small!\n");
+
+                       /* add "..." to the end of string */
+                       trunc_len = strlen(trunc) + 1;
+                       strncpy(buf + buf_len - trunc_len, trunc, trunc_len);
+
+                       return;
+               }
+
+               if (data[index] & (1 << bit)) {
+                       len += scnprintf(buf + len, buf_len - len, "%s,",
+                                           ath6kl_init_get_fw_capa_name(i));
+               }
+       }
+
+       /* overwrite the last comma */
+       if (len > 0)
+               len--;
+
+       buf[len] = '\0';
+}
+
+static int ath6kl_init_hw_reset(struct ath6kl *ar)
+{
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "cold resetting the device");
+
+       return ath6kl_diag_write32(ar, RESET_CONTROL_ADDRESS,
+                                  cpu_to_le32(RESET_CONTROL_COLD_RST));
+}
+
 static int __ath6kl_init_hw_start(struct ath6kl *ar)
 {
        long timeleft;
        int ret, i;
+       char buf[200];
 
        ath6kl_dbg(ATH6KL_DBG_BOOT, "hw start\n");
 
@@ -1569,24 +1648,35 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar)
                goto err_power_off;
 
        /* Do we need to finish the BMI phase */
-       /* FIXME: return error from ath6kl_bmi_done() */
-       if (ath6kl_bmi_done(ar)) {
-               ret = -EIO;
+       ret = ath6kl_bmi_done(ar);
+       if (ret)
                goto err_power_off;
-       }
 
        /*
         * The reason we have to wait for the target here is that the
         * driver layer has to init BMI in order to set the host block
         * size.
         */
-       if (ath6kl_htc_wait_target(ar->htc_target)) {
-               ret = -EIO;
+       ret = ath6kl_htc_wait_target(ar->htc_target);
+
+       if (ret == -ETIMEDOUT) {
+               /*
+                * Most likely USB target is in odd state after reboot and
+                * needs a reset. A cold reset makes the whole device
+                * disappear from USB bus and initialisation starts from
+                * beginning.
+                */
+               ath6kl_warn("htc wait target timed out, resetting device\n");
+               ath6kl_init_hw_reset(ar);
+               goto err_power_off;
+       } else if (ret) {
+               ath6kl_err("htc wait target failed: %d\n", ret);
                goto err_power_off;
        }
 
-       if (ath6kl_init_service_ep(ar)) {
-               ret = -EIO;
+       ret = ath6kl_init_service_ep(ar);
+       if (ret) {
+               ath6kl_err("Endpoint service initilisation failed: %d\n", ret);
                goto err_cleanup_scatter;
        }
 
@@ -1617,6 +1707,8 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar)
                            ar->wiphy->fw_version,
                            ar->fw_api,
                            test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
+               ath6kl_init_get_fwcaps(ar, buf, sizeof(buf));
+               ath6kl_info("firmware supports: %s\n", buf);
        }
 
        if (ar->version.abi_ver != ATH6KL_ABI_VERSION) {
@@ -1765,9 +1857,7 @@ void ath6kl_stop_txrx(struct ath6kl *ar)
         * Try to reset the device if we can. The driver may have been
         * configure NOT to reset the target during a debug session.
         */
-       ath6kl_dbg(ATH6KL_DBG_TRC,
-                  "attempting to reset target on instance destroy\n");
-       ath6kl_reset_device(ar, ar->target_type, true, true);
+       ath6kl_init_hw_reset(ar);
 
        up(&ar->sem);
 }
index bd50b6b7b49267665e8854e388dd068fab63707b..d4fcfcad57d03df4d612708978286997c4c42a9e 100644 (file)
@@ -345,39 +345,6 @@ out:
        return ret;
 }
 
-/* FIXME: move to a better place, target.h? */
-#define AR6003_RESET_CONTROL_ADDRESS 0x00004000
-#define AR6004_RESET_CONTROL_ADDRESS 0x00004000
-
-void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
-                        bool wait_fot_compltn, bool cold_reset)
-{
-       int status = 0;
-       u32 address;
-       __le32 data;
-
-       if (target_type != TARGET_TYPE_AR6003 &&
-           target_type != TARGET_TYPE_AR6004)
-               return;
-
-       data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) :
-                           cpu_to_le32(RESET_CONTROL_MBOX_RST);
-
-       switch (target_type) {
-       case TARGET_TYPE_AR6003:
-               address = AR6003_RESET_CONTROL_ADDRESS;
-               break;
-       case TARGET_TYPE_AR6004:
-               address = AR6004_RESET_CONTROL_ADDRESS;
-               break;
-       }
-
-       status = ath6kl_diag_write32(ar, address, data);
-
-       if (status)
-               ath6kl_err("failed to reset target\n");
-}
-
 static void ath6kl_install_static_wep_keys(struct ath6kl_vif *vif)
 {
        u8 index;
@@ -1327,9 +1294,11 @@ void init_netdev(struct net_device *dev)
        dev->watchdog_timeo = ATH6KL_TX_TIMEOUT;
 
        dev->needed_headroom = ETH_HLEN;
-       dev->needed_headroom += sizeof(struct ath6kl_llc_snap_hdr) +
-                               sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH
-                               + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES;
+       dev->needed_headroom += roundup(sizeof(struct ath6kl_llc_snap_hdr) +
+                                       sizeof(struct wmi_data_hdr) +
+                                       HTC_HDR_LENGTH +
+                                       WMI_MAX_TX_META_SZ +
+                                       ATH6KL_HTC_ALIGN_BYTES, 4);
 
        dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
 
index d111980d44c0bb9b550b0733322b663a6842c6c8..fb141454c6d2f3c9de1d82135aeace81b69276fd 100644 (file)
@@ -28,6 +28,7 @@
 #include "target.h"
 #include "debug.h"
 #include "cfg80211.h"
+#include "trace.h"
 
 struct ath6kl_sdio {
        struct sdio_func *func;
@@ -179,6 +180,8 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
                   request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len);
        ath6kl_dbg_dump(ATH6KL_DBG_SDIO_DUMP, NULL, "sdio ", buf, len);
 
+       trace_ath6kl_sdio(addr, request, buf, len);
+
        return ret;
 }
 
@@ -309,6 +312,13 @@ static int ath6kl_sdio_scat_rw(struct ath6kl_sdio *ar_sdio,
        sdio_claim_host(ar_sdio->func);
 
        mmc_set_data_timeout(&data, ar_sdio->func->card);
+
+       trace_ath6kl_sdio_scat(scat_req->addr,
+                              scat_req->req,
+                              scat_req->len,
+                              scat_req->scat_entries,
+                              scat_req->scat_list);
+
        /* synchronous call to process request */
        mmc_wait_for_req(ar_sdio->func->card->host, &mmc_req);
 
@@ -1123,10 +1133,12 @@ static int ath6kl_sdio_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
 
        ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len,
                                          HIF_WR_SYNC_BYTE_INC);
-       if (ret)
+       if (ret) {
                ath6kl_err("unable to send the bmi data to the device\n");
+               return ret;
+       }
 
-       return ret;
+       return 0;
 }
 
 static int ath6kl_sdio_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
index a98c12ba70c1dfa32aa0d3fbd25a76852d5df22b..a580a629a0da6ba24b251dab8147e7de74c34f7c 100644 (file)
@@ -25,7 +25,7 @@
 #define AR6004_BOARD_DATA_SZ     6144
 #define AR6004_BOARD_EXT_DATA_SZ 0
 
-#define RESET_CONTROL_ADDRESS          0x00000000
+#define RESET_CONTROL_ADDRESS          0x00004000
 #define RESET_CONTROL_COLD_RST         0x00000100
 #define RESET_CONTROL_MBOX_RST         0x00000004
 
diff --git a/drivers/net/wireless/ath/ath6kl/trace.c b/drivers/net/wireless/ath/ath6kl/trace.c
new file mode 100644 (file)
index 0000000..e7d64b1
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL(ath6kl_sdio);
+EXPORT_TRACEPOINT_SYMBOL(ath6kl_sdio_scat);
diff --git a/drivers/net/wireless/ath/ath6kl/trace.h b/drivers/net/wireless/ath/ath6kl/trace.h
new file mode 100644 (file)
index 0000000..1a1ea78
--- /dev/null
@@ -0,0 +1,332 @@
+#if !defined(_ATH6KL_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+
+#include <net/cfg80211.h>
+#include <linux/skbuff.h>
+#include <linux/tracepoint.h>
+#include "wmi.h"
+#include "hif.h"
+
+#if !defined(_ATH6KL_TRACE_H)
+static inline unsigned int ath6kl_get_wmi_id(void *buf, size_t buf_len)
+{
+       struct wmi_cmd_hdr *hdr = buf;
+
+       if (buf_len < sizeof(*hdr))
+               return 0;
+
+       return le16_to_cpu(hdr->cmd_id);
+}
+#endif /* __ATH6KL_TRACE_H */
+
+#define _ATH6KL_TRACE_H
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_ATH6KL_TRACING)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_ATH6KL_TRACING || __CHECKER__ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ath6kl
+
+TRACE_EVENT(ath6kl_wmi_cmd,
+       TP_PROTO(void *buf, size_t buf_len),
+
+       TP_ARGS(buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, id)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = ath6kl_get_wmi_id(buf, buf_len);
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id %d len %zd",
+               __entry->id, __entry->buf_len
+       )
+);
+
+TRACE_EVENT(ath6kl_wmi_event,
+       TP_PROTO(void *buf, size_t buf_len),
+
+       TP_ARGS(buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, id)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = ath6kl_get_wmi_id(buf, buf_len);
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id %d len %zd",
+               __entry->id, __entry->buf_len
+       )
+);
+
+TRACE_EVENT(ath6kl_sdio,
+       TP_PROTO(unsigned int addr, int flags,
+                void *buf, size_t buf_len),
+
+       TP_ARGS(addr, flags, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, tx)
+               __field(unsigned int, addr)
+               __field(int, flags)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->addr = addr;
+               __entry->flags = flags;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+
+               if (flags & HIF_WRITE)
+                       __entry->tx = 1;
+               else
+                       __entry->tx = 0;
+       ),
+
+       TP_printk(
+               "%s addr 0x%x flags 0x%x len %zd\n",
+               __entry->tx ? "tx" : "rx",
+               __entry->addr,
+               __entry->flags,
+               __entry->buf_len
+       )
+);
+
+TRACE_EVENT(ath6kl_sdio_scat,
+       TP_PROTO(unsigned int addr, int flags, unsigned int total_len,
+                unsigned int entries, struct hif_scatter_item *list),
+
+       TP_ARGS(addr, flags, total_len, entries, list),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, tx)
+               __field(unsigned int, addr)
+               __field(int, flags)
+               __field(unsigned int, entries)
+               __field(size_t, total_len)
+               __dynamic_array(unsigned int, len_array, entries)
+               __dynamic_array(u8, data, total_len)
+       ),
+
+       TP_fast_assign(
+               unsigned int *len_array;
+               int i, offset = 0;
+               size_t len;
+
+               __entry->addr = addr;
+               __entry->flags = flags;
+               __entry->entries = entries;
+               __entry->total_len = total_len;
+
+               if (flags & HIF_WRITE)
+                       __entry->tx = 1;
+               else
+                       __entry->tx = 0;
+
+               len_array = __get_dynamic_array(len_array);
+
+               for (i = 0; i < entries; i++) {
+                       len = list[i].len;
+
+                       memcpy((u8 *) __get_dynamic_array(data) + offset,
+                              list[i].buf, len);
+
+                       len_array[i] = len;
+                       offset += len;
+               }
+       ),
+
+       TP_printk(
+               "%s addr 0x%x flags 0x%x entries %d total_len %zd\n",
+               __entry->tx ? "tx" : "rx",
+               __entry->addr,
+               __entry->flags,
+               __entry->entries,
+               __entry->total_len
+       )
+);
+
+TRACE_EVENT(ath6kl_sdio_irq,
+       TP_PROTO(void *buf, size_t buf_len),
+
+       TP_ARGS(buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "irq len %zd\n", __entry->buf_len
+       )
+);
+
+TRACE_EVENT(ath6kl_htc_rx,
+       TP_PROTO(int status, int endpoint, void *buf,
+                size_t buf_len),
+
+       TP_ARGS(status, endpoint, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(int, status)
+               __field(int, endpoint)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->status = status;
+               __entry->endpoint = endpoint;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "status %d endpoint %d len %zd\n",
+               __entry->status,
+               __entry->endpoint,
+               __entry->buf_len
+       )
+);
+
+TRACE_EVENT(ath6kl_htc_tx,
+       TP_PROTO(int status, int endpoint, void *buf,
+                size_t buf_len),
+
+       TP_ARGS(status, endpoint, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(int, status)
+               __field(int, endpoint)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->status = status;
+               __entry->endpoint = endpoint;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "status %d endpoint %d len %zd\n",
+               __entry->status,
+               __entry->endpoint,
+               __entry->buf_len
+       )
+);
+
+#define ATH6KL_MSG_MAX 200
+
+DECLARE_EVENT_CLASS(ath6kl_log_event,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf),
+       TP_STRUCT__entry(
+               __dynamic_array(char, msg, ATH6KL_MSG_MAX)
+       ),
+       TP_fast_assign(
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      ATH6KL_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= ATH6KL_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(ath6kl_log_event, ath6kl_log_err,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath6kl_log_event, ath6kl_log_warn,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath6kl_log_event, ath6kl_log_info,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+TRACE_EVENT(ath6kl_log_dbg,
+       TP_PROTO(unsigned int level, struct va_format *vaf),
+       TP_ARGS(level, vaf),
+       TP_STRUCT__entry(
+               __field(unsigned int, level)
+               __dynamic_array(char, msg, ATH6KL_MSG_MAX)
+       ),
+       TP_fast_assign(
+               __entry->level = level;
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      ATH6KL_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= ATH6KL_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+TRACE_EVENT(ath6kl_log_dbg_dump,
+       TP_PROTO(const char *msg, const char *prefix,
+                const void *buf, size_t buf_len),
+
+       TP_ARGS(msg, prefix, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __string(msg, msg)
+               __string(prefix, prefix)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __assign_str(msg, msg);
+               __assign_str(prefix, prefix);
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "%s/%s\n", __get_str(prefix), __get_str(msg)
+       )
+);
+
+#endif /* _ ATH6KL_TRACE_H || TRACE_HEADER_MULTI_READ*/
+
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 78b369286579dd954f98243a24e948db2a4f3c2b..ebb24045a8ae6cbcac844a239d11a05ee6f86e14 100644 (file)
@@ -20,6 +20,7 @@
 #include "core.h"
 #include "debug.h"
 #include "htc-ops.h"
+#include "trace.h"
 
 /*
  * tid - tid_mux0..tid_mux3
@@ -288,6 +289,8 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb,
        int status = 0;
        struct ath6kl_cookie *cookie = NULL;
 
+       trace_ath6kl_wmi_cmd(skb->data, skb->len);
+
        if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) {
                dev_kfree_skb(skb);
                return -EACCES;
@@ -1324,7 +1327,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
                   __func__, ar, ept, skb, packet->buf,
                   packet->act_len, status);
 
-       if (status || !(skb->data + HTC_HDR_LENGTH)) {
+       if (status || packet->act_len < HTC_HDR_LENGTH) {
                dev_kfree_skb(skb);
                return;
        }
index 5fcd342762dedfee03bfe899cd7dd2e3d2bd6382..bed0d337712d3a6427fe5cee0a66e585fbd92fa9 100644 (file)
@@ -856,11 +856,9 @@ static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb,
        int ret;
 
        if (size > 0) {
-               buf = kmalloc(size, GFP_KERNEL);
+               buf = kmemdup(data, size, GFP_KERNEL);
                if (buf == NULL)
                        return -ENOMEM;
-
-               memcpy(buf, data, size);
        }
 
        /* note: if successful returns number of bytes transfered */
@@ -872,8 +870,9 @@ static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb,
                              size, 1000);
 
        if (ret < 0) {
-               ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n",
-                          __func__, ret);
+               ath6kl_warn("Failed to submit usb control message: %d\n", ret);
+               kfree(buf);
+               return ret;
        }
 
        kfree(buf);
@@ -903,8 +902,9 @@ static int ath6kl_usb_submit_ctrl_in(struct ath6kl_usb *ar_usb,
                                 size, 2 * HZ);
 
        if (ret < 0) {
-               ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n",
-                          __func__, ret);
+               ath6kl_warn("Failed to read usb control message: %d\n", ret);
+               kfree(buf);
+               return ret;
        }
 
        memcpy((u8 *) data, buf, size);
@@ -961,8 +961,10 @@ static int ath6kl_usb_diag_read32(struct ath6kl *ar, u32 address, u32 *data)
                                ATH6KL_USB_CONTROL_REQ_DIAG_RESP,
                                ar_usb->diag_resp_buffer, &resp_len);
 
-       if (ret)
+       if (ret) {
+               ath6kl_warn("diag read32 failed: %d\n", ret);
                return ret;
+       }
 
        resp = (struct ath6kl_usb_ctrl_diag_resp_read *)
                ar_usb->diag_resp_buffer;
@@ -976,6 +978,7 @@ static int ath6kl_usb_diag_write32(struct ath6kl *ar, u32 address, __le32 data)
 {
        struct ath6kl_usb *ar_usb = ar->hif_priv;
        struct ath6kl_usb_ctrl_diag_cmd_write *cmd;
+       int ret;
 
        cmd = (struct ath6kl_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer;
 
@@ -984,12 +987,17 @@ static int ath6kl_usb_diag_write32(struct ath6kl *ar, u32 address, __le32 data)
        cmd->address = cpu_to_le32(address);
        cmd->value = data;
 
-       return ath6kl_usb_ctrl_msg_exchange(ar_usb,
-                                           ATH6KL_USB_CONTROL_REQ_DIAG_CMD,
-                                           (u8 *) cmd,
-                                           sizeof(*cmd),
-                                           0, NULL, NULL);
+       ret = ath6kl_usb_ctrl_msg_exchange(ar_usb,
+                                          ATH6KL_USB_CONTROL_REQ_DIAG_CMD,
+                                          (u8 *) cmd,
+                                          sizeof(*cmd),
+                                          0, NULL, NULL);
+       if (ret) {
+               ath6kl_warn("diag_write32 failed: %d\n", ret);
+               return ret;
+       }
 
+       return 0;
 }
 
 static int ath6kl_usb_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
@@ -1001,7 +1009,7 @@ static int ath6kl_usb_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
        ret = ath6kl_usb_submit_ctrl_in(ar_usb,
                                        ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP,
                                        0, 0, buf, len);
-       if (ret != 0) {
+       if (ret) {
                ath6kl_err("Unable to read the bmi data from the device: %d\n",
                           ret);
                return ret;
@@ -1019,7 +1027,7 @@ static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
        ret = ath6kl_usb_submit_ctrl_out(ar_usb,
                                         ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD,
                                         0, 0, buf, len);
-       if (ret != 0) {
+       if (ret) {
                ath6kl_err("unable to send the bmi data to the device: %d\n",
                           ret);
                return ret;
index d76b5bd81a0d50288633e6899ac9ba0f15f1746f..87aefb4c4c23c5d8355df9e3c30ba08a6855278e 100644 (file)
@@ -20,6 +20,7 @@
 #include "core.h"
 #include "debug.h"
 #include "testmode.h"
+#include "trace.h"
 #include "../regd.h"
 #include "../regd_common.h"
 
@@ -2028,6 +2029,9 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx,
                if (!sband)
                        continue;
 
+               if (WARN_ON(band >= ATH6KL_NUM_BANDS))
+                       break;
+
                ratemask = rates[band];
                supp_rates = sc->supp_rates[band].rates;
                num_rates = 0;
@@ -4086,6 +4090,8 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                return -EINVAL;
        }
 
+       trace_ath6kl_wmi_event(skb->data, skb->len);
+
        return ath6kl_wmi_proc_events(wmi, skb);
 }
 
index c55e5bbafc4676947d861d5203491a85230cbe38..9f589744a9f945b285ca1c2988d795488cbf7a47 100644 (file)
@@ -731,7 +731,8 @@ static bool ar9285_hw_cl_cal(struct ath_hw *ah, struct ath9k_channel *chan)
                if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
                                  AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) {
                        ath_dbg(common, CALIBRATE,
-                               "offset calibration failed to complete in 1ms; noisy environment?\n");
+                               "offset calibration failed to complete in %d ms; noisy environment?\n",
+                               AH_WAIT_TIMEOUT / 1000);
                        return false;
                }
                REG_CLR_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN);
@@ -745,7 +746,8 @@ static bool ar9285_hw_cl_cal(struct ath_hw *ah, struct ath9k_channel *chan)
        if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL,
                          0, AH_WAIT_TIMEOUT)) {
                ath_dbg(common, CALIBRATE,
-                       "offset calibration failed to complete in 1ms; noisy environment?\n");
+                       "offset calibration failed to complete in %d ms; noisy environment?\n",
+                       AH_WAIT_TIMEOUT / 1000);
                return false;
        }
 
@@ -841,7 +843,8 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
                                   AR_PHY_AGC_CONTROL_CAL,
                                   0, AH_WAIT_TIMEOUT)) {
                        ath_dbg(common, CALIBRATE,
-                               "offset calibration failed to complete in 1ms; noisy environment?\n");
+                               "offset calibration failed to complete in %d ms; noisy environment?\n",
+                               AH_WAIT_TIMEOUT / 1000);
                        return false;
                }
 
index f76c3ca07a4501603883bbcbe9feb901d4eca390..639ba7d18ea41d14fd1ef55913c8bbc9ac7d8d42 100644 (file)
@@ -1126,7 +1126,8 @@ skip_tx_iqcal:
                        ar9003_hw_rtt_disable(ah);
 
                ath_dbg(common, CALIBRATE,
-                       "offset calibration failed to complete in 1ms; noisy environment?\n");
+                       "offset calibration failed to complete in %d ms; noisy environment?\n",
+                       AH_WAIT_TIMEOUT / 1000);
                return false;
        }
 
index 881e989ea4701b0dc98c3229e196b5b2b2a5b70f..e6b92ff265fd6e878274a2d0b35660a5bfa9a1c5 100644 (file)
@@ -3606,6 +3606,12 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
        value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz);
        REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value);
 
+       if ((AR_SREV_9462(ah)) && (ah->rxchainmask == 0x2)) {
+               value = ar9003_hw_ant_ctrl_chain_get(ah, 1, is2ghz);
+               REG_RMW_FIELD(ah, switch_chain_reg[0],
+                             AR_SWITCH_TABLE_ALL, value);
+       }
+
        for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
                if ((ah->rxchainmask & BIT(chain)) ||
                    (ah->txchainmask & BIT(chain))) {
@@ -3772,6 +3778,17 @@ static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan)
                                          AR_PHY_EXT_ATTEN_CTL_2,
                                         };
 
+       if ((AR_SREV_9462(ah)) && (ah->rxchainmask == 0x2)) {
+               value = ar9003_hw_atten_chain_get(ah, 1, chan);
+               REG_RMW_FIELD(ah, ext_atten_reg[0],
+                             AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value);
+
+               value = ar9003_hw_atten_chain_get_margin(ah, 1, chan);
+               REG_RMW_FIELD(ah, ext_atten_reg[0],
+                             AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
+                             value);
+       }
+
        /* Test value. if 0 then attenuation is unused. Don't load anything. */
        for (i = 0; i < 3; i++) {
                if (ah->txchainmask & BIT(i)) {
index ccc42a71b43642f668aae77158c697e66af85896..999ab08c34e689fd9179b0335bc475bbd46bdfbe 100644 (file)
@@ -37,28 +37,28 @@ static const u32 ar9462_pciephy_clkreq_enable_L1_2p0[][2] = {
        /* Addr      allmodes  */
        {0x00018c00, 0x18253ede},
        {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0003580c},
+       {0x00018c08, 0x0003780c},
 };
 
 static const u32 ar9462_2p0_baseband_postamble[][5] = {
        /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a800d},
        {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a01ae},
-       {0x00009824, 0x5ac640de, 0x5ac640d0, 0x5ac640d0, 0x63c640da},
+       {0x00009824, 0x63c640de, 0x5ac640d0, 0x5ac640d0, 0x63c640da},
        {0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x09143e81},
        {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
        {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
        {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
-       {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
+       {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a2},
        {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
        {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8},
        {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e},
-       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3376605e, 0x32395d5e},
+       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32365a5e},
        {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
        {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
        {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
-       {0x00009e3c, 0xcf946222, 0xcf946222, 0xcfd5c782, 0xcfd5c282},
+       {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
        {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27},
        {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
        {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@@ -82,9 +82,9 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = {
        {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
        {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
        {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
-       {0x0000a3a4, 0x00000010, 0x00000010, 0x00000000, 0x00000000},
+       {0x0000a3a4, 0x00000050, 0x00000050, 0x00000000, 0x00000000},
        {0x0000a3a8, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa},
-       {0x0000a3ac, 0xaaaaaa00, 0xaaaaaa30, 0xaaaaaa00, 0xaaaaaa00},
+       {0x0000a3ac, 0xaaaaaa00, 0xaa30aa30, 0xaaaaaa00, 0xaaaaaa00},
        {0x0000a41c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
        {0x0000a420, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce},
        {0x0000a424, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
@@ -363,14 +363,14 @@ static const u32 ar9462_pciephy_clkreq_disable_L1_2p0[][2] = {
        /* Addr      allmodes  */
        {0x00018c00, 0x18213ede},
        {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0003580c},
+       {0x00018c08, 0x0003780c},
 };
 
 static const u32 ar9462_pciephy_pll_on_clkreq_disable_L1_2p0[][2] = {
        /* Addr      allmodes  */
        {0x00018c00, 0x18212ede},
        {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0003580c},
+       {0x00018c08, 0x0003780c},
 };
 
 static const u32 ar9462_2p0_radio_postamble_sys2ant[][5] = {
@@ -775,7 +775,7 @@ static const u32 ar9462_2p0_baseband_core[][2] = {
        {0x00009fc0, 0x803e4788},
        {0x00009fc4, 0x0001efb5},
        {0x00009fcc, 0x40000014},
-       {0x00009fd0, 0x01193b93},
+       {0x00009fd0, 0x0a193b93},
        {0x0000a20c, 0x00000000},
        {0x0000a220, 0x00000000},
        {0x0000a224, 0x00000000},
@@ -850,7 +850,7 @@ static const u32 ar9462_2p0_baseband_core[][2] = {
        {0x0000a7cc, 0x00000000},
        {0x0000a7d0, 0x00000000},
        {0x0000a7d4, 0x00000004},
-       {0x0000a7dc, 0x00000001},
+       {0x0000a7dc, 0x00000000},
        {0x0000a7f0, 0x80000000},
        {0x0000a8d0, 0x004b6a8e},
        {0x0000a8d4, 0x00000820},
@@ -886,7 +886,7 @@ static const u32 ar9462_modes_high_ob_db_tx_gain_table_2p0[][5] = {
        {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
        {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
        {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
-       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+       {0x0000a410, 0x000050da, 0x000050da, 0x000050de, 0x000050de},
        {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
        {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
@@ -906,20 +906,20 @@ static const u32 ar9462_modes_high_ob_db_tx_gain_table_2p0[][5] = {
        {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640},
        {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660},
        {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861},
-       {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81},
-       {0x0000a54c, 0x59025eb6, 0x59025eb6, 0x42001a83, 0x42001a83},
-       {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001c84, 0x44001c84},
+       {0x0000a548, 0x55025eb3, 0x55025eb3, 0x3e001a81, 0x3e001a81},
+       {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x42001a83, 0x42001a83},
+       {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001a84, 0x44001a84},
        {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3},
        {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5},
        {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9},
        {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb},
-       {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
-       {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
-       {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
-       {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
-       {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
-       {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
-       {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+       {0x0000a564, 0x751ffff6, 0x751ffff6, 0x56001eec, 0x56001eec},
+       {0x0000a568, 0x751ffff6, 0x751ffff6, 0x58001ef0, 0x58001ef0},
+       {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x5a001ef4, 0x5a001ef4},
+       {0x0000a570, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6},
+       {0x0000a574, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6},
+       {0x0000a578, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6},
+       {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6},
        {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
@@ -1053,7 +1053,6 @@ static const u32 ar9462_2p0_mac_core[][2] = {
        {0x00008044, 0x00000000},
        {0x00008048, 0x00000000},
        {0x0000804c, 0xffffffff},
-       {0x00008050, 0xffffffff},
        {0x00008054, 0x00000000},
        {0x00008058, 0x00000000},
        {0x0000805c, 0x000fc78f},
@@ -1117,9 +1116,9 @@ static const u32 ar9462_2p0_mac_core[][2] = {
        {0x000081f8, 0x00000000},
        {0x000081fc, 0x00000000},
        {0x00008240, 0x00100000},
-       {0x00008244, 0x0010f424},
+       {0x00008244, 0x0010f400},
        {0x00008248, 0x00000800},
-       {0x0000824c, 0x0001e848},
+       {0x0000824c, 0x0001e800},
        {0x00008250, 0x00000000},
        {0x00008254, 0x00000000},
        {0x00008258, 0x00000000},
index 1e8508530e984487cfce4aba50df5e6af8b0873b..7bdd726c7a8f747a13f96a6ed8c921a23dc6ddff 100644 (file)
@@ -369,7 +369,6 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
        struct ieee80211_channel *c = chan->chan;
        struct ath9k_hw_cal_data *caldata = ah->caldata;
 
-       chan->channelFlags &= (~CHANNEL_CW_INT);
        if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
                ath_dbg(common, CALIBRATE,
                        "NF did not complete in calibration window\n");
@@ -384,7 +383,6 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
                ath_dbg(common, CALIBRATE,
                        "noise floor failed detected; detected %d, threshold %d\n",
                        nf, nfThresh);
-               chan->channelFlags |= CHANNEL_CW_INT;
        }
 
        if (!caldata) {
@@ -410,7 +408,7 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
        int i, j;
 
        ah->caldata->channel = chan->channel;
-       ah->caldata->channelFlags = chan->channelFlags & ~CHANNEL_CW_INT;
+       ah->caldata->channelFlags = chan->channelFlags;
        ah->caldata->chanmode = chan->chanmode;
        h = ah->caldata->nfCalHist;
        default_nf = ath9k_hw_get_default_nf(ah, chan);
index 050ca4a4850d9b874ef1ddd271a2c2abd4fd9644..6102476a65dea79e1958d76376fc112bbafde049 100644 (file)
@@ -40,7 +40,7 @@
        x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN);      \
 } while (0)
 #define ATH_EP_RND(x, mul)                                             \
-       ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+       (((x) + ((mul)/2)) / (mul))
 
 int ath9k_cmn_padpos(__le16 frame_control);
 int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
index 3714b971d18ebe41a89d1453acc63462c682af8b..e6307b86363a3aee4d78ca7303f137c1da788dae 100644 (file)
@@ -537,6 +537,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
        PR("AMPDUs Completed:", a_completed);
        PR("AMPDUs Retried:  ", a_retries);
        PR("AMPDUs XRetried: ", a_xretries);
+       PR("TXERR Filtered:  ", txerr_filtered);
        PR("FIFO Underrun:   ", fifo_underrun);
        PR("TXOP Exceeded:   ", xtxop);
        PR("TXTIMER Expiry:  ", timer_exp);
@@ -756,6 +757,8 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
                        TX_STAT_INC(qnum, completed);
        }
 
+       if (ts->ts_status & ATH9K_TXERR_FILT)
+               TX_STAT_INC(qnum, txerr_filtered);
        if (ts->ts_status & ATH9K_TXERR_FIFO)
                TX_STAT_INC(qnum, fifo_underrun);
        if (ts->ts_status & ATH9K_TXERR_XTXOP)
@@ -1909,6 +1912,7 @@ static const char ath9k_gstrings_stats[][ETH_GSTRING_LEN] = {
        AMKSTR(d_tx_desc_cfg_err),
        AMKSTR(d_tx_data_underrun),
        AMKSTR(d_tx_delim_underrun),
+       "d_rx_crc_err",
        "d_rx_decrypt_crc_err",
        "d_rx_phy_err",
        "d_rx_mic_err",
@@ -1989,6 +1993,7 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw,
        AWDATA(data_underrun);
        AWDATA(delim_underrun);
 
+       AWDATA_RX(crc_err);
        AWDATA_RX(decrypt_crc_err);
        AWDATA_RX(phy_err);
        AWDATA_RX(mic_err);
@@ -2067,7 +2072,7 @@ int ath9k_init_debug(struct ath_hw *ah)
                            &fops_modal_eeprom);
        sc->rfs_chan_spec_scan = relay_open("spectral_scan",
                                            sc->debug.debugfs_phy,
-                                           262144, 4, &rfs_spec_scan_cb,
+                                           1024, 256, &rfs_spec_scan_cb,
                                            NULL);
        debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR,
                            sc->debug.debugfs_phy, sc,
index 410d6d8f1aa7b9e9ffffe9a1a6779c576fb236ea..794a7ec83a246cb85619aba6dfdb717c5955381c 100644 (file)
@@ -142,6 +142,7 @@ struct ath_interrupt_stats {
  * @a_completed: Total AMPDUs completed
  * @a_retries: No. of AMPDUs retried (SW)
  * @a_xretries: No. of AMPDUs dropped due to xretries
+ * @txerr_filtered: No. of frames with TXERR_FILT flag set.
  * @fifo_underrun: FIFO underrun occurrences
        Valid only for:
                - non-aggregate condition.
@@ -168,6 +169,7 @@ struct ath_tx_stats {
        u32 a_completed;
        u32 a_retries;
        u32 a_xretries;
+       u32 txerr_filtered;
        u32 fifo_underrun;
        u32 xtxop;
        u32 timer_exp;
index ecc81792f2dca53571006fc84ff51a8353358fe7..508f8b33f0efe86adde27a4d4f63b827efd53917 100644 (file)
@@ -193,9 +193,7 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
                DFS_STAT_INC(sc, pulses_processed);
                if (pd != NULL && pd->add_pulse(pd, &pe)) {
                        DFS_STAT_INC(sc, radar_detected);
-                       /*
-                        * TODO: forward radar event to DFS management layer
-                        */
+                       ieee80211_radar_detected(sc->hw);
                }
        }
 }
index 55d28072adeb3a1f5ca54a7e108dd5c8ae02d22f..b7611b7bbe437e289bd5f452c5cdf6d125a6c702 100644 (file)
@@ -105,6 +105,24 @@ static ssize_t write_file_dfs(struct file *file, const char __user *user_buf,
        return count;
 }
 
+static ssize_t write_file_simulate_radar(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+
+       ieee80211_radar_detected(sc->hw);
+
+       return count;
+}
+
+static const struct file_operations fops_simulate_radar = {
+       .write = write_file_simulate_radar,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 static const struct file_operations fops_dfs_stats = {
        .read = read_file_dfs,
        .write = write_file_dfs,
@@ -117,4 +135,6 @@ void ath9k_dfs_init_debug(struct ath_softc *sc)
 {
        debugfs_create_file("dfs_stats", S_IRUSR,
                            sc->debug.debugfs_phy, sc, &fops_dfs_stats);
+       debugfs_create_file("dfs_simulate_radar", S_IWUSR,
+                           sc->debug.debugfs_phy, sc, &fops_simulate_radar);
 }
index 07e25260c31d85084c8c680f69cda6ac85264e21..3473a797651af0a4cbf3c5119bff352f99cc3c98 100644 (file)
@@ -1669,6 +1669,104 @@ bool ath9k_hw_check_alive(struct ath_hw *ah)
 }
 EXPORT_SYMBOL(ath9k_hw_check_alive);
 
+static void ath9k_hw_init_mfp(struct ath_hw *ah)
+{
+       /* Setup MFP options for CCMP */
+       if (AR_SREV_9280_20_OR_LATER(ah)) {
+               /* Mask Retry(b11), PwrMgt(b12), MoreData(b13) to 0 in mgmt
+                * frames when constructing CCMP AAD. */
+               REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT,
+                             0xc7ff);
+               ah->sw_mgmt_crypto = false;
+       } else if (AR_SREV_9160_10_OR_LATER(ah)) {
+               /* Disable hardware crypto for management frames */
+               REG_CLR_BIT(ah, AR_PCU_MISC_MODE2,
+                           AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE);
+               REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
+                           AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT);
+               ah->sw_mgmt_crypto = true;
+       } else {
+               ah->sw_mgmt_crypto = true;
+       }
+}
+
+static void ath9k_hw_reset_opmode(struct ath_hw *ah,
+                                 u32 macStaId1, u32 saveDefAntenna)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       ENABLE_REGWRITE_BUFFER(ah);
+
+       REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(common->macaddr));
+       REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(common->macaddr + 4)
+                 | macStaId1
+                 | AR_STA_ID1_RTS_USE_DEF
+                 | (ah->config.ack_6mb ? AR_STA_ID1_ACKCTS_6MB : 0)
+                 | ah->sta_id1_defaults);
+       ath_hw_setbssidmask(common);
+       REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna);
+       ath9k_hw_write_associd(ah);
+       REG_WRITE(ah, AR_ISR, ~0);
+       REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
+
+       REGWRITE_BUFFER_FLUSH(ah);
+
+       ath9k_hw_set_operating_mode(ah, ah->opmode);
+}
+
+static void ath9k_hw_init_queues(struct ath_hw *ah)
+{
+       int i;
+
+       ENABLE_REGWRITE_BUFFER(ah);
+
+       for (i = 0; i < AR_NUM_DCU; i++)
+               REG_WRITE(ah, AR_DQCUMASK(i), 1 << i);
+
+       REGWRITE_BUFFER_FLUSH(ah);
+
+       ah->intr_txqs = 0;
+       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+               ath9k_hw_resettxqueue(ah, i);
+}
+
+/*
+ * For big endian systems turn on swapping for descriptors
+ */
+static void ath9k_hw_init_desc(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (AR_SREV_9100(ah)) {
+               u32 mask;
+               mask = REG_READ(ah, AR_CFG);
+               if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) {
+                       ath_dbg(common, RESET, "CFG Byte Swap Set 0x%x\n",
+                               mask);
+               } else {
+                       mask = INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB;
+                       REG_WRITE(ah, AR_CFG, mask);
+                       ath_dbg(common, RESET, "Setting CFG 0x%x\n",
+                               REG_READ(ah, AR_CFG));
+               }
+       } else {
+               if (common->bus_ops->ath_bus_type == ATH_USB) {
+                       /* Configure AR9271 target WLAN */
+                       if (AR_SREV_9271(ah))
+                               REG_WRITE(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB);
+                       else
+                               REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
+               }
+#ifdef __BIG_ENDIAN
+               else if (AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
+                        AR_SREV_9550(ah))
+                       REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0);
+               else
+                       REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
+#endif
+       }
+}
+
 /*
  * Fast channel change:
  * (Change synthesizer based on channel freq without resetting chip)
@@ -1746,7 +1844,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        u32 saveDefAntenna;
        u32 macStaId1;
        u64 tsf = 0;
-       int i, r;
+       int r;
        bool start_mci_reset = false;
        bool save_fullsleep = ah->chip_fullsleep;
 
@@ -1763,10 +1861,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
                ath9k_hw_getnf(ah, ah->curchan);
 
        ah->caldata = caldata;
-       if (caldata &&
-           (chan->channel != caldata->channel ||
-            (chan->channelFlags & ~CHANNEL_CW_INT) !=
-            (caldata->channelFlags & ~CHANNEL_CW_INT))) {
+       if (caldata && (chan->channel != caldata->channel ||
+                       chan->channelFlags != caldata->channelFlags)) {
                /* Operating channel changed, reset channel calibration data */
                memset(caldata, 0, sizeof(*caldata));
                ath9k_init_nfcal_hist_buffer(ah, chan);
@@ -1853,22 +1949,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
                ath9k_hw_settsf64(ah, tsf);
        }
 
-       /* Setup MFP options for CCMP */
-       if (AR_SREV_9280_20_OR_LATER(ah)) {
-               /* Mask Retry(b11), PwrMgt(b12), MoreData(b13) to 0 in mgmt
-                * frames when constructing CCMP AAD. */
-               REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT,
-                             0xc7ff);
-               ah->sw_mgmt_crypto = false;
-       } else if (AR_SREV_9160_10_OR_LATER(ah)) {
-               /* Disable hardware crypto for management frames */
-               REG_CLR_BIT(ah, AR_PCU_MISC_MODE2,
-                           AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE);
-               REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
-                           AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT);
-               ah->sw_mgmt_crypto = true;
-       } else
-               ah->sw_mgmt_crypto = true;
+       ath9k_hw_init_mfp(ah);
 
        if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
                ath9k_hw_set_delta_slope(ah, chan);
@@ -1876,24 +1957,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        ath9k_hw_spur_mitigate_freq(ah, chan);
        ah->eep_ops->set_board_values(ah, chan);
 
-       ENABLE_REGWRITE_BUFFER(ah);
-
-       REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(common->macaddr));
-       REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(common->macaddr + 4)
-                 | macStaId1
-                 | AR_STA_ID1_RTS_USE_DEF
-                 | (ah->config.
-                    ack_6mb ? AR_STA_ID1_ACKCTS_6MB : 0)
-                 | ah->sta_id1_defaults);
-       ath_hw_setbssidmask(common);
-       REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna);
-       ath9k_hw_write_associd(ah);
-       REG_WRITE(ah, AR_ISR, ~0);
-       REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
-
-       REGWRITE_BUFFER_FLUSH(ah);
-
-       ath9k_hw_set_operating_mode(ah, ah->opmode);
+       ath9k_hw_reset_opmode(ah, macStaId1, saveDefAntenna);
 
        r = ath9k_hw_rf_set_freq(ah, chan);
        if (r)
@@ -1901,17 +1965,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ath9k_hw_set_clockrate(ah);
 
-       ENABLE_REGWRITE_BUFFER(ah);
-
-       for (i = 0; i < AR_NUM_DCU; i++)
-               REG_WRITE(ah, AR_DQCUMASK(i), 1 << i);
-
-       REGWRITE_BUFFER_FLUSH(ah);
-
-       ah->intr_txqs = 0;
-       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
-               ath9k_hw_resettxqueue(ah, i);
-
+       ath9k_hw_init_queues(ah);
        ath9k_hw_init_interrupt_masks(ah, ah->opmode);
        ath9k_hw_ani_cache_ini_regs(ah);
        ath9k_hw_init_qos(ah);
@@ -1966,38 +2020,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        REGWRITE_BUFFER_FLUSH(ah);
 
-       /*
-        * For big endian systems turn on swapping for descriptors
-        */
-       if (AR_SREV_9100(ah)) {
-               u32 mask;
-               mask = REG_READ(ah, AR_CFG);
-               if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) {
-                       ath_dbg(common, RESET, "CFG Byte Swap Set 0x%x\n",
-                               mask);
-               } else {
-                       mask =
-                               INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB;
-                       REG_WRITE(ah, AR_CFG, mask);
-                       ath_dbg(common, RESET, "Setting CFG 0x%x\n",
-                               REG_READ(ah, AR_CFG));
-               }
-       } else {
-               if (common->bus_ops->ath_bus_type == ATH_USB) {
-                       /* Configure AR9271 target WLAN */
-                       if (AR_SREV_9271(ah))
-                               REG_WRITE(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB);
-                       else
-                               REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
-               }
-#ifdef __BIG_ENDIAN
-               else if (AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
-                        AR_SREV_9550(ah))
-                       REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0);
-               else
-                       REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
-#endif
-       }
+       ath9k_hw_init_desc(ah);
 
        if (ath9k_hw_btcoex_is_enabled(ah))
                ath9k_hw_btcoex_enable(ah);
@@ -2010,7 +2033,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        if (AR_SREV_9300_20_OR_LATER(ah)) {
                ar9003_hw_bb_watchdog_config(ah);
-
                ar9003_hw_disable_phy_restart(ah);
        }
 
@@ -2358,8 +2380,11 @@ static bool ath9k_hw_dfs_tested(struct ath_hw *ah)
 {
 
        switch (ah->hw_version.macVersion) {
+       /* for temporary testing DFS with 9280 */
+       case AR_SREV_VERSION_9280:
        /* AR9580 will likely be our first target to get testing on */
        case AR_SREV_VERSION_9580:
+               return true;
        default:
                return false;
        }
index 784e81ccb9031ed6dd3d2f29ad1dcda0faa9f4f7..30e62d92d46d79bdf8b512e5c57bdcb8545676eb 100644 (file)
@@ -363,7 +363,6 @@ enum ath9k_int {
        ATH9K_INT_NOCARD = 0xffffffff
 };
 
-#define CHANNEL_CW_INT    0x00002
 #define CHANNEL_CCK       0x00020
 #define CHANNEL_OFDM      0x00040
 #define CHANNEL_2GHZ      0x00080
index af932c9444def08a14e8826948936ba33e73342c..3be2eb0da84a42d5056e512de489beb4d2757bea 100644 (file)
@@ -319,6 +319,10 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
                ath9k_ps_wakeup(sc);
                ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
                sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
+               /* synchronize DFS detector if regulatory domain changed */
+               if (sc->dfs_detector != NULL)
+                       sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
+                                                        request->dfs_region);
                ath9k_ps_restore(sc);
        }
 }
@@ -727,12 +731,28 @@ static const struct ieee80211_iface_limit if_limits[] = {
                                 BIT(NL80211_IFTYPE_P2P_GO) },
 };
 
-static const struct ieee80211_iface_combination if_comb = {
-       .limits = if_limits,
-       .n_limits = ARRAY_SIZE(if_limits),
-       .max_interfaces = 2048,
-       .num_different_channels = 1,
-       .beacon_int_infra_match = true,
+
+static const struct ieee80211_iface_limit if_dfs_limits[] = {
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) },
+};
+
+static const struct ieee80211_iface_combination if_comb[] = {
+       {
+               .limits = if_limits,
+               .n_limits = ARRAY_SIZE(if_limits),
+               .max_interfaces = 2048,
+               .num_different_channels = 1,
+               .beacon_int_infra_match = true,
+       },
+       {
+               .limits = if_dfs_limits,
+               .n_limits = ARRAY_SIZE(if_dfs_limits),
+               .max_interfaces = 1,
+               .num_different_channels = 1,
+               .beacon_int_infra_match = true,
+               .radar_detect_widths =  BIT(NL80211_CHAN_NO_HT) |
+                                       BIT(NL80211_CHAN_HT20),
+       }
 };
 
 void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
@@ -763,8 +783,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                BIT(NL80211_IFTYPE_ADHOC) |
                BIT(NL80211_IFTYPE_MESH_POINT);
 
-       hw->wiphy->iface_combinations = &if_comb;
-       hw->wiphy->n_iface_combinations = 1;
+       hw->wiphy->iface_combinations = if_comb;
+       hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
 
        if (AR_SREV_5416(sc->sc_ah))
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
index 988372d218a4dd24015a1b19d104839fab8f0080..1bf52c88004a7b650d2385dbb459f110e0a033b5 100644 (file)
@@ -1249,10 +1249,27 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                if (old_pos >= 0)
                        ath_update_survey_nf(sc, old_pos);
 
-               /* perform spectral scan if requested. */
-               if (sc->scanning && sc->spectral_mode == SPECTRAL_CHANSCAN)
-                       ath9k_spectral_scan_trigger(hw);
-
+               /*
+                * Enable radar pulse detection if on a DFS channel. Spectral
+                * scanning and radar detection can not be used concurrently.
+                */
+               if (hw->conf.radar_enabled) {
+                       u32 rxfilter;
+
+                       /* set HW specific DFS configuration */
+                       ath9k_hw_set_radar_params(ah);
+                       rxfilter = ath9k_hw_getrxfilter(ah);
+                       rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
+                                   ATH9K_RX_FILTER_PHYERR;
+                       ath9k_hw_setrxfilter(ah, rxfilter);
+                       ath_dbg(common, DFS, "DFS enabled at freq %d\n",
+                               curchan->center_freq);
+               } else {
+                       /* perform spectral scan if requested. */
+                       if (sc->scanning &&
+                           sc->spectral_mode == SPECTRAL_CHANSCAN)
+                               ath9k_spectral_scan_trigger(hw);
+               }
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
@@ -1749,7 +1766,7 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
        mutex_unlock(&sc->mutex);
 }
 
-static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
+static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
index ee156e5431472dcf8e1428a78a6bb102bd51c8b4..ee7ca5aecdb0648fa4f65b44c5febc4990d46429 100644 (file)
@@ -381,6 +381,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
        rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
                | ATH9K_RX_FILTER_MCAST;
 
+       /* if operating on a DFS channel, enable radar pulse detection */
+       if (sc->hw->conf.radar_enabled)
+               rfilt |= ATH9K_RX_FILTER_PHYRADAR | ATH9K_RX_FILTER_PHYERR;
+
        if (sc->rx.rxfilter & FIF_PROBE_REQ)
                rfilt |= ATH9K_RX_FILTER_PROBEREQ;
 
@@ -1228,6 +1232,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                    unlikely(tsf_lower - rs.rs_tstamp > 0x10000000))
                        rxs->mactime += 0x100000000ULL;
 
+               if (rs.rs_phyerr == ATH9K_PHYERR_RADAR)
+                       ath9k_dfs_process_phyerr(sc, hdr, &rs, rxs->mactime);
+
                if (rs.rs_status & ATH9K_RXERR_PHY) {
                        if (ath_process_fft(sc, hdr, &rs, rxs->mactime)) {
                                RX_STAT_INC(rx_spectral);
index 25599741cd8a5863fe3879afa288f07b05ead23a..9dce106cd6d4a1b963910a8310737be18929867a 100644 (file)
 
 static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 3, 2, 1, 0 };
 
-enum carl9170_rf_init_mode {
-       CARL9170_RFI_NONE,
-       CARL9170_RFI_WARM,
-       CARL9170_RFI_COLD,
-};
-
 #define CARL9170_MAX_RX_BUFFER_SIZE            8192
 
 enum carl9170_device_state {
@@ -599,7 +593,7 @@ int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state);
 
 /* PHY / RF */
 int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
-       enum nl80211_channel_type bw, enum carl9170_rf_init_mode rfi);
+                        enum nl80211_channel_type bw);
 int carl9170_get_noisefloor(struct ar9170 *ar);
 
 /* FW */
index 93fe6003a493827dc61e0418607860c90de36d5d..40109be81f7c7f468386ebb07fcc7c4ebcda6dcf 100644 (file)
@@ -655,7 +655,7 @@ static ssize_t carl9170_debugfs_bug_write(struct ar9170 *ar, const char *buf,
 
        case 'P':
                err = carl9170_set_channel(ar, ar->hw->conf.channel,
-                       ar->hw->conf.channel_type, CARL9170_RFI_COLD);
+                                          ar->hw->conf.channel_type);
                if (err < 0)
                        count = err;
 
index f293b3ff475628a389c1337df81d24cb3b3b5640..699c557bc2c778bf51494626a6b0b6d6056dd1da 100644 (file)
@@ -939,7 +939,7 @@ static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed)
                        goto out;
 
                err = carl9170_set_channel(ar, hw->conf.channel,
-                       hw->conf.channel_type, CARL9170_RFI_NONE);
+                                          hw->conf.channel_type);
                if (err)
                        goto out;
 
@@ -1703,7 +1703,7 @@ found:
        return 0;
 }
 
-static void carl9170_op_flush(struct ieee80211_hw *hw, bool drop)
+static void carl9170_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct ar9170 *ar = hw->priv;
        unsigned int vid;
index b72c09cf43a4c614a56d5b63ca1f34f11b28d4f0..07f82234c860e35eebbddded079ef267dc2f13ee 100644 (file)
@@ -1569,16 +1569,14 @@ static enum carl9170_bw nl80211_to_carl(enum nl80211_channel_type type)
 }
 
 int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
-                        enum nl80211_channel_type _bw,
-                        enum carl9170_rf_init_mode rfi)
+                        enum nl80211_channel_type _bw)
 {
        const struct carl9170_phy_freq_params *freqpar;
        struct carl9170_rf_init_result rf_res;
        struct carl9170_rf_init rf;
-       u32 cmd, tmp, offs = 0, new_ht = 0;
+       u32 tmp, offs = 0, new_ht = 0;
        int err;
        enum carl9170_bw bw;
-       bool warm_reset;
        struct ieee80211_channel *old_channel = NULL;
 
        bw = nl80211_to_carl(_bw);
@@ -1592,51 +1590,27 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
        /* may be NULL at first setup */
        if (ar->channel) {
                old_channel = ar->channel;
-               warm_reset = (old_channel->band != channel->band) ||
-                            (old_channel->center_freq ==
-                             channel->center_freq) ||
-                            (ar->ht_settings != new_ht);
-
                ar->channel = NULL;
-       } else {
-               warm_reset = true;
        }
 
-       /* HW workaround */
-       if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] &&
-           channel->center_freq <= 2417)
-               warm_reset = true;
-
-       if (rfi != CARL9170_RFI_NONE || warm_reset) {
-               u32 val;
-
-               if (rfi == CARL9170_RFI_COLD)
-                       val = AR9170_PWR_RESET_BB_COLD_RESET;
-               else
-                       val = AR9170_PWR_RESET_BB_WARM_RESET;
-
-               /* warm/cold reset BB/ADDA */
-               err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, val);
-               if (err)
-                       return err;
-
-               err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, 0x0);
-               if (err)
-                       return err;
+       /* cold reset BB/ADDA */
+       err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET,
+                                AR9170_PWR_RESET_BB_COLD_RESET);
+       if (err)
+               return err;
 
-               err = carl9170_init_phy(ar, channel->band);
-               if (err)
-                       return err;
+       err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, 0x0);
+       if (err)
+               return err;
 
-               err = carl9170_init_rf_banks_0_7(ar,
-                       channel->band == IEEE80211_BAND_5GHZ);
-               if (err)
-                       return err;
+       err = carl9170_init_phy(ar, channel->band);
+       if (err)
+               return err;
 
-               cmd = CARL9170_CMD_RF_INIT;
-       } else {
-               cmd = CARL9170_CMD_FREQUENCY;
-       }
+       err = carl9170_init_rf_banks_0_7(ar,
+                                        channel->band == IEEE80211_BAND_5GHZ);
+       if (err)
+               return err;
 
        err = carl9170_exec_cmd(ar, CARL9170_CMD_FREQ_START, 0, NULL, 0, NULL);
        if (err)
@@ -1648,8 +1622,8 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
                return err;
 
        err = carl9170_init_rf_bank4_pwr(ar,
-               channel->band == IEEE80211_BAND_5GHZ,
-               channel->center_freq, bw);
+                                        channel->band == IEEE80211_BAND_5GHZ,
+                                        channel->center_freq, bw);
        if (err)
                return err;
 
@@ -1703,13 +1677,8 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
        rf.delta_slope_coeff_man = cpu_to_le32(freqpar->coeff_man);
        rf.delta_slope_coeff_exp_shgi = cpu_to_le32(freqpar->coeff_exp_shgi);
        rf.delta_slope_coeff_man_shgi = cpu_to_le32(freqpar->coeff_man_shgi);
-
-       if (rfi != CARL9170_RFI_NONE)
-               rf.finiteLoopCount = cpu_to_le32(2000);
-       else
-               rf.finiteLoopCount = cpu_to_le32(1000);
-
-       err = carl9170_exec_cmd(ar, cmd, sizeof(rf), &rf,
+       rf.finiteLoopCount = cpu_to_le32(2000);
+       err = carl9170_exec_cmd(ar, CARL9170_CMD_RF_INIT, sizeof(rf), &rf,
                                sizeof(rf_res), &rf_res);
        if (err)
                return err;
@@ -1724,9 +1693,8 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
                          old_channel->center_freq : -1, channel->center_freq,
                          err);
 
-               if ((rfi == CARL9170_RFI_COLD) || (ar->chan_fail > 3)) {
-                       /*
-                        * We have tried very hard to change to _another_
+               if (ar->chan_fail > 3) {
+                       /* We have tried very hard to change to _another_
                         * channel and we've failed to do so!
                         * Chances are that the PHY/RF is no longer
                         * operable (due to corruptions/fatal events/bugs?)
@@ -1736,8 +1704,7 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
                        return 0;
                }
 
-               err = carl9170_set_channel(ar, channel, _bw,
-                                          CARL9170_RFI_COLD);
+               err = carl9170_set_channel(ar, channel, _bw);
                if (err)
                        return err;
        } else {
index 9c0b150d5b8e2a20b3373d91b733362f083df63a..c61cafa2665b18c5215a953273919a024c5c4473 100644 (file)
@@ -387,8 +387,7 @@ static void carl9170_tx_status_process_ampdu(struct ar9170 *ar,
        u8 tid;
 
        if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) ||
-           txinfo->flags & IEEE80211_TX_CTL_INJECTED ||
-          (!(super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_AGGR))))
+           txinfo->flags & IEEE80211_TX_CTL_INJECTED)
                return;
 
        rcu_read_lock();
@@ -981,30 +980,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
 
                SET_VAL(CARL9170_TX_SUPER_AMPDU_FACTOR,
                        txc->s.ampdu_settings, factor);
-
-               for (i = 0; i < CARL9170_TX_MAX_RATES; i++) {
-                       txrate = &info->control.rates[i];
-                       if (txrate->idx >= 0) {
-                               txc->s.ri[i] =
-                                       CARL9170_TX_SUPER_RI_AMPDU;
-
-                               if (WARN_ON(!(txrate->flags &
-                                             IEEE80211_TX_RC_MCS))) {
-                                       /*
-                                        * Not sure if it's even possible
-                                        * to aggregate non-ht rates with
-                                        * this HW.
-                                        */
-                                       goto err_out;
-                               }
-                               continue;
-                       }
-
-                       txrate->idx = 0;
-                       txrate->count = ar->hw->max_rate_tries;
-               }
-
-               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR);
        }
 
        /*
@@ -1012,11 +987,31 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
         * taken from mac_control. For all fallback rate, the firmware
         * updates the mac_control flags from the rate info field.
         */
-       for (i = 1; i < CARL9170_TX_MAX_RATES; i++) {
+       for (i = 0; i < CARL9170_TX_MAX_RATES; i++) {
+               __le32 phy_set;
                txrate = &info->control.rates[i];
                if (txrate->idx < 0)
                        break;
 
+               phy_set = carl9170_tx_physet(ar, info, txrate);
+               if (i == 0) {
+                       /* first rate - part of the hw's frame header */
+                       txc->f.phy_control = phy_set;
+
+                       if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS)
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+                       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+                       else if (carl9170_tx_cts_check(ar, txrate))
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+
+               } else {
+                       /* fallback rates are stored in the firmware's
+                        * retry rate set array.
+                        */
+                       txc->s.rr[i - 1] = phy_set;
+               }
+
                SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
                        txrate->count);
 
@@ -1027,21 +1022,13 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
                        txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
                                CARL9170_TX_SUPER_RI_ERP_PROT_S);
 
-               txc->s.rr[i - 1] = carl9170_tx_physet(ar, info, txrate);
+               if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS))
+                       txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
        }
 
-       txrate = &info->control.rates[0];
-       SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[0], txrate->count);
-
-       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
-               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
-       else if (carl9170_tx_cts_check(ar, txrate))
-               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
-
        txc->s.len = cpu_to_le16(skb->len);
        txc->f.length = cpu_to_le16(len + FCS_LEN);
        txc->f.mac_control = mac_tmp;
-       txc->f.phy_control = carl9170_tx_physet(ar, info, txrate);
 
        arinfo = (void *)info->rate_driver_data;
        arinfo->timeout = jiffies;
@@ -1381,9 +1368,9 @@ static void carl9170_tx(struct ar9170 *ar)
 }
 
 static bool carl9170_tx_ampdu_queue(struct ar9170 *ar,
-       struct ieee80211_sta *sta, struct sk_buff *skb)
+       struct ieee80211_sta *sta, struct sk_buff *skb,
+       struct ieee80211_tx_info *txinfo)
 {
-       struct _carl9170_tx_superframe *super = (void *) skb->data;
        struct carl9170_sta_info *sta_info;
        struct carl9170_sta_tid *agg;
        struct sk_buff *iter;
@@ -1450,7 +1437,7 @@ err_unlock:
 
 err_unlock_rcu:
        rcu_read_unlock();
-       super->f.mac_control &= ~cpu_to_le16(AR9170_TX_MAC_AGGR);
+       txinfo->flags &= ~IEEE80211_TX_CTL_AMPDU;
        carl9170_tx_status(ar, skb, false);
        ar->tx_dropped++;
        return false;
@@ -1492,7 +1479,7 @@ void carl9170_op_tx(struct ieee80211_hw *hw,
                 * sta == NULL checks are redundant in this
                 * special case.
                 */
-               run = carl9170_tx_ampdu_queue(ar, sta, skb);
+               run = carl9170_tx_ampdu_queue(ar, sta, skb, info);
                if (run)
                        carl9170_tx_ampdu(ar);
 
index 5c54aa43ca2d81b3936d917aeb68f7331aabb34b..1816b4e7dc26409352da12b166aac3108c4acd76 100644 (file)
@@ -45,7 +45,8 @@ bool ath_hw_keyreset(struct ath_common *common, u16 entry)
        void *ah = common->ah;
 
        if (entry >= common->keymax) {
-               ath_err(common, "keycache entry %u out of range\n", entry);
+               ath_err(common, "keyreset: keycache entry %u out of range\n",
+                       entry);
                return false;
        }
 
@@ -91,7 +92,8 @@ static bool ath_hw_keysetmac(struct ath_common *common,
        void *ah = common->ah;
 
        if (entry >= common->keymax) {
-               ath_err(common, "keycache entry %u out of range\n", entry);
+               ath_err(common, "keysetmac: keycache entry %u out of range\n",
+                       entry);
                return false;
        }
 
@@ -133,7 +135,8 @@ static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
        u32 keyType;
 
        if (entry >= common->keymax) {
-               ath_err(common, "keycache entry %u out of range\n", entry);
+               ath_err(common, "set-entry: keycache entry %u out of range\n",
+                       entry);
                return false;
        }
 
index 9396dc9fe3c5c475588de3e2adce034eb66b6a42..d288eea0a26a0136bd66b8c8bed4e8a1dd4dd2ee 100644 (file)
@@ -9,5 +9,7 @@ wil6210-objs += wmi.o
 wil6210-objs += interrupt.o
 wil6210-objs += txrx.o
 
-subdir-ccflags-y += -Werror
+ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
+       subdir-ccflags-y += -Werror
+endif
 subdir-ccflags-y += -D__CHECK_ENDIAN__
index 9ecc1968262cd4d98f336c75445aee4e08cb3c21..c5d4a87abaaf76eb3e91c167a0905d69cd96edef 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/sched.h>
-#include <linux/etherdevice.h>
-#include <linux/wireless.h>
-#include <linux/ieee80211.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-#include <net/cfg80211.h>
-
 #include "wil6210.h"
 #include "wmi.h"
 
@@ -292,7 +282,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
 
        /* WMI_CONNECT_CMD */
        memset(&conn, 0, sizeof(conn));
-       switch (bss->capability & 0x03) {
+       switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
        case WLAN_CAPABILITY_DMG_TYPE_AP:
                conn.network_type = WMI_NETTYPE_INFRA;
                break;
@@ -437,17 +427,18 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        if (rc)
                return rc;
 
-       rc = wmi_set_channel(wil, channel->hw_value);
-       if (rc)
-               return rc;
-
        /* MAC address - pre-requisite for other commands */
        wmi_set_mac_address(wil, ndev->dev_addr);
 
        /* IE's */
        /* bcon 'head IE's are not relevant for 60g band */
-       wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
-                  bcon->beacon_ies);
+       /*
+        * FW do not form regular beacon, so bcon IE's are not set
+        * For the DMG bcon, when it will be supported, bcon IE's will
+        * be reused; add something like:
+        * wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
+        * bcon->beacon_ies);
+        */
        wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len,
                   bcon->proberesp_ies);
        wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len,
@@ -455,7 +446,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
 
        wil->secure_pcp = info->privacy;
 
-       rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype);
+       rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
+                          channel->hw_value);
        if (rc)
                return rc;
 
@@ -472,11 +464,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
 {
        int rc = 0;
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
-       struct wireless_dev *wdev = ndev->ieee80211_ptr;
-       u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
 
-       /* To stop beaconing, set BI to 0 */
-       rc = wmi_set_bcon(wil, 0, wmi_nettype);
+       rc = wmi_pcp_stop(wil);
 
        return rc;
 }
diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h
deleted file mode 100644 (file)
index e5712f0..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef WIL_DBG_HEXDUMP_H_
-#define WIL_DBG_HEXDUMP_H_
-
-#include <linux/printk.h>
-#include <linux/dynamic_debug.h>
-
-#if defined(CONFIG_DYNAMIC_DEBUG)
-#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize,     \
-                                groupsize, buf, len, ascii)            \
-       dynamic_hex_dump(prefix_str, prefix_type, rowsize,              \
-                            groupsize, buf, len, ascii)
-
-#else /* defined(CONFIG_DYNAMIC_DEBUG) */
-#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize,     \
-                                groupsize, buf, len, ascii)            \
-       print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize,    \
-                      groupsize, buf, len, ascii)
-#endif /* defined(CONFIG_DYNAMIC_DEBUG) */
-
-#endif /* WIL_DBG_HEXDUMP_H_ */
index 65fc9683bfd8e2751f3a80ca16605ae6d89e86e1..4be07f5e22b991b6223caadf99ff2c6faf3db664 100644 (file)
@@ -312,14 +312,6 @@ static const struct file_operations fops_memread = {
        .llseek         = seq_lseek,
 };
 
-static int wil_default_open(struct inode *inode, struct file *file)
-{
-       if (inode->i_private)
-               file->private_data = inode->i_private;
-
-       return 0;
-}
-
 static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
                                size_t count, loff_t *ppos)
 {
@@ -361,7 +353,7 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
 
 static const struct file_operations fops_ioblob = {
        .read =         wil_read_file_ioblob,
-       .open =         wil_default_open,
+       .open =         simple_open,
        .llseek =       default_llseek,
 };
 
@@ -396,7 +388,7 @@ static ssize_t wil_write_file_reset(struct file *file, const char __user *buf,
 
 static const struct file_operations fops_reset = {
        .write = wil_write_file_reset,
-       .open  = wil_default_open,
+       .open  = simple_open,
 };
 /*---------Tx descriptor------------*/
 
@@ -526,7 +518,50 @@ static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf,
 static const struct file_operations fops_ssid = {
        .read = wil_read_file_ssid,
        .write = wil_write_file_ssid,
-       .open  = wil_default_open,
+       .open  = simple_open,
+};
+
+/*---------temp------------*/
+static void print_temp(struct seq_file *s, const char *prefix, u32 t)
+{
+       switch (t) {
+       case 0:
+       case ~(u32)0:
+               seq_printf(s, "%s N/A\n", prefix);
+       break;
+       default:
+               seq_printf(s, "%s %d.%03d\n", prefix, t / 1000, t % 1000);
+               break;
+       }
+}
+
+static int wil_temp_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       u32 t_m, t_r;
+
+       int rc = wmi_get_temperature(wil, &t_m, &t_r);
+       if (rc) {
+               seq_printf(s, "Failed\n");
+               return 0;
+       }
+
+       print_temp(s, "MAC temperature   :", t_m);
+       print_temp(s, "Radio temperature :", t_r);
+
+       return 0;
+}
+
+static int wil_temp_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_temp_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_temp = {
+       .open           = wil_temp_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
 };
 
 /*----------------*/
@@ -563,6 +598,7 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
        debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
 
        debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
+       debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp);
 
        wil->rgf_blob.data = (void * __force)wil->csr + 0;
        wil->rgf_blob.size = 0xa000;
index dc97e7b2609cb84c00d8954ba067aab60b2b5a0a..e3c1e7684f9c6874772484f69e8687d27b8722b7 100644 (file)
@@ -240,6 +240,15 @@ static void wil_notify_fw_error(struct wil6210_priv *wil)
        kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 }
 
+static void wil_cache_mbox_regs(struct wil6210_priv *wil)
+{
+       /* make shadow copy of registers that should not change on run time */
+       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
+                            sizeof(struct wil6210_mbox_ctl));
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+}
+
 static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
 {
        struct wil6210_priv *wil = cookie;
@@ -257,14 +266,19 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
        wil6210_mask_irq_misc(wil);
 
        if (isr & ISR_MISC_FW_ERROR) {
-               wil_dbg_irq(wil, "IRQ: Firmware error\n");
+               wil_err(wil, "Firmware error detected\n");
                clear_bit(wil_status_fwready, &wil->status);
-               wil_notify_fw_error(wil);
-               isr &= ~ISR_MISC_FW_ERROR;
+               /*
+                * do not clear @isr here - we do 2-nd part in thread
+                * there, user space get notified, and it should be done
+                * in non-atomic context
+                */
        }
 
        if (isr & ISR_MISC_FW_READY) {
                wil_dbg_irq(wil, "IRQ: FW ready\n");
+               wil_cache_mbox_regs(wil);
+               set_bit(wil_status_reset_done, &wil->status);
                /**
                 * Actual FW ready indicated by the
                 * WMI_FW_READY_EVENTID
@@ -289,6 +303,11 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
 
        wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
 
+       if (isr & ISR_MISC_FW_ERROR) {
+               wil_notify_fw_error(wil);
+               isr &= ~ISR_MISC_FW_ERROR;
+       }
+
        if (isr & ISR_MISC_MBOX_EVT) {
                wil_dbg_irq(wil, "MBOX event\n");
                wmi_recv_cmd(wil);
index 761c389586d4cef581b1239d58f3e313e9f9e49a..a0478e2f68688ebdcc71f2351e7593efa4f3c12a 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/sched.h>
-#include <linux/ieee80211.h>
-#include <linux/wireless.h>
-#include <linux/slab.h>
 #include <linux/moduleparam.h>
 #include <linux/if_arp.h>
 
@@ -109,13 +103,24 @@ static void wil_connect_timer_fn(ulong x)
        schedule_work(&wil->disconnect_worker);
 }
 
-static void wil_cache_mbox_regs(struct wil6210_priv *wil)
+static void wil_connect_worker(struct work_struct *work)
 {
-       /* make shadow copy of registers that should not change on run time */
-       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
-                            sizeof(struct wil6210_mbox_ctl));
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+       int rc;
+       struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+                                               connect_worker);
+       int cid = wil->pending_connect_cid;
+
+       if (cid < 0) {
+               wil_err(wil, "No connection pending\n");
+               return;
+       }
+
+       wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
+
+       rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, cid, 0);
+       wil->pending_connect_cid = -1;
+       if (rc == 0)
+               wil_link_on(wil);
 }
 
 int wil_priv_init(struct wil6210_priv *wil)
@@ -130,7 +135,7 @@ int wil_priv_init(struct wil6210_priv *wil)
        wil->pending_connect_cid = -1;
        setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
 
-       INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker);
+       INIT_WORK(&wil->connect_worker, wil_connect_worker);
        INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
        INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
 
@@ -147,8 +152,6 @@ int wil_priv_init(struct wil6210_priv *wil)
                return -EAGAIN;
        }
 
-       wil_cache_mbox_regs(wil);
-
        return 0;
 }
 
@@ -185,15 +188,11 @@ static void wil_target_reset(struct wil6210_priv *wil)
        W(RGF_USER_MAC_CPU_0,  BIT(1)); /* mac_cpu_man_rst */
        W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
 
-       msleep(100);
-
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
 
-       msleep(100);
-
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
@@ -203,12 +202,6 @@ static void wil_target_reset(struct wil6210_priv *wil)
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
 
-       msleep(2000);
-
-       W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */
-
-       msleep(2000);
-
        wil_dbg_misc(wil, "Reset completed\n");
 
 #undef W
@@ -265,8 +258,6 @@ int wil_reset(struct wil6210_priv *wil)
        wil->pending_connect_cid = -1;
        INIT_COMPLETION(wil->wmi_ready);
 
-       wil_cache_mbox_regs(wil);
-
        /* TODO: release MAC reset */
        wil6210_enable_irq(wil);
 
@@ -352,9 +343,9 @@ static int __wil_up(struct wil6210_priv *wil)
                        wil_err(wil, "SSID not set\n");
                        return -EINVAL;
                }
-               wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid);
-               if (channel)
-                       wmi_set_channel(wil, channel->hw_value);
+               rc = wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid);
+               if (rc)
+                       return rc;
                break;
        default:
                break;
@@ -364,9 +355,12 @@ static int __wil_up(struct wil6210_priv *wil)
        wmi_set_mac_address(wil, ndev->dev_addr);
 
        /* Set up beaconing if required. */
-       rc = wmi_set_bcon(wil, bi, wmi_nettype);
-       if (rc)
-               return rc;
+       if (bi > 0) {
+               rc = wmi_pcp_start(wil, bi, wmi_nettype,
+                                  (channel ? channel->hw_value : 0));
+               if (rc)
+                       return rc;
+       }
 
        /* Rx VRING. After MAC and beacon */
        wil_rx_init(wil);
index 8ce2e33dce206065aaa5e5cee9ce3e4c3db3163a..098a8ec6b841ac9433ed3d66f99cbeaaa74ba635 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/module.h>
-#include <linux/netdevice.h>
 #include <linux/etherdevice.h>
-#include <linux/slab.h>
 
 #include "wil6210.h"
 
index 81c35c6e3832460c694c15b687ceb2a137987a6b..eb1dc7ad80fb367bbb6c56959a8758aa85225ac4 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/netdevice.h>
 #include <linux/debugfs.h>
 #include <linux/pci.h>
 #include <linux/moduleparam.h>
index d1315b442375124d369223bbce0dda98fca8b691..79d4e3271b00834d75966f6b959201edd46ba98c 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
 #include <linux/etherdevice.h>
-#include <linux/hardirq.h>
 #include <net/ieee80211_radiotap.h>
 #include <linux/if_arp.h>
 #include <linux/moduleparam.h>
@@ -83,8 +80,6 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
         */
        vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL);
        if (!vring->va) {
-               wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n",
-                       vring->size);
                kfree(vring->ctx);
                vring->ctx = NULL;
                return -ENOMEM;
@@ -560,7 +555,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
        if (rc)
                goto out_free;
 
-       if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) {
+       if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) {
                wil_err(wil, "Tx config failed, status 0x%02x\n",
                        reply.cmd.status);
                rc = -EINVAL;
index aea961ff8f08c20cdf0c0b0d82047c28d69d10ea..8f76ecd8a7e5e5ec0862b45053c254af66df9a62 100644 (file)
@@ -21,8 +21,6 @@
 #include <linux/wireless.h>
 #include <net/cfg80211.h>
 
-#include "dbg_hexdump.h"
-
 #define WIL_NAME "wil6210"
 
 /**
@@ -188,6 +186,7 @@ enum { /* for wil6210_priv.status */
        wil_status_fwready = 0,
        wil_status_fwconnected,
        wil_status_dontscan,
+       wil_status_reset_done,
        wil_status_irqen, /* FIXME: interrupts enabled - for debug */
 };
 
@@ -210,6 +209,8 @@ struct wil6210_priv {
        struct wireless_dev *wdev;
        void __iomem *csr;
        ulong status;
+       u32 fw_version;
+       u8 n_mids; /* number of additional MIDs as reported by FW */
        /* profile */
        u32 monitor_flags;
        u32 secure_pcp; /* create secure PCP? */
@@ -227,7 +228,7 @@ struct wil6210_priv {
        struct workqueue_struct *wmi_wq; /* for deferred calls */
        struct work_struct wmi_event_worker;
        struct workqueue_struct *wmi_wq_conn; /* for connect worker */
-       struct work_struct wmi_connect_worker;
+       struct work_struct connect_worker;
        struct work_struct disconnect_worker;
        struct timer_list connect_timer;
        int pending_connect_cid;
@@ -277,13 +278,13 @@ struct wil6210_priv {
 
 #define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize,    \
                          groupsize, buf, len, ascii)           \
-                         wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\
+                         print_hex_dump_debug("DBG[TXRX]" prefix_str,\
                                         prefix_type, rowsize,  \
                                         groupsize, buf, len, ascii)
 
 #define wil_hex_dump_wmi(prefix_str, prefix_type, rowsize,     \
                         groupsize, buf, len, ascii)            \
-                        wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\
+                        print_hex_dump_debug("DBG[ WMI]" prefix_str,\
                                        prefix_type, rowsize,   \
                                        groupsize, buf, len, ascii)
 
@@ -313,7 +314,6 @@ int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len);
 void wmi_recv_cmd(struct wil6210_priv *wil);
 int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
             u16 reply_id, void *reply, u8 reply_size, int to_msec);
-void wmi_connect_worker(struct work_struct *work);
 void wmi_event_worker(struct work_struct *work);
 void wmi_event_flush(struct wil6210_priv *wil);
 int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid);
@@ -328,6 +328,8 @@ int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
 int wmi_echo(struct wil6210_priv *wil);
 int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
 int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);
+int wmi_p2p_cfg(struct wil6210_priv *wil, int channel);
+int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
 
 int wil6210_init_irq(struct wil6210_priv *wil, int irq);
 void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
@@ -341,7 +343,8 @@ struct wireless_dev *wil_cfg80211_init(struct device *dev);
 void wil_wdev_free(struct wil6210_priv *wil);
 
 int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
-int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype);
+int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
+int wmi_pcp_stop(struct wil6210_priv *wil);
 void wil6210_disconnect(struct wil6210_priv *wil, void *bssid);
 
 int wil_rx_init(struct wil6210_priv *wil);
index 0bb3b76b4b58c0c846a2fde77af14fcdffadf339..45b04e383f9a1745e272ccfc8a8dd0abf79cfef9 100644 (file)
@@ -14,9 +14,6 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/pci.h>
-#include <linux/io.h>
-#include <linux/list.h>
 #include <linux/etherdevice.h>
 #include <linux/if_arp.h>
 
@@ -272,16 +269,18 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
        struct net_device *ndev = wil_to_ndev(wil);
        struct wireless_dev *wdev = wil->wdev;
        struct wmi_ready_event *evt = d;
-       u32 ver = le32_to_cpu(evt->sw_version);
+       wil->fw_version = le32_to_cpu(evt->sw_version);
+       wil->n_mids = evt->numof_additional_mids;
 
-       wil_dbg_wmi(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac);
+       wil_dbg_wmi(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version,
+                   evt->mac, wil->n_mids);
 
        if (!is_valid_ether_addr(ndev->dev_addr)) {
                memcpy(ndev->dev_addr, evt->mac, ETH_ALEN);
                memcpy(ndev->perm_addr, evt->mac, ETH_ALEN);
        }
        snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version),
-                "%d", ver);
+                "%d", wil->fw_version);
 }
 
 static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
@@ -324,17 +323,9 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
 
        if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
                struct cfg80211_bss *bss;
-               u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
-               u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
-               u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
-               const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
-               size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
-                                                u.beacon.variable);
-               wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
-
-               bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid,
-                                         tsf, cap, bi, ie_buf, ie_len,
-                                         signal, GFP_KERNEL);
+
+               bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
+                                               d_len, signal, GFP_KERNEL);
                if (bss) {
                        wil_dbg_wmi(wil, "Added BSS %pM\n",
                                    rx_mgmt_frame->bssid);
@@ -342,6 +333,9 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
                } else {
                        wil_err(wil, "cfg80211_inform_bss() failed\n");
                }
+       } else {
+               cfg80211_rx_mgmt(wil->wdev, freq, signal,
+                                (void *)rx_mgmt_frame, d_len, GFP_KERNEL);
        }
 }
 
@@ -443,7 +437,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
        memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN);
 
        wil->pending_connect_cid = evt->cid;
-       queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker);
+       queue_work(wil->wmi_wq_conn, &wil->connect_worker);
 }
 
 static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
@@ -528,6 +522,37 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
        }
 }
 
+static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wmi_data_port_open_event *evt = d;
+
+       wil_dbg_wmi(wil, "Link UP for CID %d\n", evt->cid);
+
+       netif_carrier_on(ndev);
+}
+
+static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wmi_wbe_link_down_event *evt = d;
+
+       wil_dbg_wmi(wil, "Link DOWN for CID %d, reason %d\n",
+                   evt->cid, le32_to_cpu(evt->reason));
+
+       netif_carrier_off(ndev);
+}
+
+static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
+                             int len)
+{
+       struct wmi_vring_ba_status_event *evt = d;
+
+       wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d\n",
+                   evt->ringid, evt->status ? "N/A" : "OK", evt->agg_wsize,
+                   __le16_to_cpu(evt->ba_timeout));
+}
+
 static const struct {
        int eventid;
        void (*handler)(struct wil6210_priv *wil, int eventid,
@@ -541,6 +566,9 @@ static const struct {
        {WMI_DISCONNECT_EVENTID,        wmi_evt_disconnect},
        {WMI_NOTIFY_REQ_DONE_EVENTID,   wmi_evt_notify},
        {WMI_EAPOL_RX_EVENTID,          wmi_evt_eapol_rx},
+       {WMI_DATA_PORT_OPEN_EVENTID,    wmi_evt_linkup},
+       {WMI_WBE_LINKDOWN_EVENTID,      wmi_evt_linkdown},
+       {WMI_BA_STATUS_EVENTID,         wmi_evt_ba_status},
 };
 
 /*
@@ -559,6 +587,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
        void __iomem *src;
        ulong flags;
 
+       if (!test_bit(wil_status_reset_done, &wil->status)) {
+               wil_err(wil, "Reset not completed\n");
+               return;
+       }
+
        for (;;) {
                u16 len;
 
@@ -683,18 +716,39 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
        return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
 }
 
-int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype)
+int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
 {
-       struct wmi_bcon_ctrl_cmd cmd = {
+       int rc;
+
+       struct wmi_pcp_start_cmd cmd = {
                .bcon_interval = cpu_to_le16(bi),
                .network_type = wmi_nettype,
                .disable_sec_offload = 1,
+               .channel = chan,
        };
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_pcp_started_event evt;
+       } __packed reply;
 
        if (!wil->secure_pcp)
                cmd.disable_sec = 1;
 
-       return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd));
+       rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd),
+                     WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 100);
+       if (rc)
+               return rc;
+
+       if (reply.evt.status != WMI_FW_STATUS_SUCCESS)
+               rc = -EINVAL;
+
+       return rc;
+}
+
+int wmi_pcp_stop(struct wil6210_priv *wil)
+{
+       return wmi_call(wil, WMI_PCP_STOP_CMDID, NULL, 0,
+                       WMI_PCP_STOPPED_EVENTID, NULL, 0, 20);
 }
 
 int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid)
@@ -765,6 +819,16 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel)
        return 0;
 }
 
+int wmi_p2p_cfg(struct wil6210_priv *wil, int channel)
+{
+       struct wmi_p2p_cfg_cmd cmd = {
+               .discovery_mode = WMI_DISCOVERY_MODE_NON_OFFLOAD,
+               .channel = channel - 1,
+       };
+
+       return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd));
+}
+
 int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb)
 {
        struct wmi_eapol_tx_cmd *cmd;
@@ -843,7 +907,7 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
        /* BUG: FW API define ieLen as u8. Will fix FW */
        cmd->ie_len = cpu_to_le16(ie_len);
        memcpy(cmd->ie_info, ie, ie_len);
-       rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len);
+       rc = wmi_send(wil, WMI_SET_APPIE_CMDID, cmd, len);
        kfree(cmd);
 
        return rc;
@@ -898,6 +962,31 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
        return rc;
 }
 
+int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r)
+{
+       int rc;
+       struct wmi_temp_sense_cmd cmd = {
+               .measure_marlon_m_en = cpu_to_le32(!!t_m),
+               .measure_marlon_r_en = cpu_to_le32(!!t_r),
+       };
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_temp_sense_done_event evt;
+       } __packed reply;
+
+       rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, &cmd, sizeof(cmd),
+                     WMI_TEMP_SENSE_DONE_EVENTID, &reply, sizeof(reply), 100);
+       if (rc)
+               return rc;
+
+       if (t_m)
+               *t_m = le32_to_cpu(reply.evt.marlon_m_t1000);
+       if (t_r)
+               *t_r = le32_to_cpu(reply.evt.marlon_r_t1000);
+
+       return 0;
+}
+
 void wmi_event_flush(struct wil6210_priv *wil)
 {
        struct pending_wmi_event *evt, *t;
@@ -997,24 +1086,3 @@ void wmi_event_worker(struct work_struct *work)
                kfree(evt);
        }
 }
-
-void wmi_connect_worker(struct work_struct *work)
-{
-       int rc;
-       struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
-                                               wmi_connect_worker);
-
-       if (wil->pending_connect_cid < 0) {
-               wil_err(wil, "No connection pending\n");
-               return;
-       }
-
-       wil_dbg_wmi(wil, "Configure for connection CID %d\n",
-                   wil->pending_connect_cid);
-
-       rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE,
-                              wil->pending_connect_cid, 0);
-       wil->pending_connect_cid = -1;
-       if (rc == 0)
-               wil_link_on(wil);
-}
index 3bbf87572b07970e8c3c3ac5dee55657abf0400f..50b8528394f4050b8098098e9bbdd0520ae1bf2a 100644 (file)
@@ -36,6 +36,7 @@
 enum wmi_command_id {
        WMI_CONNECT_CMDID               = 0x0001,
        WMI_DISCONNECT_CMDID            = 0x0003,
+       WMI_DISCONNECT_STA_CMDID        = 0x0004,
        WMI_START_SCAN_CMDID            = 0x0007,
        WMI_SET_BSS_FILTER_CMDID        = 0x0009,
        WMI_SET_PROBED_SSID_CMDID       = 0x000a,
@@ -44,7 +45,6 @@ enum wmi_command_id {
        WMI_ADD_CIPHER_KEY_CMDID        = 0x0016,
        WMI_DELETE_CIPHER_KEY_CMDID     = 0x0017,
        WMI_SET_APPIE_CMDID             = 0x003f,
-       WMI_GET_APPIE_CMDID             = 0x0040,
        WMI_SET_WSC_STATUS_CMDID        = 0x0041,
        WMI_PXMT_RANGE_CFG_CMDID        = 0x0042,
        WMI_PXMT_SNR2_RANGE_CFG_CMDID   = 0x0043,
@@ -55,11 +55,11 @@ enum wmi_command_id {
        WMI_DEEP_ECHO_CMDID             = 0x0804,
        WMI_CONFIG_MAC_CMDID            = 0x0805,
        WMI_CONFIG_PHY_DEBUG_CMDID      = 0x0806,
-       WMI_ADD_STATION_CMDID           = 0x0807,
        WMI_ADD_DEBUG_TX_PCKT_CMDID     = 0x0808,
        WMI_PHY_GET_STATISTICS_CMDID    = 0x0809,
        WMI_FS_TUNE_CMDID               = 0x080a,
        WMI_CORR_MEASURE_CMDID          = 0x080b,
+       WMI_READ_RSSI_CMDID             = 0x080c,
        WMI_TEMP_SENSE_CMDID            = 0x080e,
        WMI_DC_CALIB_CMDID              = 0x080f,
        WMI_SEND_TONE_CMDID             = 0x0810,
@@ -75,9 +75,9 @@ enum wmi_command_id {
        MAC_IO_STATIC_PARAMS_CMDID      = 0x081b,
        MAC_IO_DYNAMIC_PARAMS_CMDID     = 0x081c,
        WMI_SILENT_RSSI_CALIB_CMDID     = 0x081d,
+       WMI_RF_RX_TEST_CMDID            = 0x081e,
        WMI_CFG_RX_CHAIN_CMDID          = 0x0820,
        WMI_VRING_CFG_CMDID             = 0x0821,
-       WMI_RX_ON_CMDID                 = 0x0822,
        WMI_VRING_BA_EN_CMDID           = 0x0823,
        WMI_VRING_BA_DIS_CMDID          = 0x0824,
        WMI_RCP_ADDBA_RESP_CMDID        = 0x0825,
@@ -87,7 +87,6 @@ enum wmi_command_id {
        WMI_SET_PCP_CHANNEL_CMDID       = 0x0829,
        WMI_GET_PCP_CHANNEL_CMDID       = 0x082a,
        WMI_SW_TX_REQ_CMDID             = 0x082b,
-       WMI_RX_OFF_CMDID                = 0x082c,
        WMI_READ_MAC_RXQ_CMDID          = 0x0830,
        WMI_READ_MAC_TXQ_CMDID          = 0x0831,
        WMI_WRITE_MAC_RXQ_CMDID         = 0x0832,
@@ -112,6 +111,18 @@ enum wmi_command_id {
        WMI_FLASH_READ_CMDID            = 0x0902,
        WMI_FLASH_WRITE_CMDID           = 0x0903,
        WMI_SECURITY_UNIT_TEST_CMDID    = 0x0904,
+       /*P2P*/
+       WMI_P2P_CFG_CMDID               = 0x0910,
+       WMI_PORT_ALLOCATE_CMDID         = 0x0911,
+       WMI_PORT_DELETE_CMDID           = 0x0912,
+       WMI_POWER_MGMT_CFG_CMDID        = 0x0913,
+       WMI_START_LISTEN_CMDID          = 0x0914,
+       WMI_START_SEARCH_CMDID          = 0x0915,
+       WMI_DISCOVERY_START_CMDID       = 0x0916,
+       WMI_DISCOVERY_STOP_CMDID        = 0x0917,
+       WMI_PCP_START_CMDID             = 0x0918,
+       WMI_PCP_STOP_CMDID              = 0x0919,
+       WMI_GET_PCP_FACTOR_CMDID        = 0x091b,
 
        WMI_SET_MAC_ADDRESS_CMDID       = 0xf003,
        WMI_ABORT_SCAN_CMDID            = 0xf007,
@@ -131,18 +142,6 @@ enum wmi_command_id {
  * Commands data structures
  */
 
-/*
- * Frame Types
- */
-enum wmi_mgmt_frame_type {
-       WMI_FRAME_BEACON        = 0,
-       WMI_FRAME_PROBE_REQ     = 1,
-       WMI_FRAME_PROBE_RESP    = 2,
-       WMI_FRAME_ASSOC_REQ     = 3,
-       WMI_FRAME_ASSOC_RESP    = 4,
-       WMI_NUM_MGMT_FRAME,
-};
-
 /*
  * WMI_CONNECT_CMDID
  */
@@ -184,7 +183,7 @@ enum wmi_crypto_type {
 enum wmi_connect_ctrl_flag_bits {
        WMI_CONNECT_ASSOC_POLICY_USER           = 0x0001,
        WMI_CONNECT_SEND_REASSOC                = 0x0002,
-       WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER    = 0x0004,
+       WMI_CONNECT_IGNORE_WPA_GROUP_CIPHER     = 0x0004,
        WMI_CONNECT_PROFILE_MATCH_DONE          = 0x0008,
        WMI_CONNECT_IGNORE_AAC_BEACON           = 0x0010,
        WMI_CONNECT_CSA_FOLLOW_BSS              = 0x0020,
@@ -212,6 +211,13 @@ struct wmi_connect_cmd {
        u8 reserved1[2];
 } __packed;
 
+/*
+ * WMI_DISCONNECT_STA_CMDID
+ */
+struct wmi_disconnect_sta_cmd {
+       u8 dst_mac[WMI_MAC_LEN];
+       __le16 disconnect_reason;
+} __packed;
 
 /*
  * WMI_RECONNECT_CMDID
@@ -289,10 +295,12 @@ struct wmi_delete_cipher_key_cmd {
 enum wmi_scan_type {
        WMI_LONG_SCAN           = 0,
        WMI_SHORT_SCAN          = 1,
+       WMI_PBC_SCAN            = 2,
 };
 
 struct wmi_start_scan_cmd {
        u8 reserved[8];
+
        __le32 home_dwell_time; /* Max duration in the home channel(ms) */
        __le32 force_scan_interval;     /* Time interval between scans (ms)*/
        u8 scan_type;           /* wmi_scan_type */
@@ -309,7 +317,7 @@ struct wmi_start_scan_cmd {
 /*
  * WMI_SET_PROBED_SSID_CMDID
  */
-#define MAX_PROBED_SSID_INDEX   (15)
+#define MAX_PROBED_SSID_INDEX  (3)
 
 enum wmi_ssid_flag {
        WMI_SSID_FLAG_DISABLE   = 0,    /* disables entry */
@@ -328,6 +336,20 @@ struct wmi_probed_ssid_cmd {
  * WMI_SET_APPIE_CMDID
  * Add Application specified IE to a management frame
  */
+#define WMI_MAX_IE_LEN         (1024)
+
+/*
+ * Frame Types
+ */
+enum wmi_mgmt_frame_type {
+       WMI_FRAME_BEACON        = 0,
+       WMI_FRAME_PROBE_REQ     = 1,
+       WMI_FRAME_PROBE_RESP    = 2,
+       WMI_FRAME_ASSOC_REQ     = 3,
+       WMI_FRAME_ASSOC_RESP    = 4,
+       WMI_NUM_MGMT_FRAME,
+};
+
 struct wmi_set_appie_cmd {
        u8 mgmt_frm_type;       /* enum wmi_mgmt_frame_type */
        u8 reserved;
@@ -335,13 +357,18 @@ struct wmi_set_appie_cmd {
        u8 ie_info[0];
 } __packed;
 
-#define WMI_MAX_IE_LEN (1024)
 
+/*
+ * WMI_PXMT_RANGE_CFG_CMDID
+ */
 struct wmi_pxmt_range_cfg_cmd {
        u8 dst_mac[WMI_MAC_LEN];
        __le16 range;
 } __packed;
 
+/*
+ * WMI_PXMT_SNR2_RANGE_CFG_CMDID
+ */
 struct wmi_pxmt_snr2_range_cfg_cmd {
        s8 snr2range_arr[WMI_PROX_RANGE_NUM-1];
 } __packed;
@@ -359,6 +386,23 @@ struct wmi_rf_mgmt_cmd {
        __le32 rf_mgmt_type;
 } __packed;
 
+
+/*
+ * WMI_RF_RX_TEST_CMDID
+ */
+struct wmi_rf_rx_test_cmd {
+       __le32 sector;
+} __packed;
+
+/*
+ * WMI_CORR_MEASURE_CMDID
+ */
+struct wmi_corr_measure_cmd {
+       s32 freq_mhz;
+       __le32 length_samples;
+       __le32 iterations;
+} __packed;
+
 /*
  * WMI_SET_SSID_CMDID
  */
@@ -388,6 +432,74 @@ struct wmi_bcon_ctrl_cmd {
        u8 disable_sec;
 } __packed;
 
+
+/******* P2P ***********/
+
+/*
+ * WMI_PORT_ALLOCATE_CMDID
+ */
+enum wmi_port_role {
+       WMI_PORT_STA            = 0,
+       WMI_PORT_PCP            = 1,
+       WMI_PORT_AP             = 2,
+       WMI_PORT_P2P_DEV        = 3,
+       WMI_PORT_P2P_CLIENT     = 4,
+       WMI_PORT_P2P_GO         = 5,
+};
+
+struct wmi_port_allocate_cmd {
+       u8 mac[WMI_MAC_LEN];
+       u8 port_role;
+       u8 midid;
+} __packed;
+
+/*
+ * WMI_PORT_DELETE_CMDID
+ */
+struct wmi_delete_port_cmd {
+       u8 mid;
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_P2P_CFG_CMDID
+ */
+enum wmi_discovery_mode {
+       WMI_DISCOVERY_MODE_NON_OFFLOAD  = 0,
+       WMI_DISCOVERY_MODE_OFFLOAD      = 1,
+};
+
+struct wmi_p2p_cfg_cmd {
+       u8 discovery_mode;      /* wmi_discovery_mode */
+       u8 channel;
+       __le16 bcon_interval; /* base to listen/search duration calculation */
+} __packed;
+
+/*
+ * WMI_POWER_MGMT_CFG_CMDID
+ */
+enum wmi_power_source_type {
+       WMI_POWER_SOURCE_BATTERY        = 0,
+       WMI_POWER_SOURCE_OTHER          = 1,
+};
+
+struct wmi_power_mgmt_cfg_cmd {
+       u8 power_source;        /* wmi_power_source_type */
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_PCP_START_CMDID
+ */
+struct wmi_pcp_start_cmd {
+       __le16 bcon_interval;
+       u8 reserved0[10];
+       u8 network_type;
+       u8 channel;
+       u8 disable_sec_offload;
+       u8 disable_sec;
+} __packed;
+
 /*
  * WMI_SW_TX_REQ_CMDID
  */
@@ -435,16 +547,17 @@ enum wmi_vring_cfg_schd_params_priority {
        WMI_SCH_PRIO_HIGH                       = 1,
 };
 
+#define CIDXTID_CID_POS (0)
+#define CIDXTID_CID_LEN (4)
+#define CIDXTID_CID_MSK (0xF)
+#define CIDXTID_TID_POS (4)
+#define CIDXTID_TID_LEN (4)
+#define CIDXTID_TID_MSK (0xF0)
+
 struct wmi_vring_cfg {
        struct wmi_sw_ring_cfg tx_sw_ring;
        u8 ringid;                              /* 0-23 vrings */
 
-       #define CIDXTID_CID_POS (0)
-       #define CIDXTID_CID_LEN (4)
-       #define CIDXTID_CID_MSK (0xF)
-       #define CIDXTID_TID_POS (4)
-       #define CIDXTID_TID_LEN (4)
-       #define CIDXTID_TID_MSK (0xF0)
        u8 cidxtid;
 
        u8 encap_trans_type;
@@ -501,8 +614,14 @@ struct wmi_vring_ba_dis_cmd {
  */
 struct wmi_notify_req_cmd {
        u8 cid;
-       u8 reserved[3];
+       u8 year;
+       u8 month;
+       u8 day;
        __le32 interval_usec;
+       u8 hour;
+       u8 minute;
+       u8 second;
+       u8 miliseconds;
 } __packed;
 
 /*
@@ -548,6 +667,11 @@ enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type {
        WMI_NWIFI_RX_TRANS_MODE_PBSS2STA        = 2,
 };
 
+enum wmi_cfg_rx_chain_cmd_reorder_type {
+       WMI_RX_HW_REORDER = 0,
+       WMI_RX_SW_REORDER = 1,
+};
+
 struct wmi_cfg_rx_chain_cmd {
        __le32 action;
        struct wmi_sw_ring_cfg rx_sw_ring;
@@ -596,7 +720,8 @@ struct wmi_cfg_rx_chain_cmd {
        __le16 wb_thrsh;
        __le32 itr_value;
        __le16 host_thrsh;
-       u8 reserved[2];
+       u8 reorder_type;
+       u8 reserved;
        struct wmi_sniffer_cfg sniffer_cfg;
 } __packed;
 
@@ -604,15 +729,7 @@ struct wmi_cfg_rx_chain_cmd {
  * WMI_RCP_ADDBA_RESP_CMDID
  */
 struct wmi_rcp_addba_resp_cmd {
-
-       #define CIDXTID_CID_POS (0)
-       #define CIDXTID_CID_LEN (4)
-       #define CIDXTID_CID_MSK (0xF)
-       #define CIDXTID_TID_POS (4)
-       #define CIDXTID_TID_LEN (4)
-       #define CIDXTID_TID_MSK (0xF0)
        u8 cidxtid;
-
        u8 dialog_token;
        __le16 status_code;
        __le16 ba_param_set;    /* ieee80211_ba_parameterset field to send */
@@ -623,15 +740,7 @@ struct wmi_rcp_addba_resp_cmd {
  * WMI_RCP_DELBA_CMDID
  */
 struct wmi_rcp_delba_cmd {
-
-       #define CIDXTID_CID_POS (0)
-       #define CIDXTID_CID_LEN (4)
-       #define CIDXTID_CID_MSK (0xF)
-       #define CIDXTID_TID_POS (4)
-       #define CIDXTID_TID_LEN (4)
-       #define CIDXTID_TID_MSK (0xF0)
        u8 cidxtid;
-
        u8 reserved;
        __le16 reason;
 } __packed;
@@ -640,15 +749,7 @@ struct wmi_rcp_delba_cmd {
  * WMI_RCP_ADDBA_REQ_CMDID
  */
 struct wmi_rcp_addba_req_cmd {
-
-       #define CIDXTID_CID_POS (0)
-       #define CIDXTID_CID_LEN (4)
-       #define CIDXTID_CID_MSK (0xF)
-       #define CIDXTID_TID_POS (4)
-       #define CIDXTID_TID_LEN (4)
-       #define CIDXTID_TID_MSK (0xF0)
        u8 cidxtid;
-
        u8 dialog_token;
        /* ieee80211_ba_parameterset field as it received */
        __le16 ba_param_set;
@@ -665,7 +766,6 @@ struct wmi_set_mac_address_cmd {
        u8 reserved[2];
 } __packed;
 
-
 /*
 * WMI_EAPOL_TX_CMDID
 */
@@ -691,6 +791,17 @@ struct wmi_echo_cmd {
        __le32 value;
 } __packed;
 
+/*
+ * WMI_TEMP_SENSE_CMDID
+ *
+ * Measure MAC and radio temperatures
+ */
+struct wmi_temp_sense_cmd {
+       __le32 measure_marlon_m_en;
+       __le32 measure_marlon_r_en;
+} __packed;
+
+
 /*
  * WMI Events
  */
@@ -699,7 +810,6 @@ struct wmi_echo_cmd {
  * List of Events (target to host)
  */
 enum wmi_event_id {
-       WMI_IMM_RSP_EVENTID                     = 0x0000,
        WMI_READY_EVENTID                       = 0x1001,
        WMI_CONNECT_EVENTID                     = 0x1002,
        WMI_DISCONNECT_EVENTID                  = 0x1003,
@@ -709,13 +819,9 @@ enum wmi_event_id {
        WMI_FW_READY_EVENTID                    = 0x1801,
        WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID      = 0x0200,
        WMI_ECHO_RSP_EVENTID                    = 0x1803,
-       WMI_CONFIG_MAC_DONE_EVENTID             = 0x1805,
-       WMI_CONFIG_PHY_DEBUG_DONE_EVENTID       = 0x1806,
-       WMI_ADD_STATION_DONE_EVENTID            = 0x1807,
-       WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID      = 0x1808,
-       WMI_PHY_GET_STATISTICS_EVENTID          = 0x1809,
        WMI_FS_TUNE_DONE_EVENTID                = 0x180a,
-       WMI_CORR_MEASURE_DONE_EVENTID           = 0x180b,
+       WMI_CORR_MEASURE_EVENTID                = 0x180b,
+       WMI_READ_RSSI_EVENTID                   = 0x180c,
        WMI_TEMP_SENSE_DONE_EVENTID             = 0x180e,
        WMI_DC_CALIB_DONE_EVENTID               = 0x180f,
        WMI_IQ_TX_CALIB_DONE_EVENTID            = 0x1811,
@@ -727,10 +833,9 @@ enum wmi_event_id {
        WMI_MARLON_R_WRITE_DONE_EVENTID         = 0x1819,
        WMI_MARLON_R_TXRX_SEL_DONE_EVENTID      = 0x181a,
        WMI_SILENT_RSSI_CALIB_DONE_EVENTID      = 0x181d,
-
+       WMI_RF_RX_TEST_DONE_EVENTID             = 0x181e,
        WMI_CFG_RX_CHAIN_DONE_EVENTID           = 0x1820,
        WMI_VRING_CFG_DONE_EVENTID              = 0x1821,
-       WMI_RX_ON_DONE_EVENTID                  = 0x1822,
        WMI_BA_STATUS_EVENTID                   = 0x1823,
        WMI_RCP_ADDBA_REQ_EVENTID               = 0x1824,
        WMI_ADDBA_RESP_SENT_EVENTID             = 0x1825,
@@ -738,7 +843,6 @@ enum wmi_event_id {
        WMI_GET_SSID_EVENTID                    = 0x1828,
        WMI_GET_PCP_CHANNEL_EVENTID             = 0x182a,
        WMI_SW_TX_COMPLETE_EVENTID              = 0x182b,
-       WMI_RX_OFF_DONE_EVENTID                 = 0x182c,
 
        WMI_READ_MAC_RXQ_EVENTID                = 0x1830,
        WMI_READ_MAC_TXQ_EVENTID                = 0x1831,
@@ -765,7 +869,16 @@ enum wmi_event_id {
        WMI_UNIT_TEST_EVENTID                   = 0x1900,
        WMI_FLASH_READ_DONE_EVENTID             = 0x1902,
        WMI_FLASH_WRITE_DONE_EVENTID            = 0x1903,
-
+       /*P2P*/
+       WMI_PORT_ALLOCATED_EVENTID              = 0x1911,
+       WMI_PORT_DELETED_EVENTID                = 0x1912,
+       WMI_LISTEN_STARTED_EVENTID              = 0x1914,
+       WMI_SEARCH_STARTED_EVENTID              = 0x1915,
+       WMI_DISCOVERY_STARTED_EVENTID           = 0x1916,
+       WMI_DISCOVERY_STOPPED_EVENTID           = 0x1917,
+       WMI_PCP_STARTED_EVENTID                 = 0x1918,
+       WMI_PCP_STOPPED_EVENTID                 = 0x1919,
+       WMI_PCP_FACTOR_EVENTID                  = 0x191a,
        WMI_SET_CHANNEL_EVENTID                 = 0x9000,
        WMI_ASSOC_REQ_EVENTID                   = 0x9001,
        WMI_EAPOL_RX_EVENTID                    = 0x9002,
@@ -777,6 +890,12 @@ enum wmi_event_id {
  * Events data structures
  */
 
+
+enum wmi_fw_status {
+       WMI_FW_STATUS_SUCCESS,
+       WMI_FW_STATUS_FAILURE,
+};
+
 /*
  * WMI_RF_MGMT_STATUS_EVENTID
  */
@@ -857,7 +976,7 @@ struct wmi_ready_event {
        __le32 abi_version;
        u8 mac[WMI_MAC_LEN];
        u8 phy_capability;              /* enum wmi_phy_capability */
-       u8 reserved;
+       u8 numof_additional_mids;
 } __packed;
 
 /*
@@ -876,6 +995,8 @@ struct wmi_notify_req_done_event {
        __le16 other_rx_sector;
        __le16 other_tx_sector;
        __le16 range;
+       u8 sqi;
+       u8 reserved[3];
 } __packed;
 
 /*
@@ -951,27 +1072,15 @@ struct wmi_vring_ba_status_event {
  * WMI_DELBA_EVENTID
  */
 struct wmi_delba_event {
-
-       #define CIDXTID_CID_POS (0)
-       #define CIDXTID_CID_LEN (4)
-       #define CIDXTID_CID_MSK (0xF)
-       #define CIDXTID_TID_POS (4)
-       #define CIDXTID_TID_LEN (4)
-       #define CIDXTID_TID_MSK (0xF0)
        u8 cidxtid;
-
        u8 from_initiator;
        __le16 reason;
 } __packed;
 
+
 /*
  * WMI_VRING_CFG_DONE_EVENTID
  */
-enum wmi_vring_cfg_done_event_status {
-       WMI_VRING_CFG_SUCCESS           = 0,
-       WMI_VRING_CFG_FAILURE           = 1,
-};
-
 struct wmi_vring_cfg_done_event {
        u8 ringid;
        u8 status;
@@ -982,21 +1091,8 @@ struct wmi_vring_cfg_done_event {
 /*
  * WMI_ADDBA_RESP_SENT_EVENTID
  */
-enum wmi_rcp_addba_resp_sent_event_status {
-       WMI_ADDBA_SUCCESS               = 0,
-       WMI_ADDBA_FAIL                  = 1,
-};
-
 struct wmi_rcp_addba_resp_sent_event {
-
-       #define CIDXTID_CID_POS (0)
-       #define CIDXTID_CID_LEN (4)
-       #define CIDXTID_CID_MSK (0xF)
-       #define CIDXTID_TID_POS (4)
-       #define CIDXTID_TID_LEN (4)
-       #define CIDXTID_TID_MSK (0xF0)
        u8 cidxtid;
-
        u8 reserved;
        __le16 status;
 } __packed;
@@ -1005,15 +1101,7 @@ struct wmi_rcp_addba_resp_sent_event {
  * WMI_RCP_ADDBA_REQ_EVENTID
  */
 struct wmi_rcp_addba_req_event {
-
-       #define CIDXTID_CID_POS (0)
-       #define CIDXTID_CID_LEN (4)
-       #define CIDXTID_CID_MSK (0xF)
-       #define CIDXTID_TID_POS (4)
-       #define CIDXTID_TID_LEN (4)
-       #define CIDXTID_TID_MSK (0xF0)
        u8 cidxtid;
-
        u8 dialog_token;
        __le16 ba_param_set;    /* ieee80211_ba_parameterset as it received */
        __le16 ba_timeout;
@@ -1055,6 +1143,7 @@ struct wmi_data_port_open_event {
        u8 reserved[3];
 } __packed;
 
+
 /*
  * WMI_GET_PCP_CHANNEL_EVENTID
  */
@@ -1063,6 +1152,54 @@ struct wmi_get_pcp_channel_event {
        u8 reserved[3];
 } __packed;
 
+
+/*
+* WMI_PORT_ALLOCATED_EVENTID
+*/
+struct wmi_port_allocated_event {
+       u8 status;      /* wmi_fw_status */
+       u8 reserved[3];
+} __packed;
+
+/*
+* WMI_PORT_DELETED_EVENTID
+*/
+struct wmi_port_deleted_event {
+       u8 status;      /* wmi_fw_status */
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_LISTEN_STARTED_EVENTID
+ */
+struct wmi_listen_started_event {
+       u8 status;      /* wmi_fw_status */
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_SEARCH_STARTED_EVENTID
+ */
+struct wmi_search_started_event {
+       u8 status;      /* wmi_fw_status */
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_PCP_STARTED_EVENTID
+ */
+struct wmi_pcp_started_event {
+       u8 status;      /* wmi_fw_status */
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_PCP_FACTOR_EVENTID
+ */
+struct wmi_pcp_factor_event {
+       __le32 pcp_factor;
+} __packed;
+
 /*
  * WMI_SW_TX_COMPLETE_EVENTID
  */
@@ -1077,6 +1214,23 @@ struct wmi_sw_tx_complete_event {
        u8 reserved[3];
 } __packed;
 
+/*
+ * WMI_CORR_MEASURE_EVENTID
+ */
+struct wmi_corr_measure_event {
+       s32 i;
+       s32 q;
+       s32 image_i;
+       s32 image_q;
+} __packed;
+
+/*
+ * WMI_READ_RSSI_EVENTID
+ */
+struct wmi_read_rssi_event {
+       __le32 ina_rssi_adc_dbm;
+} __packed;
+
 /*
  * WMI_GET_SSID_EVENTID
  */
@@ -1091,7 +1245,8 @@ struct wmi_get_ssid_event {
 struct wmi_rx_mgmt_info {
        u8 mcs;
        s8 snr;
-       __le16 range;
+       u8 range;
+       u8 sqi;
        __le16 stype;
        __le16 status;
        __le32 len;
@@ -1113,4 +1268,14 @@ struct wmi_echo_event {
        __le32 echoed_value;
 } __packed;
 
+/*
+ * WMI_TEMP_SENSE_DONE_EVENTID
+ *
+ * Measure MAC and radio temperatures
+ */
+struct wmi_temp_sense_done_event {
+       __le32 marlon_m_t1000;
+       __le32 marlon_r_t1000;
+} __packed;
+
 #endif /* __WILOCITY_WMI_H__ */
index 287c6b670a36c9ac5123d64071e4f11fa93575bb..078e6f3477a9dfb3aa9a1fd67c7cb0fd4c8fa8cb 100644 (file)
@@ -131,7 +131,7 @@ config B43_PHY_LP
 
 config B43_PHY_HT
        bool "Support for HT-PHY (high throughput) devices"
-       depends on B43
+       depends on B43 && B43_BCMA
        ---help---
          Support for the HT-PHY.
 
@@ -166,8 +166,8 @@ config B43_DEBUG
          Broadcom 43xx debugging.
 
          This adds additional runtime sanity checks and statistics to the driver.
-         These checks and statistics might me expensive and hurt runtime performance
-         of your system.
+         These checks and statistics might be expensive and hurt the runtime
+         performance of your system.
          This also adds the b43 debugfs interface.
 
          Do not enable this, unless you are debugging the driver.
index 10e288d470e75be03ef936048fe70574ac3ea2c2..f5e840104f4be2f3a266371b8dae386329da26be 100644 (file)
@@ -285,7 +285,9 @@ enum {
 #define B43_SHM_SH_DTIMPER             0x0012  /* DTIM period */
 #define B43_SHM_SH_NOSLPZNATDTIM       0x004C  /* NOSLPZNAT DTIM */
 /* SHM_SHARED beacon/AP variables */
+#define B43_SHM_SH_BT_BASE0            0x0068  /* Beacon template base 0 */
 #define B43_SHM_SH_BTL0                        0x0018  /* Beacon template length 0 */
+#define B43_SHM_SH_BT_BASE1            0x0468  /* Beacon template base 1 */
 #define B43_SHM_SH_BTL1                        0x001A  /* Beacon template length 1 */
 #define B43_SHM_SH_BTSFOFF             0x001C  /* Beacon TSF offset */
 #define B43_SHM_SH_TIMBPOS             0x001E  /* TIM B position in beacon */
@@ -473,6 +475,12 @@ enum {
 #define B43_MACCMD_CCA                 0x00000008      /* Clear channel assessment */
 #define B43_MACCMD_BGNOISE             0x00000010      /* Background noise */
 
+/* See BCMA_CLKCTLST_EXTRESREQ and BCMA_CLKCTLST_EXTRESST */
+#define B43_BCMA_CLKCTLST_80211_PLL_REQ        0x00000100
+#define B43_BCMA_CLKCTLST_PHY_PLL_REQ  0x00000200
+#define B43_BCMA_CLKCTLST_80211_PLL_ST 0x01000000
+#define B43_BCMA_CLKCTLST_PHY_PLL_ST   0x02000000
+
 /* BCMA 802.11 core specific IO Control (BCMA_IOCTL) flags */
 #define B43_BCMA_IOCTL_PHY_CLKEN       0x00000004      /* PHY Clock Enable */
 #define B43_BCMA_IOCTL_PHY_RESET       0x00000008      /* PHY Reset */
index 122146943bf204693b540b124f6c947bec34833b..523355b876597f5d0db6e003f9230367c6777a86 100644 (file)
@@ -419,8 +419,6 @@ static inline
 
 static int alloc_ringmemory(struct b43_dmaring *ring)
 {
-       gfp_t flags = GFP_KERNEL;
-
        /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
         * alignment and 8K buffers for 64-bit DMA with 8K alignment.
         * In practice we could use smaller buffers for the latter, but the
@@ -435,12 +433,9 @@ static int alloc_ringmemory(struct b43_dmaring *ring)
 
        ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev,
                                            ring_mem_size, &(ring->dmabase),
-                                           flags);
-       if (!ring->descbase) {
-               b43err(ring->dev->wl, "DMA ringmemory allocation failed\n");
+                                           GFP_KERNEL | __GFP_ZERO);
+       if (!ring->descbase)
                return -ENOMEM;
-       }
-       memset(ring->descbase, 0, ring_mem_size);
 
        return 0;
 }
index 05682736e466410cb5497b6736700ebf7a1049c4..4ac73d2f86058f371226b56d7d55cf61d8de13f5 100644 (file)
@@ -1189,10 +1189,15 @@ static void b43_bcma_phy_reset(struct b43_wldev *dev)
 
 static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode)
 {
+       u32 req = B43_BCMA_CLKCTLST_80211_PLL_REQ |
+                 B43_BCMA_CLKCTLST_PHY_PLL_REQ;
+       u32 status = B43_BCMA_CLKCTLST_80211_PLL_ST |
+                    B43_BCMA_CLKCTLST_PHY_PLL_ST;
+
        b43_device_enable(dev, B43_BCMA_IOCTL_PHY_CLKEN);
        bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST);
        b43_bcma_phy_reset(dev);
-       bcma_core_pll_ctl(dev->dev->bdev, 0x300, 0x3000000, true);
+       bcma_core_pll_ctl(dev->dev->bdev, req, status, true);
 }
 #endif
 
@@ -1305,17 +1310,19 @@ static u32 b43_jssi_read(struct b43_wldev *dev)
 {
        u32 val = 0;
 
-       val = b43_shm_read16(dev, B43_SHM_SHARED, 0x08A);
+       val = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI1);
        val <<= 16;
-       val |= b43_shm_read16(dev, B43_SHM_SHARED, 0x088);
+       val |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI0);
 
        return val;
 }
 
 static void b43_jssi_write(struct b43_wldev *dev, u32 jssi)
 {
-       b43_shm_write16(dev, B43_SHM_SHARED, 0x088, (jssi & 0x0000FFFF));
-       b43_shm_write16(dev, B43_SHM_SHARED, 0x08A, (jssi & 0xFFFF0000) >> 16);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI0,
+                       (jssi & 0x0000FFFF));
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI1,
+                       (jssi & 0xFFFF0000) >> 16);
 }
 
 static void b43_generate_noise_sample(struct b43_wldev *dev)
@@ -1618,7 +1625,7 @@ static void b43_upload_beacon0(struct b43_wldev *dev)
 
        if (wl->beacon0_uploaded)
                return;
-       b43_write_beacon_template(dev, 0x68, 0x18);
+       b43_write_beacon_template(dev, B43_SHM_SH_BT_BASE0, B43_SHM_SH_BTL0);
        wl->beacon0_uploaded = true;
 }
 
@@ -1628,7 +1635,7 @@ static void b43_upload_beacon1(struct b43_wldev *dev)
 
        if (wl->beacon1_uploaded)
                return;
-       b43_write_beacon_template(dev, 0x468, 0x1A);
+       b43_write_beacon_template(dev, B43_SHM_SH_BT_BASE1, B43_SHM_SH_BTL1);
        wl->beacon1_uploaded = true;
 }
 
@@ -2775,9 +2782,7 @@ static int b43_gpio_init(struct b43_wldev *dev)
        switch (dev->dev->bus_type) {
 #ifdef CONFIG_B43_BCMA
        case B43_BUS_BCMA:
-               bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
-                               (bcma_cc_read32(&dev->dev->bdev->bus->drv_cc,
-                                       BCMA_CC_GPIOCTL) & ~mask) | set);
+               bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, mask, set);
                break;
 #endif
 #ifdef CONFIG_B43_SSB
@@ -2802,8 +2807,7 @@ static void b43_gpio_cleanup(struct b43_wldev *dev)
        switch (dev->dev->bus_type) {
 #ifdef CONFIG_B43_BCMA
        case B43_BUS_BCMA:
-               bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
-                               0);
+               bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, ~0, 0);
                break;
 #endif
 #ifdef CONFIG_B43_SSB
@@ -3111,7 +3115,7 @@ static int b43_chip_init(struct b43_wldev *dev)
 
        /* Probe Response Timeout value */
        /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */
-       b43_shm_write16(dev, B43_SHM_SHARED, 0x0074, 0x0000);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRMAXTIME, 0);
 
        /* Initially set the wireless operation mode. */
        b43_adjust_opmode(dev);
index 7416c5e9154d6a734ece86a5d071ca57f7be485a..b8667706fc2783379d7b56bacc812e8affa40eba 100644 (file)
@@ -153,10 +153,85 @@ static void b43_radio_2059_init(struct b43_wldev *dev)
        b43_radio_mask(dev, 0x11, ~0x0008);
 }
 
+/**************************************************
+ * RF
+ **************************************************/
+
+static void b43_phy_ht_force_rf_sequence(struct b43_wldev *dev, u16 rf_seq)
+{
+       u8 i;
+
+       u16 save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE);
+       b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, 0x3);
+
+       b43_phy_set(dev, B43_PHY_HT_RF_SEQ_TRIG, rf_seq);
+       for (i = 0; i < 200; i++) {
+               if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & rf_seq)) {
+                       i = 0;
+                       break;
+               }
+               msleep(1);
+       }
+       if (i)
+               b43err(dev->wl, "Forcing RF sequence timeout\n");
+
+       b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode);
+}
+
+static void b43_phy_ht_pa_override(struct b43_wldev *dev, bool enable)
+{
+       struct b43_phy_ht *htphy = dev->phy.ht;
+       static const u16 regs[3] = { B43_PHY_HT_RF_CTL_INT_C1,
+                                    B43_PHY_HT_RF_CTL_INT_C2,
+                                    B43_PHY_HT_RF_CTL_INT_C3 };
+       int i;
+
+       if (enable) {
+               for (i = 0; i < 3; i++)
+                       b43_phy_write(dev, regs[i], htphy->rf_ctl_int_save[i]);
+       } else {
+               for (i = 0; i < 3; i++)
+                       htphy->rf_ctl_int_save[i] = b43_phy_read(dev, regs[i]);
+               /* TODO: Does 5GHz band use different value (not 0x0400)? */
+               for (i = 0; i < 3; i++)
+                       b43_phy_write(dev, regs[i], 0x0400);
+       }
+}
+
 /**************************************************
  * Various PHY ops
  **************************************************/
 
+static u16 b43_phy_ht_classifier(struct b43_wldev *dev, u16 mask, u16 val)
+{
+       u16 tmp;
+       u16 allowed = B43_PHY_HT_CLASS_CTL_CCK_EN |
+                     B43_PHY_HT_CLASS_CTL_OFDM_EN |
+                     B43_PHY_HT_CLASS_CTL_WAITED_EN;
+
+       tmp = b43_phy_read(dev, B43_PHY_HT_CLASS_CTL);
+       tmp &= allowed;
+       tmp &= ~mask;
+       tmp |= (val & mask);
+       b43_phy_maskset(dev, B43_PHY_HT_CLASS_CTL, ~allowed, tmp);
+
+       return tmp;
+}
+
+static void b43_phy_ht_reset_cca(struct b43_wldev *dev)
+{
+       u16 bbcfg;
+
+       b43_phy_force_clock(dev, true);
+       bbcfg = b43_phy_read(dev, B43_PHY_HT_BBCFG);
+       b43_phy_write(dev, B43_PHY_HT_BBCFG, bbcfg | B43_PHY_HT_BBCFG_RSTCCA);
+       udelay(1);
+       b43_phy_write(dev, B43_PHY_HT_BBCFG, bbcfg & ~B43_PHY_HT_BBCFG_RSTCCA);
+       b43_phy_force_clock(dev, false);
+
+       b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX);
+}
+
 static void b43_phy_ht_zero_extg(struct b43_wldev *dev)
 {
        u8 i, j;
@@ -176,10 +251,10 @@ static void b43_phy_ht_afe_unk1(struct b43_wldev *dev)
 {
        u8 i;
 
-       const u16 ctl_regs[3][2] = {
-               { B43_PHY_HT_AFE_CTL1, B43_PHY_HT_AFE_CTL2 },
-               { B43_PHY_HT_AFE_CTL3, B43_PHY_HT_AFE_CTL4 },
-               { B43_PHY_HT_AFE_CTL5, B43_PHY_HT_AFE_CTL6},
+       static const u16 ctl_regs[3][2] = {
+               { B43_PHY_HT_AFE_C1_OVER, B43_PHY_HT_AFE_C1 },
+               { B43_PHY_HT_AFE_C2_OVER, B43_PHY_HT_AFE_C2 },
+               { B43_PHY_HT_AFE_C3_OVER, B43_PHY_HT_AFE_C3},
        };
 
        for (i = 0; i < 3; i++) {
@@ -193,27 +268,6 @@ static void b43_phy_ht_afe_unk1(struct b43_wldev *dev)
        }
 }
 
-static void b43_phy_ht_force_rf_sequence(struct b43_wldev *dev, u16 rf_seq)
-{
-       u8 i;
-
-       u16 save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE);
-       b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, 0x3);
-
-       b43_phy_set(dev, B43_PHY_HT_RF_SEQ_TRIG, rf_seq);
-       for (i = 0; i < 200; i++) {
-               if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & rf_seq)) {
-                       i = 0;
-                       break;
-               }
-               msleep(1);
-       }
-       if (i)
-               b43err(dev->wl, "Forcing RF sequence timeout\n");
-
-       b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode);
-}
-
 static void b43_phy_ht_read_clip_detection(struct b43_wldev *dev, u16 *clip_st)
 {
        clip_st[0] = b43_phy_read(dev, B43_PHY_HT_C1_CLIP1THRES);
@@ -239,16 +293,427 @@ static void b43_phy_ht_bphy_init(struct b43_wldev *dev)
        b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668);
 }
 
+/**************************************************
+ * Samples
+ **************************************************/
+
+static void b43_phy_ht_stop_playback(struct b43_wldev *dev)
+{
+       struct b43_phy_ht *phy_ht = dev->phy.ht;
+       u16 tmp;
+       int i;
+
+       tmp = b43_phy_read(dev, B43_PHY_HT_SAMP_STAT);
+       if (tmp & 0x1)
+               b43_phy_set(dev, B43_PHY_HT_SAMP_CMD, B43_PHY_HT_SAMP_CMD_STOP);
+       else if (tmp & 0x2)
+               b43_phy_mask(dev, B43_PHY_HT_IQLOCAL_CMDGCTL, 0x7FFF);
+
+       b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0x0004);
+
+       for (i = 0; i < 3; i++) {
+               if (phy_ht->bb_mult_save[i] >= 0) {
+                       b43_httab_write(dev, B43_HTTAB16(13, 0x63 + i * 4),
+                                       phy_ht->bb_mult_save[i]);
+                       b43_httab_write(dev, B43_HTTAB16(13, 0x67 + i * 4),
+                                       phy_ht->bb_mult_save[i]);
+               }
+       }
+}
+
+static u16 b43_phy_ht_load_samples(struct b43_wldev *dev)
+{
+       int i;
+       u16 len = 20 << 3;
+
+       b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, 0x4400);
+
+       for (i = 0; i < len; i++) {
+               b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, 0);
+               b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, 0);
+       }
+
+       return len;
+}
+
+static void b43_phy_ht_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
+                                  u16 wait)
+{
+       struct b43_phy_ht *phy_ht = dev->phy.ht;
+       u16 save_seq_mode;
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               if (phy_ht->bb_mult_save[i] < 0)
+                       phy_ht->bb_mult_save[i] = b43_httab_read(dev, B43_HTTAB16(13, 0x63 + i * 4));
+       }
+
+       b43_phy_write(dev, B43_PHY_HT_SAMP_DEP_CNT, samps - 1);
+       if (loops != 0xFFFF)
+               loops--;
+       b43_phy_write(dev, B43_PHY_HT_SAMP_LOOP_CNT, loops);
+       b43_phy_write(dev, B43_PHY_HT_SAMP_WAIT_CNT, wait);
+
+       save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE);
+       b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE,
+                   B43_PHY_HT_RF_SEQ_MODE_CA_OVER);
+
+       /* TODO: find out mask bits! Do we need more function arguments? */
+       b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0);
+       b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0);
+       b43_phy_mask(dev, B43_PHY_HT_IQLOCAL_CMDGCTL, ~0);
+       b43_phy_set(dev, B43_PHY_HT_SAMP_CMD, 0x1);
+
+       for (i = 0; i < 100; i++) {
+               if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & 1)) {
+                       i = 0;
+                       break;
+               }
+               udelay(10);
+       }
+       if (i)
+               b43err(dev->wl, "run samples timeout\n");
+
+       b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode);
+}
+
+static void b43_phy_ht_tx_tone(struct b43_wldev *dev)
+{
+       u16 samp;
+
+       samp = b43_phy_ht_load_samples(dev);
+       b43_phy_ht_run_samples(dev, samp, 0xFFFF, 0);
+}
+
+/**************************************************
+ * RSSI
+ **************************************************/
+
+static void b43_phy_ht_rssi_select(struct b43_wldev *dev, u8 core_sel,
+                                  u8 rssi_type)
+{
+       static const u16 ctl_regs[3][2] = {
+               { B43_PHY_HT_AFE_C1, B43_PHY_HT_AFE_C1_OVER, },
+               { B43_PHY_HT_AFE_C2, B43_PHY_HT_AFE_C2_OVER, },
+               { B43_PHY_HT_AFE_C3, B43_PHY_HT_AFE_C3_OVER, },
+       };
+       static const u16 radio_r[] = { R2059_SYN, R2059_TXRX0, R2059_RXRX1, };
+       int core;
+
+       if (core_sel == 0) {
+               b43err(dev->wl, "RSSI selection for core off not implemented yet\n");
+       } else {
+               for (core = 0; core < 3; core++) {
+                       /* Check if caller requested a one specific core */
+                       if ((core_sel == 1 && core != 0) ||
+                           (core_sel == 2 && core != 1) ||
+                           (core_sel == 3 && core != 2))
+                               continue;
+
+                       switch (rssi_type) {
+                       case 4:
+                               b43_phy_set(dev, ctl_regs[core][0], 0x3 << 8);
+                               b43_phy_set(dev, ctl_regs[core][0], 0x3 << 10);
+                               b43_phy_set(dev, ctl_regs[core][1], 0x1 << 9);
+                               b43_phy_set(dev, ctl_regs[core][1], 0x1 << 10);
+
+                               b43_radio_set(dev, R2059_RXRX1 | 0xbf, 0x1);
+                               b43_radio_write(dev, radio_r[core] | 0x159,
+                                               0x11);
+                               break;
+                       default:
+                               b43err(dev->wl, "RSSI selection for type %d not implemented yet\n",
+                                      rssi_type);
+                       }
+               }
+       }
+}
+
+static void b43_phy_ht_poll_rssi(struct b43_wldev *dev, u8 type, s32 *buf,
+                                u8 nsamp)
+{
+       u16 phy_regs_values[12];
+       static const u16 phy_regs_to_save[] = {
+               B43_PHY_HT_AFE_C1, B43_PHY_HT_AFE_C1_OVER,
+               0x848, 0x841,
+               B43_PHY_HT_AFE_C2, B43_PHY_HT_AFE_C2_OVER,
+               0x868, 0x861,
+               B43_PHY_HT_AFE_C3, B43_PHY_HT_AFE_C3_OVER,
+               0x888, 0x881,
+       };
+       u16 tmp[3];
+       int i;
+
+       for (i = 0; i < 12; i++)
+               phy_regs_values[i] = b43_phy_read(dev, phy_regs_to_save[i]);
+
+       b43_phy_ht_rssi_select(dev, 5, type);
+
+       for (i = 0; i < 6; i++)
+               buf[i] = 0;
+
+       for (i = 0; i < nsamp; i++) {
+               tmp[0] = b43_phy_read(dev, B43_PHY_HT_RSSI_C1);
+               tmp[1] = b43_phy_read(dev, B43_PHY_HT_RSSI_C2);
+               tmp[2] = b43_phy_read(dev, B43_PHY_HT_RSSI_C3);
+
+               buf[0] += ((s8)((tmp[0] & 0x3F) << 2)) >> 2;
+               buf[1] += ((s8)(((tmp[0] >> 8) & 0x3F) << 2)) >> 2;
+               buf[2] += ((s8)((tmp[1] & 0x3F) << 2)) >> 2;
+               buf[3] += ((s8)(((tmp[1] >> 8) & 0x3F) << 2)) >> 2;
+               buf[4] += ((s8)((tmp[2] & 0x3F) << 2)) >> 2;
+               buf[5] += ((s8)(((tmp[2] >> 8) & 0x3F) << 2)) >> 2;
+       }
+
+       for (i = 0; i < 12; i++)
+               b43_phy_write(dev, phy_regs_to_save[i], phy_regs_values[i]);
+}
+
+/**************************************************
+ * Tx/Rx
+ **************************************************/
+
+static void b43_phy_ht_tx_power_fix(struct b43_wldev *dev)
+{
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               u16 mask;
+               u32 tmp = b43_httab_read(dev, B43_HTTAB32(26, 0xE8));
+
+               if (0) /* FIXME */
+                       mask = 0x2 << (i * 4);
+               else
+                       mask = 0;
+               b43_phy_mask(dev, B43_PHY_EXTG(0x108), mask);
+
+               b43_httab_write(dev, B43_HTTAB16(7, 0x110 + i), tmp >> 16);
+               b43_httab_write(dev, B43_HTTAB8(13, 0x63 + (i * 4)),
+                               tmp & 0xFF);
+               b43_httab_write(dev, B43_HTTAB8(13, 0x73 + (i * 4)),
+                               tmp & 0xFF);
+       }
+}
+
+static void b43_phy_ht_tx_power_ctl(struct b43_wldev *dev, bool enable)
+{
+       struct b43_phy_ht *phy_ht = dev->phy.ht;
+       u16 en_bits = B43_PHY_HT_TXPCTL_CMD_C1_COEFF |
+                     B43_PHY_HT_TXPCTL_CMD_C1_HWPCTLEN |
+                     B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN;
+       static const u16 cmd_regs[3] = { B43_PHY_HT_TXPCTL_CMD_C1,
+                                        B43_PHY_HT_TXPCTL_CMD_C2,
+                                        B43_PHY_HT_TXPCTL_CMD_C3 };
+       int i;
+
+       if (!enable) {
+               if (b43_phy_read(dev, B43_PHY_HT_TXPCTL_CMD_C1) & en_bits) {
+                       /* We disable enabled TX pwr ctl, save it's state */
+                       /*
+                        * TODO: find the registers. On N-PHY they were 0x1ed
+                        * and 0x1ee, we need 3 such a registers for HT-PHY
+                        */
+               }
+               b43_phy_mask(dev, B43_PHY_HT_TXPCTL_CMD_C1, ~en_bits);
+       } else {
+               b43_phy_set(dev, B43_PHY_HT_TXPCTL_CMD_C1, en_bits);
+
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       for (i = 0; i < 3; i++)
+                               b43_phy_write(dev, cmd_regs[i], 0x32);
+               }
+
+               for (i = 0; i < 3; i++)
+                       if (phy_ht->tx_pwr_idx[i] <=
+                           B43_PHY_HT_TXPCTL_CMD_C1_INIT)
+                               b43_phy_write(dev, cmd_regs[i],
+                                             phy_ht->tx_pwr_idx[i]);
+       }
+
+       phy_ht->tx_pwr_ctl = enable;
+}
+
+static void b43_phy_ht_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
+{
+       struct b43_phy_ht *phy_ht = dev->phy.ht;
+       s32 rssi_buf[6];
+
+       /* TODO */
+
+       b43_phy_ht_tx_tone(dev);
+       udelay(20);
+       b43_phy_ht_poll_rssi(dev, 4, rssi_buf, 1);
+       b43_phy_ht_stop_playback(dev);
+       b43_phy_ht_reset_cca(dev);
+
+       phy_ht->idle_tssi[0] = rssi_buf[0] & 0xff;
+       phy_ht->idle_tssi[1] = rssi_buf[2] & 0xff;
+       phy_ht->idle_tssi[2] = rssi_buf[4] & 0xff;
+
+       /* TODO */
+}
+
+static void b43_phy_ht_tx_power_ctl_setup(struct b43_wldev *dev)
+{
+       struct b43_phy_ht *phy_ht = dev->phy.ht;
+       struct ssb_sprom *sprom = dev->dev->bus_sprom;
+
+       u8 *idle = phy_ht->idle_tssi;
+       u8 target[3];
+       s16 a1[3], b0[3], b1[3];
+
+       u16 freq = dev->phy.channel_freq;
+       int i, c;
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               for (c = 0; c < 3; c++) {
+                       target[c] = sprom->core_pwr_info[c].maxpwr_2g;
+                       a1[c] = sprom->core_pwr_info[c].pa_2g[0];
+                       b0[c] = sprom->core_pwr_info[c].pa_2g[1];
+                       b1[c] = sprom->core_pwr_info[c].pa_2g[2];
+               }
+       } else if (freq >= 4900 && freq < 5100) {
+               for (c = 0; c < 3; c++) {
+                       target[c] = sprom->core_pwr_info[c].maxpwr_5gl;
+                       a1[c] = sprom->core_pwr_info[c].pa_5gl[0];
+                       b0[c] = sprom->core_pwr_info[c].pa_5gl[1];
+                       b1[c] = sprom->core_pwr_info[c].pa_5gl[2];
+               }
+       } else if (freq >= 5100 && freq < 5500) {
+               for (c = 0; c < 3; c++) {
+                       target[c] = sprom->core_pwr_info[c].maxpwr_5g;
+                       a1[c] = sprom->core_pwr_info[c].pa_5g[0];
+                       b0[c] = sprom->core_pwr_info[c].pa_5g[1];
+                       b1[c] = sprom->core_pwr_info[c].pa_5g[2];
+               }
+       } else if (freq >= 5500) {
+               for (c = 0; c < 3; c++) {
+                       target[c] = sprom->core_pwr_info[c].maxpwr_5gh;
+                       a1[c] = sprom->core_pwr_info[c].pa_5gh[0];
+                       b0[c] = sprom->core_pwr_info[c].pa_5gh[1];
+                       b1[c] = sprom->core_pwr_info[c].pa_5gh[2];
+               }
+       } else {
+               target[0] = target[1] = target[2] = 52;
+               a1[0] = a1[1] = a1[2] = -424;
+               b0[0] = b0[1] = b0[2] = 5612;
+               b1[0] = b1[1] = b1[2] = -1393;
+       }
+
+       b43_phy_set(dev, B43_PHY_HT_TSSIMODE, B43_PHY_HT_TSSIMODE_EN);
+       b43_phy_mask(dev, B43_PHY_HT_TXPCTL_CMD_C1,
+                    ~B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN & 0xFFFF);
+
+       /* TODO: Does it depend on sprom->fem.ghz2.tssipos? */
+       b43_phy_set(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, 0x4000);
+
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1,
+                       ~B43_PHY_HT_TXPCTL_CMD_C1_INIT, 0x19);
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C2,
+                       ~B43_PHY_HT_TXPCTL_CMD_C2_INIT, 0x19);
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C3,
+                       ~B43_PHY_HT_TXPCTL_CMD_C3_INIT, 0x19);
+
+       b43_phy_set(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI,
+                   B43_PHY_HT_TXPCTL_IDLE_TSSI_BINF);
+
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI,
+                       ~B43_PHY_HT_TXPCTL_IDLE_TSSI_C1,
+                       idle[0] << B43_PHY_HT_TXPCTL_IDLE_TSSI_C1_SHIFT);
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI,
+                       ~B43_PHY_HT_TXPCTL_IDLE_TSSI_C2,
+                       idle[1] << B43_PHY_HT_TXPCTL_IDLE_TSSI_C2_SHIFT);
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI2,
+                       ~B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3,
+                       idle[2] << B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3_SHIFT);
+
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_N, ~B43_PHY_HT_TXPCTL_N_TSSID,
+                       0xf0);
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_N, ~B43_PHY_HT_TXPCTL_N_NPTIL2,
+                       0x3 << B43_PHY_HT_TXPCTL_N_NPTIL2_SHIFT);
+#if 0
+       /* TODO: what to mask/set? */
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1, 0x800, 0)
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1, 0x400, 0)
+#endif
+
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR,
+                       ~B43_PHY_HT_TXPCTL_TARG_PWR_C1,
+                       target[0] << B43_PHY_HT_TXPCTL_TARG_PWR_C1_SHIFT);
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR,
+                       ~B43_PHY_HT_TXPCTL_TARG_PWR_C2 & 0xFFFF,
+                       target[1] << B43_PHY_HT_TXPCTL_TARG_PWR_C2_SHIFT);
+       b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR2,
+                       ~B43_PHY_HT_TXPCTL_TARG_PWR2_C3,
+                       target[2] << B43_PHY_HT_TXPCTL_TARG_PWR2_C3_SHIFT);
+
+       for (c = 0; c < 3; c++) {
+               s32 num, den, pwr;
+               u32 regval[64];
+
+               for (i = 0; i < 64; i++) {
+                       num = 8 * (16 * b0[c] + b1[c] * i);
+                       den = 32768 + a1[c] * i;
+                       pwr = max((4 * num + den / 2) / den, -8);
+                       regval[i] = pwr;
+               }
+               b43_httab_write_bulk(dev, B43_HTTAB16(26 + c, 0), 64, regval);
+       }
+}
+
 /**************************************************
  * Channel switching ops.
  **************************************************/
 
+static void b43_phy_ht_spur_avoid(struct b43_wldev *dev,
+                                 struct ieee80211_channel *new_channel)
+{
+       struct bcma_device *core = dev->dev->bdev;
+       int spuravoid = 0;
+       u16 tmp;
+
+       /* Check for 13 and 14 is just a guess, we don't have enough logs. */
+       if (new_channel->hw_value == 13 || new_channel->hw_value == 14)
+               spuravoid = 1;
+       bcma_core_pll_ctl(core, B43_BCMA_CLKCTLST_PHY_PLL_REQ, 0, false);
+       bcma_pmu_spuravoid_pllupdate(&core->bus->drv_cc, spuravoid);
+       bcma_core_pll_ctl(core,
+                         B43_BCMA_CLKCTLST_80211_PLL_REQ |
+                         B43_BCMA_CLKCTLST_PHY_PLL_REQ,
+                         B43_BCMA_CLKCTLST_80211_PLL_ST |
+                         B43_BCMA_CLKCTLST_PHY_PLL_ST, false);
+
+       /* Values has been taken from wlc_bmac_switch_macfreq comments */
+       switch (spuravoid) {
+       case 2: /* 126MHz */
+               tmp = 0x2082;
+               break;
+       case 1: /* 123MHz */
+               tmp = 0x5341;
+               break;
+       default: /* 120MHz */
+               tmp = 0x8889;
+       }
+
+       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, tmp);
+       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+
+       /* TODO: reset PLL */
+
+       if (spuravoid)
+               b43_phy_set(dev, B43_PHY_HT_BBCFG, B43_PHY_HT_BBCFG_RSTRX);
+       else
+               b43_phy_mask(dev, B43_PHY_HT_BBCFG,
+                               ~B43_PHY_HT_BBCFG_RSTRX & 0xFFFF);
+
+       b43_phy_ht_reset_cca(dev);
+}
+
 static void b43_phy_ht_channel_setup(struct b43_wldev *dev,
                                const struct b43_phy_ht_channeltab_e_phy *e,
                                struct ieee80211_channel *new_channel)
 {
        bool old_band_5ghz;
-       u8 i;
 
        old_band_5ghz = b43_phy_read(dev, B43_PHY_HT_BANDCTL) & 0; /* FIXME */
        if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) {
@@ -264,25 +729,20 @@ static void b43_phy_ht_channel_setup(struct b43_wldev *dev,
        b43_phy_write(dev, B43_PHY_HT_BW5, e->bw5);
        b43_phy_write(dev, B43_PHY_HT_BW6, e->bw6);
 
-       /* TODO: some ops on PHY regs 0x0B0 and 0xC0A */
+       if (new_channel->hw_value == 14) {
+               b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_OFDM_EN, 0);
+               b43_phy_set(dev, B43_PHY_HT_TEST, 0x0800);
+       } else {
+               b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_OFDM_EN,
+                                     B43_PHY_HT_CLASS_CTL_OFDM_EN);
+               if (new_channel->band == IEEE80211_BAND_2GHZ)
+                       b43_phy_mask(dev, B43_PHY_HT_TEST, ~0x840);
+       }
 
-       /* TODO: separated function? */
-       for (i = 0; i < 3; i++) {
-               u16 mask;
-               u32 tmp = b43_httab_read(dev, B43_HTTAB32(26, 0xE8));
+       if (1) /* TODO: On N it's for early devices only, what about HT? */
+               b43_phy_ht_tx_power_fix(dev);
 
-               if (0) /* FIXME */
-                       mask = 0x2 << (i * 4);
-               else
-                       mask = 0;
-               b43_phy_mask(dev, B43_PHY_EXTG(0x108), mask);
-
-               b43_httab_write(dev, B43_HTTAB16(7, 0x110 + i), tmp >> 16);
-               b43_httab_write(dev, B43_HTTAB8(13, 0x63 + (i * 4)),
-                               tmp & 0xFF);
-               b43_httab_write(dev, B43_HTTAB8(13, 0x73 + (i * 4)),
-                               tmp & 0xFF);
-       }
+       b43_phy_ht_spur_avoid(dev, new_channel);
 
        b43_phy_write(dev, 0x017e, 0x3830);
 }
@@ -337,14 +797,29 @@ static void b43_phy_ht_op_prepare_structs(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
        struct b43_phy_ht *phy_ht = phy->ht;
+       int i;
 
        memset(phy_ht, 0, sizeof(*phy_ht));
+
+       phy_ht->tx_pwr_ctl = true;
+       for (i = 0; i < 3; i++)
+               phy_ht->tx_pwr_idx[i] = B43_PHY_HT_TXPCTL_CMD_C1_INIT + 1;
+
+       for (i = 0; i < 3; i++)
+               phy_ht->bb_mult_save[i] = -1;
 }
 
 static int b43_phy_ht_op_init(struct b43_wldev *dev)
 {
+       struct b43_phy_ht *phy_ht = dev->phy.ht;
        u16 tmp;
        u16 clip_state[3];
+       bool saved_tx_pwr_ctl;
+
+       if (dev->dev->bus_type != B43_BUS_BCMA) {
+               b43err(dev->wl, "HT-PHY is supported only on BCMA bus!\n");
+               return -EOPNOTSUPP;
+       }
 
        b43_phy_ht_tables_init(dev);
 
@@ -357,9 +832,9 @@ static int b43_phy_ht_op_init(struct b43_wldev *dev)
 
        b43_phy_mask(dev, B43_PHY_EXTG(0), ~0x3);
 
-       b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0);
-       b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0);
-       b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0);
+       b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0);
+       b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0);
+       b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0);
 
        b43_phy_write(dev, B43_PHY_EXTG(0x103), 0x20);
        b43_phy_write(dev, B43_PHY_EXTG(0x101), 0x20);
@@ -371,8 +846,11 @@ static int b43_phy_ht_op_init(struct b43_wldev *dev)
        if (0) /* TODO: condition */
                ; /* TODO: PHY op on reg 0x217 */
 
-       b43_phy_read(dev, 0xb0); /* TODO: what for? */
-       b43_phy_set(dev, 0xb0, 0x1);
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+               b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_CCK_EN, 0);
+       else
+               b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_CCK_EN,
+                                     B43_PHY_HT_CLASS_CTL_CCK_EN);
 
        b43_phy_set(dev, 0xb1, 0x91);
        b43_phy_write(dev, 0x32f, 0x0003);
@@ -448,12 +926,13 @@ static int b43_phy_ht_op_init(struct b43_wldev *dev)
 
        b43_mac_phy_clock_set(dev, true);
 
+       b43_phy_ht_pa_override(dev, false);
        b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RX2TX);
        b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX);
-
-       /* TODO: PHY op on reg 0xb0 */
+       b43_phy_ht_pa_override(dev, true);
 
        /* TODO: Should we restore it? Or store it in global PHY info? */
+       b43_phy_ht_classifier(dev, 0, 0);
        b43_phy_ht_read_clip_detection(dev, clip_state);
 
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
@@ -462,6 +941,13 @@ static int b43_phy_ht_op_init(struct b43_wldev *dev)
        b43_httab_write_bulk(dev, B43_HTTAB32(0x1a, 0xc0),
                        B43_HTTAB_1A_C0_LATE_SIZE, b43_httab_0x1a_0xc0_late);
 
+       saved_tx_pwr_ctl = phy_ht->tx_pwr_ctl;
+       b43_phy_ht_tx_power_fix(dev);
+       b43_phy_ht_tx_power_ctl(dev, false);
+       b43_phy_ht_tx_power_ctl_idle_tssi(dev);
+       b43_phy_ht_tx_power_ctl_setup(dev);
+       b43_phy_ht_tx_power_ctl(dev, saved_tx_pwr_ctl);
+
        return 0;
 }
 
@@ -506,19 +992,19 @@ static void b43_phy_ht_op_software_rfkill(struct b43_wldev *dev,
 static void b43_phy_ht_op_switch_analog(struct b43_wldev *dev, bool on)
 {
        if (on) {
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL2, 0x00cd);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0x0000);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL4, 0x00cd);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0x0000);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL6, 0x00cd);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0x0000);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C1, 0x00cd);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0x0000);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C2, 0x00cd);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0x0000);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C3, 0x00cd);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0x0000);
        } else {
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0x07ff);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL2, 0x00fd);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0x07ff);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL4, 0x00fd);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0x07ff);
-               b43_phy_write(dev, B43_PHY_HT_AFE_CTL6, 0x00fd);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0x07ff);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C1, 0x00fd);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0x07ff);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C2, 0x00fd);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0x07ff);
+               b43_phy_write(dev, B43_PHY_HT_AFE_C3, 0x00fd);
        }
 }
 
index 6544c4293b34ddc0f3980041d7f29ce547fb5212..9b2408efb2243e338472ba870b15ead97deded74 100644 (file)
 #define B43_PHY_HT_TABLE_ADDR                  0x072 /* Table address */
 #define B43_PHY_HT_TABLE_DATALO                        0x073 /* Table data low */
 #define B43_PHY_HT_TABLE_DATAHI                        0x074 /* Table data high */
+#define B43_PHY_HT_CLASS_CTL                   0x0B0 /* Classifier control */
+#define  B43_PHY_HT_CLASS_CTL_CCK_EN           0x0001 /* CCK enable */
+#define  B43_PHY_HT_CLASS_CTL_OFDM_EN          0x0002 /* OFDM enable */
+#define  B43_PHY_HT_CLASS_CTL_WAITED_EN                0x0004 /* Waited enable */
+#define B43_PHY_HT_IQLOCAL_CMDGCTL             0x0C2   /* I/Q LO cal command G control */
+#define B43_PHY_HT_SAMP_CMD                    0x0C3   /* Sample command */
+#define  B43_PHY_HT_SAMP_CMD_STOP              0x0002  /* Stop */
+#define B43_PHY_HT_SAMP_LOOP_CNT               0x0C4   /* Sample loop count */
+#define B43_PHY_HT_SAMP_WAIT_CNT               0x0C5   /* Sample wait count */
+#define B43_PHY_HT_SAMP_DEP_CNT                        0x0C6   /* Sample depth count */
+#define B43_PHY_HT_SAMP_STAT                   0x0C7   /* Sample status */
+#define B43_PHY_HT_TSSIMODE                    0x122   /* TSSI mode */
+#define  B43_PHY_HT_TSSIMODE_EN                        0x0001  /* TSSI enable */
+#define  B43_PHY_HT_TSSIMODE_PDEN              0x0002  /* Power det enable */
 #define B43_PHY_HT_BW1                         0x1CE
 #define B43_PHY_HT_BW2                         0x1CF
 #define B43_PHY_HT_BW3                         0x1D0
 #define B43_PHY_HT_BW4                         0x1D1
 #define B43_PHY_HT_BW5                         0x1D2
 #define B43_PHY_HT_BW6                         0x1D3
+#define B43_PHY_HT_TXPCTL_CMD_C1               0x1E7   /* TX power control command */
+#define  B43_PHY_HT_TXPCTL_CMD_C1_INIT         0x007F  /* Init */
+#define  B43_PHY_HT_TXPCTL_CMD_C1_COEFF                0x2000  /* Power control coefficients */
+#define  B43_PHY_HT_TXPCTL_CMD_C1_HWPCTLEN     0x4000  /* Hardware TX power control enable */
+#define  B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN       0x8000  /* TX power control enable */
+#define B43_PHY_HT_TXPCTL_N                    0x1E8   /* TX power control N num */
+#define  B43_PHY_HT_TXPCTL_N_TSSID             0x00FF  /* N TSSI delay */
+#define  B43_PHY_HT_TXPCTL_N_TSSID_SHIFT       0
+#define  B43_PHY_HT_TXPCTL_N_NPTIL2            0x0700  /* N PT integer log2 */
+#define  B43_PHY_HT_TXPCTL_N_NPTIL2_SHIFT      8
+#define B43_PHY_HT_TXPCTL_IDLE_TSSI            0x1E9   /* TX power control idle TSSI */
+#define  B43_PHY_HT_TXPCTL_IDLE_TSSI_C1                0x003F
+#define  B43_PHY_HT_TXPCTL_IDLE_TSSI_C1_SHIFT  0
+#define  B43_PHY_HT_TXPCTL_IDLE_TSSI_C2                0x3F00
+#define  B43_PHY_HT_TXPCTL_IDLE_TSSI_C2_SHIFT  8
+#define  B43_PHY_HT_TXPCTL_IDLE_TSSI_BINF      0x8000  /* Raw TSSI offset bin format */
+#define B43_PHY_HT_TXPCTL_TARG_PWR             0x1EA   /* TX power control target power */
+#define  B43_PHY_HT_TXPCTL_TARG_PWR_C1         0x00FF  /* Power 0 */
+#define  B43_PHY_HT_TXPCTL_TARG_PWR_C1_SHIFT   0
+#define  B43_PHY_HT_TXPCTL_TARG_PWR_C2         0xFF00  /* Power 1 */
+#define  B43_PHY_HT_TXPCTL_TARG_PWR_C2_SHIFT   8
+#define B43_PHY_HT_TXPCTL_CMD_C2               0x222
+#define  B43_PHY_HT_TXPCTL_CMD_C2_INIT         0x007F
+#define B43_PHY_HT_RSSI_C1                     0x219
+#define B43_PHY_HT_RSSI_C2                     0x21A
+#define B43_PHY_HT_RSSI_C3                     0x21B
 
 #define B43_PHY_HT_C1_CLIP1THRES               B43_PHY_OFDM(0x00E)
 #define B43_PHY_HT_C2_CLIP1THRES               B43_PHY_OFDM(0x04E)
 #define B43_PHY_HT_C3_CLIP1THRES               B43_PHY_OFDM(0x08E)
 
 #define B43_PHY_HT_RF_SEQ_MODE                 B43_PHY_EXTG(0x000)
+#define  B43_PHY_HT_RF_SEQ_MODE_CA_OVER                0x0001  /* Core active override */
+#define  B43_PHY_HT_RF_SEQ_MODE_TR_OVER                0x0002  /* Trigger override */
 #define B43_PHY_HT_RF_SEQ_TRIG                 B43_PHY_EXTG(0x003)
 #define  B43_PHY_HT_RF_SEQ_TRIG_RX2TX          0x0001 /* RX2TX */
 #define  B43_PHY_HT_RF_SEQ_TRIG_TX2RX          0x0002 /* TX2RX */
 
 #define B43_PHY_HT_RF_CTL1                     B43_PHY_EXTG(0x010)
 
-#define B43_PHY_HT_AFE_CTL1                    B43_PHY_EXTG(0x110)
-#define B43_PHY_HT_AFE_CTL2                    B43_PHY_EXTG(0x111)
-#define B43_PHY_HT_AFE_CTL3                    B43_PHY_EXTG(0x114)
-#define B43_PHY_HT_AFE_CTL4                    B43_PHY_EXTG(0x115)
-#define B43_PHY_HT_AFE_CTL5                    B43_PHY_EXTG(0x118)
-#define B43_PHY_HT_AFE_CTL6                    B43_PHY_EXTG(0x119)
+#define B43_PHY_HT_RF_CTL_INT_C1               B43_PHY_EXTG(0x04c)
+#define B43_PHY_HT_RF_CTL_INT_C2               B43_PHY_EXTG(0x06c)
+#define B43_PHY_HT_RF_CTL_INT_C3               B43_PHY_EXTG(0x08c)
+
+#define B43_PHY_HT_AFE_C1_OVER                 B43_PHY_EXTG(0x110)
+#define B43_PHY_HT_AFE_C1                      B43_PHY_EXTG(0x111)
+#define B43_PHY_HT_AFE_C2_OVER                 B43_PHY_EXTG(0x114)
+#define B43_PHY_HT_AFE_C2                      B43_PHY_EXTG(0x115)
+#define B43_PHY_HT_AFE_C3_OVER                 B43_PHY_EXTG(0x118)
+#define B43_PHY_HT_AFE_C3                      B43_PHY_EXTG(0x119)
+
+#define B43_PHY_HT_TXPCTL_CMD_C3               B43_PHY_EXTG(0x164)
+#define  B43_PHY_HT_TXPCTL_CMD_C3_INIT         0x007F
+#define B43_PHY_HT_TXPCTL_IDLE_TSSI2           B43_PHY_EXTG(0x165)     /* TX power control idle TSSI */
+#define  B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3       0x003F
+#define  B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3_SHIFT 0
+#define B43_PHY_HT_TXPCTL_TARG_PWR2            B43_PHY_EXTG(0x166)     /* TX power control target power */
+#define  B43_PHY_HT_TXPCTL_TARG_PWR2_C3                0x00FF
+#define  B43_PHY_HT_TXPCTL_TARG_PWR2_C3_SHIFT  0
+
+#define B43_PHY_HT_TEST                                B43_PHY_N_BMODE(0x00A)
 
 
 /* Values for PHY registers used on channel switching */
@@ -56,6 +113,14 @@ struct b43_phy_ht_channeltab_e_phy {
 
 
 struct b43_phy_ht {
+       u16 rf_ctl_int_save[3];
+
+       bool tx_pwr_ctl;
+       u8 tx_pwr_idx[3];
+
+       s32 bb_mult_save[3];
+
+       u8 idle_tssi[3];
 };
 
 
index 3ae28561f7a4078f88f9ddc636e2b89c5df42c6a..5ed352ddae9ed274854ed46c3faa5535d305bdff 100644 (file)
@@ -104,14 +104,8 @@ static void lpphy_read_band_sprom(struct b43_wldev *dev)
                maxpwr = sprom->maxpwr_bg;
                lpphy->max_tx_pwr_med_band = maxpwr;
                cckpo = sprom->cck2gpo;
-               /*
-                * We don't read SPROM's opo as specs say. On rev8 SPROMs
-                * opo == ofdm2gpo and we don't know any SSB with LP-PHY
-                * and SPROM rev below 8.
-                */
-               B43_WARN_ON(sprom->revision < 8);
-               ofdmpo = sprom->ofdm2gpo;
                if (cckpo) {
+                       ofdmpo = sprom->ofdm2gpo;
                        for (i = 0; i < 4; i++) {
                                lpphy->tx_max_rate[i] =
                                        maxpwr - (ofdmpo & 0xF) * 2;
@@ -124,11 +118,11 @@ static void lpphy_read_band_sprom(struct b43_wldev *dev)
                                ofdmpo >>= 4;
                        }
                } else {
-                       ofdmpo &= 0xFF;
+                       u8 opo = sprom->opo;
                        for (i = 0; i < 4; i++)
                                lpphy->tx_max_rate[i] = maxpwr;
                        for (i = 4; i < 15; i++)
-                               lpphy->tx_max_rate[i] = maxpwr - ofdmpo;
+                               lpphy->tx_max_rate[i] = maxpwr - opo;
                }
        } else { /* 5GHz */
                lpphy->tx_isolation_low_band = sprom->tri5gl;
index b70f220bc4b378e626a59752fca5bce965456340..63cca9c2bf970b8efe4114ac488689087b46325b 100644 (file)
@@ -2789,10 +2789,6 @@ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core,
  * Tx and Rx
  **************************************************/
 
-void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
-{//TODO
-}
-
 static void b43_nphy_op_adjust_txpower(struct b43_wldev *dev)
 {//TODO
 }
@@ -4892,7 +4888,7 @@ static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init)
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N */
-int b43_phy_initn(struct b43_wldev *dev)
+static int b43_phy_initn(struct b43_wldev *dev)
 {
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
        struct b43_phy *phy = &dev->phy;
index ce037fb6789a483842e37127046d621e29ff5319..b4fd9345d673a3542d94e66260ebc685eed7ef8d 100644 (file)
@@ -2980,7 +2980,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_rx[] = {
        .rx             = prefix##_rx,                  \
        .rx_length      = ARRAY_SIZE(prefix##_rx)
 
-struct b2056_inittabs_pts b2056_inittabs[] = {
+static const struct b2056_inittabs_pts b2056_inittabs[] = {
        [3] = { INITTABSPTS(b2056_inittab_rev3) },
        [4] = { INITTABSPTS(b2056_inittab_rev4) },
        [5] = { INITTABSPTS(b2056_inittab_rev5) },
@@ -9035,7 +9035,7 @@ static void b2056_upload_inittab(struct b43_wldev *dev, bool ghz5,
 void b2056_upload_inittabs(struct b43_wldev *dev,
                           bool ghz5, bool ignore_uploadflag)
 {
-       struct b2056_inittabs_pts *pts;
+       const struct b2056_inittabs_pts *pts;
 
        if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) {
                B43_WARN_ON(1);
@@ -9057,7 +9057,7 @@ void b2056_upload_inittabs(struct b43_wldev *dev,
 
 void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5)
 {
-       struct b2056_inittabs_pts *pts;
+       const struct b2056_inittabs_pts *pts;
        const struct b2056_inittab_entry *e;
 
        if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) {
index fb633094403aada9b15d1070b51e42ffdfe25b59..1e93926f388f3a65fa6ef412b1a9fb042869f874 100644 (file)
@@ -25,12 +25,12 @@ void b43_sdio_exit(void);
 #else /* CONFIG_B43_SDIO */
 
 
-int b43_sdio_request_irq(struct b43_wldev *dev,
+static inline int b43_sdio_request_irq(struct b43_wldev *dev,
                         void (*handler)(struct b43_wldev *dev))
 {
        return -ENODEV;
 }
-void b43_sdio_free_irq(struct b43_wldev *dev)
+static inline void b43_sdio_free_irq(struct b43_wldev *dev)
 {
 }
 static inline int b43_sdio_init(void)
index aaca60c6f57518a0adfc60b9f61ecbc6d074440a..110510d53958855049ff657f97c287a100d1e762 100644 (file)
@@ -2800,7 +2800,7 @@ static const struct nphy_rf_control_override_rev7
        { 0x0010, 0x344, 0x345, 0x0010, 4 },
 };
 
-struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_wa_phy6_radio11_ghz2 = {
+static struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_wa_phy6_radio11_ghz2 = {
        { 10, 14, 19, 27 },
        { -5, 6, 10, 15 },
        { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA },
@@ -2811,7 +2811,7 @@ struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_wa_phy6_radio11_ghz2 = {
        0x18, 0x18, 0x18,
        0x01D0, 0x5,
 };
-struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_workaround[2][4] = {
+static struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_workaround[2][4] = {
        { /* 2GHz */
                { /* PHY rev 3 */
                        { 7, 11, 16, 23 },
index 5176363cadf297f720d7ddca09293a8c96cb5f8e..e347b8d80ea49fca5f257a686f4af6206ff4bb3c 100644 (file)
@@ -313,7 +313,7 @@ static const u32 b43_lcntab_0x18[] = {
  * TX gain.
  **************************************************/
 
-const struct b43_lcntab_tx_gain_tbl_entry
+static const struct b43_lcntab_tx_gain_tbl_entry
        b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0[B43_LCNTAB_TX_GAIN_SIZE] = {
        { 0x03, 0x00, 0x1f, 0x0, 0x48 },
        { 0x03, 0x00, 0x1f, 0x0, 0x46 },
@@ -449,7 +449,7 @@ const struct b43_lcntab_tx_gain_tbl_entry
  * SW control.
  **************************************************/
 
-const u16 b43_lcntab_sw_ctl_4313_epa_rev0[] = {
+static const u16 b43_lcntab_sw_ctl_4313_epa_rev0[] = {
        0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
        0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
        0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
@@ -631,7 +631,7 @@ static void b43_phy_lcn_upload_static_tables(struct b43_wldev *dev)
        lcntab_upload(dev, B43_LCNTAB32(0x18, 0), b43_lcntab_0x18);
 }
 
-void b43_phy_lcn_load_tx_gain_tab(struct b43_wldev *dev,
+static void b43_phy_lcn_load_tx_gain_tab(struct b43_wldev *dev,
                        const struct b43_lcntab_tx_gain_tbl_entry *gain_table)
 {
        u32 i;
index 2d3c6644f82d9603ac5c18578a4d82c59ce5ac54..faeafe219c578fbb1a3694be2e2826e69cb92691 100644 (file)
@@ -334,13 +334,9 @@ static int alloc_ringmemory(struct b43legacy_dmaring *ring)
        ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev,
                                            B43legacy_DMA_RINGMEMSIZE,
                                            &(ring->dmabase),
-                                           GFP_KERNEL);
-       if (!ring->descbase) {
-               b43legacyerr(ring->dev->wl, "DMA ringmemory allocation"
-                            " failed\n");
+                                           GFP_KERNEL | __GFP_ZERO);
+       if (!ring->descbase)
                return -ENOMEM;
-       }
-       memset(ring->descbase, 0, B43legacy_DMA_RINGMEMSIZE);
 
        return 0;
 }
index 1d92d874ebb61e487a33a2a8827e864ef39f1a15..747e9317dabdfc0e195348a8696cd78f9ae14e75 100644 (file)
@@ -12,8 +12,9 @@ config BRCMSMAC
        select CORDIC
        ---help---
          This module adds support for PCIe wireless adapters based on Broadcom
-         IEEE802.11n SoftMAC chipsets.  If you choose to build a module, it'll
-         be called brcmsmac.ko.
+         IEEE802.11n SoftMAC chipsets. It also has WLAN led support, which will
+         be available if you select BCMA_DRIVER_GPIO. If you choose to build a
+         module, the driver will be called brcmsmac.ko.
 
 config BRCMFMAC
        tristate "Broadcom IEEE802.11n embedded FullMAC WLAN driver"
index 756e19fc279510de0a89a18a42913e255e5b7de6..598c8e2f8d2b571a3e87861a65c49b3c6e44fe99 100644 (file)
@@ -26,6 +26,7 @@ brcmfmac-objs += \
                wl_cfg80211.o \
                fwil.o \
                fweh.o \
+               fwsignal.o \
                p2p.o \
                dhd_cdc.o \
                dhd_common.o \
@@ -39,3 +40,5 @@ brcmfmac-$(CONFIG_BRCMFMAC_USB) += \
                usb.o
 brcmfmac-$(CONFIG_BRCMDBG) += \
                dhd_dbg.o
+brcmfmac-$(CONFIG_BRCM_TRACING) += \
+               tracepoint.o
index 11fd1c735589f31ab12789ec1067ad143c9a75ce..f3149debede02d9d382e454e0017a53db998915f 100644 (file)
@@ -65,7 +65,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
        u8 data;
        unsigned long flags;
 
-       brcmf_dbg(TRACE, "Entering: irq %d\n", sdiodev->irq);
+       brcmf_dbg(SDIO, "Entering: irq %d\n", sdiodev->irq);
 
        ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler,
                          sdiodev->irq_flags, "brcmf_oob_intr",
@@ -102,7 +102,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
 
 int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
 {
-       brcmf_dbg(TRACE, "Entering\n");
+       brcmf_dbg(SDIO, "Entering\n");
 
        sdio_claim_host(sdiodev->func[1]);
        brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
@@ -136,7 +136,7 @@ static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func)
 
 int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
 {
-       brcmf_dbg(TRACE, "Entering\n");
+       brcmf_dbg(SDIO, "Entering\n");
 
        sdio_claim_host(sdiodev->func[1]);
        sdio_claim_irq(sdiodev->func[1], brcmf_sdio_irqhandler);
@@ -148,7 +148,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
 
 int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
 {
-       brcmf_dbg(TRACE, "Entering\n");
+       brcmf_dbg(SDIO, "Entering\n");
 
        sdio_claim_host(sdiodev->func[1]);
        sdio_release_irq(sdiodev->func[2]);
@@ -253,9 +253,9 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
        u8 data;
        int retval;
 
-       brcmf_dbg(INFO, "addr:0x%08x\n", addr);
+       brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
        retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false);
-       brcmf_dbg(INFO, "data:0x%02x\n", data);
+       brcmf_dbg(SDIO, "data:0x%02x\n", data);
 
        if (ret)
                *ret = retval;
@@ -268,9 +268,9 @@ u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
        u32 data;
        int retval;
 
-       brcmf_dbg(INFO, "addr:0x%08x\n", addr);
+       brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
        retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false);
-       brcmf_dbg(INFO, "data:0x%08x\n", data);
+       brcmf_dbg(SDIO, "data:0x%08x\n", data);
 
        if (ret)
                *ret = retval;
@@ -283,7 +283,7 @@ void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
 {
        int retval;
 
-       brcmf_dbg(INFO, "addr:0x%08x, data:0x%02x\n", addr, data);
+       brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data);
        retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true);
 
        if (ret)
@@ -295,7 +295,7 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
 {
        int retval;
 
-       brcmf_dbg(INFO, "addr:0x%08x, data:0x%08x\n", addr, data);
+       brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data);
        retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true);
 
        if (ret)
@@ -358,7 +358,7 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        uint width;
        int err = 0;
 
-       brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n",
+       brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
 
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
@@ -381,7 +381,7 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        uint width;
        int err = 0;
 
-       brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n",
+       brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pktq->qlen);
 
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
@@ -428,7 +428,7 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
        int err = 0;
 
-       brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n",
+       brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
 
        /* Async not implemented yet */
@@ -492,13 +492,13 @@ int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr,
 int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
 {
        char t_func = (char)fn;
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        /* issue abort cmd52 command through F0 */
        brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0,
                                 SDIO_CCCR_ABORT, &t_func);
 
-       brcmf_dbg(TRACE, "Exit\n");
+       brcmf_dbg(SDIO, "Exit\n");
        return 0;
 }
 
index d92d373733d74a93b8d9fe6440792ebda5496d7d..716548989e4f837eb0174f3568e93284bd5d6387 100644 (file)
@@ -139,7 +139,7 @@ int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func,
 {
        int err_ret;
 
-       brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr);
+       brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr);
 
        brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait);
        if (brcmf_pm_resume_error(sdiodev))
@@ -179,7 +179,7 @@ int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
                return -EINVAL;
        }
 
-       brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+       brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
                  rw, func, addr, nbytes);
 
        brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
@@ -252,7 +252,7 @@ brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
 
        struct sk_buff *pkt;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        brcmf_pm_resume_wait(sdiodev, &sdiodev->request_chain_wait);
        if (brcmf_pm_resume_error(sdiodev))
@@ -270,7 +270,7 @@ brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
                                  write ? "TX" : "RX", pkt, SGCount, addr,
                                  pkt_len, err_ret);
                } else {
-                       brcmf_dbg(TRACE, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n",
+                       brcmf_dbg(SDIO, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n",
                                  write ? "TX" : "RX", pkt, SGCount, addr,
                                  pkt_len);
                }
@@ -280,7 +280,7 @@ brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
                SGCount++;
        }
 
-       brcmf_dbg(TRACE, "Exit\n");
+       brcmf_dbg(SDIO, "Exit\n");
        return err_ret;
 }
 
@@ -295,7 +295,7 @@ int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
        uint pkt_len;
        bool fifo = (fix_inc == SDIOH_DATA_FIX);
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        if (pkt == NULL)
                return -EINVAL;
@@ -314,7 +314,7 @@ int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
                brcmf_err("%s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
                          write ? "TX" : "RX", pkt, addr, pkt_len, status);
        } else {
-               brcmf_dbg(TRACE, "%s xfr'd %p, addr=0x%05x, len=%d\n",
+               brcmf_dbg(SDIO, "%s xfr'd %p, addr=0x%05x, len=%d\n",
                          write ? "TX" : "RX", pkt, addr, pkt_len);
        }
 
@@ -350,12 +350,12 @@ static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev)
        u32 fbraddr;
        u8 func;
 
-       brcmf_dbg(TRACE, "\n");
+       brcmf_dbg(SDIO, "\n");
 
        /* Get the Card's common CIS address */
        sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev,
                                                           SDIO_CCCR_CIS);
-       brcmf_dbg(INFO, "Card's Common CIS Ptr = 0x%x\n",
+       brcmf_dbg(SDIO, "Card's Common CIS Ptr = 0x%x\n",
                  sdiodev->func_cis_ptr[0]);
 
        /* Get the Card's function CIS (for each function) */
@@ -363,7 +363,7 @@ static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev)
             func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
                sdiodev->func_cis_ptr[func] =
                    brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr);
-               brcmf_dbg(INFO, "Function %d CIS Ptr = 0x%x\n",
+               brcmf_dbg(SDIO, "Function %d CIS Ptr = 0x%x\n",
                          func, sdiodev->func_cis_ptr[func]);
        }
 
@@ -382,7 +382,7 @@ int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev)
 {
        int err_ret = 0;
 
-       brcmf_dbg(TRACE, "\n");
+       brcmf_dbg(SDIO, "\n");
 
        sdiodev->num_funcs = 2;
 
@@ -404,13 +404,13 @@ int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev)
 
 out:
        sdio_release_host(sdiodev->func[1]);
-       brcmf_dbg(TRACE, "Done\n");
+       brcmf_dbg(SDIO, "Done\n");
        return err_ret;
 }
 
 void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev)
 {
-       brcmf_dbg(TRACE, "\n");
+       brcmf_dbg(SDIO, "\n");
 
        /* Disable Function 2 */
        sdio_claim_host(sdiodev->func[2]);
@@ -458,11 +458,11 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
        struct brcmf_sdio_dev *sdiodev;
        struct brcmf_bus *bus_if;
 
-       brcmf_dbg(TRACE, "Enter\n");
-       brcmf_dbg(TRACE, "Class=%x\n", func->class);
-       brcmf_dbg(TRACE, "sdio vendor ID: 0x%04x\n", func->vendor);
-       brcmf_dbg(TRACE, "sdio device ID: 0x%04x\n", func->device);
-       brcmf_dbg(TRACE, "Function#: %d\n", func->num);
+       brcmf_dbg(SDIO, "Enter\n");
+       brcmf_dbg(SDIO, "Class=%x\n", func->class);
+       brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
+       brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
+       brcmf_dbg(SDIO, "Function#: %d\n", func->num);
 
        /* Consume func num 1 but dont do anything with it. */
        if (func->num == 1)
@@ -501,13 +501,13 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
        if (err)
                goto fail;
 
-       brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_probe...\n");
+       brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n");
        err = brcmf_sdio_probe(sdiodev);
        if (err) {
                brcmf_err("F2 error, probe failed %d...\n", err);
                goto fail;
        }
-       brcmf_dbg(TRACE, "F2 init completed...\n");
+       brcmf_dbg(SDIO, "F2 init completed...\n");
        return 0;
 
 fail:
@@ -523,10 +523,10 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func)
        struct brcmf_bus *bus_if;
        struct brcmf_sdio_dev *sdiodev;
 
-       brcmf_dbg(TRACE, "Enter\n");
-       brcmf_dbg(TRACE, "sdio vendor ID: 0x%04x\n", func->vendor);
-       brcmf_dbg(TRACE, "sdio device ID: 0x%04x\n", func->device);
-       brcmf_dbg(TRACE, "Function: %d\n", func->num);
+       brcmf_dbg(SDIO, "Enter\n");
+       brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
+       brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
+       brcmf_dbg(SDIO, "Function: %d\n", func->num);
 
        if (func->num != 1 && func->num != 2)
                return;
@@ -543,7 +543,7 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func)
                kfree(sdiodev);
        }
 
-       brcmf_dbg(TRACE, "Exit\n");
+       brcmf_dbg(SDIO, "Exit\n");
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -554,7 +554,7 @@ static int brcmf_sdio_suspend(struct device *dev)
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
        int ret = 0;
 
-       brcmf_dbg(TRACE, "\n");
+       brcmf_dbg(SDIO, "\n");
 
        atomic_set(&sdiodev->suspend, true);
 
@@ -645,7 +645,7 @@ static struct platform_driver brcmf_sdio_pd = {
 
 void brcmf_sdio_exit(void)
 {
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        sdio_unregister_driver(&brcmf_sdmmc_driver);
 
@@ -656,7 +656,7 @@ void brcmf_sdio_init(void)
 {
        int ret;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        ret = platform_driver_register(&brcmf_sdio_pd);
 
@@ -666,7 +666,7 @@ void brcmf_sdio_init(void)
 #else
 void brcmf_sdio_exit(void)
 {
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        sdio_unregister_driver(&brcmf_sdmmc_driver);
 }
@@ -675,7 +675,7 @@ void brcmf_sdio_init(void)
 {
        int ret;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        ret = sdio_register_driver(&brcmf_sdmmc_driver);
 
index ef6f23be6d32068c551050bf8d87d6e11ccdbb08..5249c67b466ceabf3488f6e442da8ffd7a61f93d 100644 (file)
@@ -72,6 +72,7 @@
 #define BRCMF_C_SET_WSEC                       134
 #define BRCMF_C_GET_PHY_NOISE                  135
 #define BRCMF_C_GET_BSS_INFO                   136
+#define BRCMF_C_GET_BANDLIST                   140
 #define BRCMF_C_SET_SCB_TIMEOUT                        158
 #define BRCMF_C_GET_PHYLIST                    180
 #define BRCMF_C_SET_SCAN_CHANNEL_TIME          185
@@ -475,6 +476,11 @@ struct brcmf_sta_info_le {
        __le32  rx_decrypt_failures;    /* # of packet decrypted failed */
 };
 
+struct brcmf_chanspec_list {
+       __le32  count;          /* # of entries */
+       __le32  element[1];     /* variable length uint32 list */
+};
+
 /*
  * WLC_E_PROBRESP_MSG
  * WLC_E_P2P_PROBREQ_MSG
@@ -501,6 +507,7 @@ struct brcmf_dcmd {
 /* 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 */
 
 /* Common structure for module and instance linkage */
 struct brcmf_pub {
@@ -527,6 +534,10 @@ struct brcmf_pub {
        unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
 
        struct brcmf_fweh_info fweh;
+
+       bool fw_signals;
+       struct brcmf_fws_info *fws;
+       spinlock_t fws_spinlock;
 #ifdef DEBUG
        struct dentry *dbgfs_dir;
 #endif
@@ -537,10 +548,25 @@ struct brcmf_if_event {
        u8 action;
        u8 flags;
        u8 bssidx;
+       u8 role;
 };
 
-/* forward declaration */
+/* 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_BLOCK_BUS:
+ *     netif stopped due to bus blocking.
+ */
+enum brcmf_netif_stop_reason {
+       BRCMF_NETIF_STOP_REASON_FWS_FC = 1,
+       BRCMF_NETIF_STOP_REASON_BLOCK_BUS = 2
+};
 
 /**
  * struct brcmf_if - interface control information.
@@ -549,9 +575,13 @@ struct brcmf_cfg80211_vif;
  * @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.
  * @pend_8021x_cnt: tracks outstanding number of 802.1x frames.
  * @pend_8021x_wait: used for signalling change in count.
  */
@@ -562,9 +592,11 @@ struct brcmf_if {
        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;
        atomic_t pend_8021x_cnt;
        wait_queue_head_t pend_8021x_wait;
 };
@@ -582,13 +614,17 @@ extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
                                    void *buf, uint len);
 
 /* Remove any protocol-specific data header. */
-extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
+extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
                               struct sk_buff *rxp);
 
 extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
 extern struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx,
                                     s32 ifidx, char *name, u8 *mac_addr);
 extern void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
+void brcmf_txflowblock_if(struct brcmf_if *ifp,
+                         enum brcmf_netif_stop_reason reason, bool state);
 extern u32 brcmf_get_chip_info(struct brcmf_if *ifp);
+extern void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
+                            bool success);
 
 #endif                         /* _BRCMF_H_ */
index ad25c3408b59abaf2315e89352fcf2fce7bc6f6a..080395f49fa5922400419d821e1fda4fcc1b460e 100644 (file)
@@ -39,10 +39,12 @@ struct brcmf_bus_dcmd {
  * @txdata: send a data frame to the dongle (callee disposes skb).
  * @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).
  *
  * 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.
+ * will assure there is only one active transaction. Unless
+ * indicated otherwise these callbacks are mandatory.
  */
 struct brcmf_bus_ops {
        int (*init)(struct device *dev);
@@ -50,6 +52,7 @@ struct brcmf_bus_ops {
        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);
 };
 
 /**
@@ -115,6 +118,14 @@ 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);
+}
 /*
  * interface functions from common layer
  */
@@ -134,7 +145,7 @@ extern void brcmf_dev_reset(struct device *dev);
 /* Indication from bus module to change flow-control state */
 extern void brcmf_txflowblock(struct device *dev, bool state);
 
-/* Notify tx completion */
+/* Notify the bus has transferred the tx packet to firmware */
 extern void brcmf_txcomplete(struct device *dev, struct sk_buff *txp,
                             bool success);
 
index a2354d951dd7b5ae823a25b1dcd046afa9c617f8..59c77aa3b9597894385a9b8c4c8d72aa5f6db84f 100644 (file)
@@ -28,6 +28,7 @@
 #include "dhd.h"
 #include "dhd_proto.h"
 #include "dhd_bus.h"
+#include "fwsignal.h"
 #include "dhd_dbg.h"
 
 struct brcmf_proto_cdc_dcmd {
@@ -71,13 +72,26 @@ struct brcmf_proto_cdc_dcmd {
        ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \
        ((idx) << BDC_FLAG2_IF_SHIFT)))
 
+/**
+ * struct brcmf_proto_bdc_header - BDC header format
+ *
+ * @flags: flags contain protocol and checksum info.
+ * @priority: 802.1d priority and USB flow control info (bit 4:7).
+ * @flags2: additional flags containing dongle interface index.
+ * @data_offset: start of packet data. header is following by firmware signals.
+ */
 struct brcmf_proto_bdc_header {
        u8 flags;
-       u8 priority;    /* 802.1d Priority, 4:7 flow control info for usb */
+       u8 priority;
        u8 flags2;
        u8 data_offset;
 };
 
+/*
+ * maximum length of firmware signal data between
+ * the BDC header and packet data in the tx path.
+ */
+#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES       12
 
 #define RETRIES 2 /* # of retries to retrieve matching dcmd response */
 #define BUS_HEADER_LEN (16+64)         /* Must be atleast SDPCM_RESERVE
@@ -258,7 +272,7 @@ static void pkt_set_sum_good(struct sk_buff *skb, bool x)
        skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE);
 }
 
-void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
+void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
                         struct sk_buff *pktbuf)
 {
        struct brcmf_proto_bdc_header *h;
@@ -266,7 +280,6 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
        brcmf_dbg(CDC, "Enter\n");
 
        /* Push BDC header used to convey priority for buses that don't */
-
        skb_push(pktbuf, BDC_HEADER_LEN);
 
        h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
@@ -277,11 +290,11 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
 
        h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
        h->flags2 = 0;
-       h->data_offset = 0;
+       h->data_offset = offset;
        BDC_SET_IF_IDX(h, ifidx);
 }
 
-int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
+int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
                        struct sk_buff *pktbuf)
 {
        struct brcmf_proto_bdc_header *h;
@@ -290,8 +303,8 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
 
        /* Pop BDC header used to convey priority for buses that don't */
 
-       if (pktbuf->len < BDC_HEADER_LEN) {
-               brcmf_err("rx data too short (%d < %d)\n",
+       if (pktbuf->len <= BDC_HEADER_LEN) {
+               brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
                          pktbuf->len, BDC_HEADER_LEN);
                return -EBADE;
        }
@@ -328,7 +341,10 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
        pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
 
        skb_pull(pktbuf, BDC_HEADER_LEN);
-       skb_pull(pktbuf, h->data_offset << 2);
+       if (do_fws)
+               brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
+       else
+               skb_pull(pktbuf, h->data_offset << 2);
 
        if (pktbuf->len == 0)
                return -ENODATA;
@@ -350,7 +366,7 @@ int brcmf_proto_attach(struct brcmf_pub *drvr)
        }
 
        drvr->prot = cdc;
-       drvr->hdrlen += BDC_HEADER_LEN;
+       drvr->hdrlen += BDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
        drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
                        sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN;
        return 0;
index 4544342a04281d84e67fd93222a50ce3ac2fdc7c..be0787cab24f4ffc981d7831f1cafcaa15b3d33f 100644 (file)
@@ -24,6 +24,7 @@
 #include "dhd_proto.h"
 #include "dhd_dbg.h"
 #include "fwil.h"
+#include "tracepoint.h"
 
 #define PKTFILTER_BUF_SIZE             128
 #define BRCMF_ARPOL_MODE               0xb     /* agent|snoop|peer_autoreply */
@@ -373,3 +374,35 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
 done:
        return err;
 }
+
+#ifdef CONFIG_BRCM_TRACING
+void __brcmf_err(const char *func, const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       pr_err("%s: %pV", func, &vaf);
+       trace_brcmf_err(func, &vaf);
+       va_end(args);
+}
+#endif
+#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
index 57671eddf79dda2a204ed0da5ece8524d91733ba..202869cd0932619a54c6f6914ead97b24b8907bf 100644 (file)
@@ -22,6 +22,7 @@
 #include "dhd.h"
 #include "dhd_bus.h"
 #include "dhd_dbg.h"
+#include "tracepoint.h"
 
 static struct dentry *root_folder;
 
@@ -123,3 +124,82 @@ void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
                debugfs_create_file("counters", S_IRUGO, dentry,
                                    sdcnt, &brcmf_debugfs_sdio_counter_ops);
 }
+
+static
+ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
+                                    size_t count, loff_t *ppos)
+{
+       struct brcmf_fws_stats *fwstats = f->private_data;
+       char buf[650];
+       int res;
+
+       /* only allow read from start */
+       if (*ppos > 0)
+               return 0;
+
+       res = scnprintf(buf, sizeof(buf),
+                       "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"
+                       "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
+                       "fifo_credits_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+                       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->send_pkts[0], fwstats->send_pkts[1],
+                       fwstats->send_pkts[2], fwstats->send_pkts[3],
+                       fwstats->send_pkts[4],
+                       fwstats->fifo_credits_sent[0],
+                       fwstats->fifo_credits_sent[1],
+                       fwstats->fifo_credits_sent[2],
+                       fwstats->fifo_credits_sent[3],
+                       fwstats->fifo_credits_sent[4]);
+
+       return simple_read_from_buffer(data, count, ppos, buf, res);
+}
+
+static const struct file_operations brcmf_debugfs_fws_stats_ops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = brcmf_debugfs_fws_stats_read
+};
+
+void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
+                                   struct brcmf_fws_stats *stats)
+{
+       struct dentry *dentry =  drvr->dbgfs_dir;
+
+       if (!IS_ERR_OR_NULL(dentry))
+               debugfs_create_file("fws_stats", S_IRUGO, dentry,
+                                   stats, &brcmf_debugfs_fws_stats_ops);
+}
index bc013cbe06f611647829694b97277423e43a7d5e..009c87bfd9ae45e292af73082e6eccba3e1e3b1e 100644 (file)
@@ -34,6 +34,7 @@
 #define BRCMF_SCAN_VAL 0x00004000
 #define BRCMF_CONN_VAL 0x00008000
 #define BRCMF_CDC_VAL  0x00010000
+#define BRCMF_SDIO_VAL 0x00020000
 
 /* set default print format */
 #undef pr_fmt
@@ -43,6 +44,7 @@
  * 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
                        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)
-
+#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 {                                                           \
-       if (brcmf_msg_level & BRCMF_##level##_VAL)              \
-               pr_debug("%s: " fmt, __func__, ##__VA_ARGS__);  \
+       __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)
@@ -69,7 +77,7 @@ do {                                                          \
 #define BRCMF_EVENT_ON()       (brcmf_msg_level & BRCMF_EVENT_VAL)
 #define BRCMF_FIL_ON()         (brcmf_msg_level & BRCMF_FIL_VAL)
 
-#else  /* (defined DEBUG) || (defined DEBUG) */
+#else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
 
 #define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
 
@@ -81,10 +89,11 @@ do {                                                                \
 #define BRCMF_EVENT_ON()       0
 #define BRCMF_FIL_ON()         0
 
-#endif                         /* defined(DEBUG) */
+#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)
@@ -125,6 +134,32 @@ struct brcmf_sdio_count {
        ulong rx_readahead_cnt; /* packets where header read-ahead was used */
 };
 
+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 fifo_credits_sent[5];
+       u32 fifo_credits_back[6];
+       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;
+};
+
 struct brcmf_pub;
 #ifdef DEBUG
 void brcmf_debugfs_init(void);
@@ -134,6 +169,8 @@ void brcmf_debugfs_detach(struct brcmf_pub *drvr);
 struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
 void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
                                     struct brcmf_sdio_count *sdcnt);
+void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
+                                   struct brcmf_fws_stats *stats);
 #else
 static inline void brcmf_debugfs_init(void)
 {
@@ -148,6 +185,10 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
 static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
 {
 }
+static inline void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
+                                                 struct brcmf_fws_stats *stats)
+{
+}
 #endif
 
 #endif                         /* _BRCMF_DBG_H_ */
index c06cea88df0dbcaf55b7b9078810c71e0c5f8851..763a84eba2164ca73a07923059f39c067a8717da 100644 (file)
 #include "p2p.h"
 #include "wl_cfg80211.h"
 #include "fwil.h"
+#include "fwsignal.h"
 
 MODULE_AUTHOR("Broadcom Corporation");
 MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
-MODULE_SUPPORTED_DEVICE("Broadcom 802.11 WLAN fullmac cards");
 MODULE_LICENSE("Dual BSD/GPL");
 
 #define MAX_WAIT_FOR_8021X_TX          50      /* msecs */
 
 /* Error bits */
 int brcmf_msg_level;
-module_param(brcmf_msg_level, int, 0);
+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;
@@ -222,18 +223,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
                goto done;
        }
 
-       /* handle ethernet header */
-       eh = (struct ethhdr *)(skb->data);
-       if (is_multicast_ether_addr(eh->h_dest))
-               drvr->tx_multicast++;
-       if (ntohs(eh->h_proto) == ETH_P_PAE)
-               atomic_inc(&ifp->pend_8021x_cnt);
-
-       /* If the protocol uses a data header, apply it */
-       brcmf_proto_hdrpush(drvr, ifp->ifidx, skb);
-
-       /* Use bus module to send data frame */
-       ret =  brcmf_bus_txdata(drvr->bus_if, skb);
+       ret = brcmf_fws_process_skb(ifp, skb);
 
 done:
        if (ret) {
@@ -247,9 +237,27 @@ done:
        return NETDEV_TX_OK;
 }
 
+void brcmf_txflowblock_if(struct brcmf_if *ifp,
+                         enum brcmf_netif_stop_reason reason, bool state)
+{
+       if (!ifp)
+               return;
+
+       brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n",
+                 ifp->bssidx, ifp->netif_stop, reason, state);
+       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);
+       }
+}
+
 void brcmf_txflowblock(struct device *dev, bool state)
 {
-       struct net_device *ndev;
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
        int i;
@@ -257,13 +265,8 @@ void brcmf_txflowblock(struct device *dev, bool state)
        brcmf_dbg(TRACE, "Enter\n");
 
        for (i = 0; i < BRCMF_MAX_IFS; i++)
-               if (drvr->iflist[i]) {
-                       ndev = drvr->iflist[i]->ndev;
-                       if (state)
-                               netif_stop_queue(ndev);
-                       else
-                               netif_wake_queue(ndev);
-               }
+               brcmf_txflowblock_if(drvr->iflist[i],
+                                    BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state);
 }
 
 void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
@@ -283,7 +286,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
                skb_unlink(skb, skb_list);
 
                /* process and remove protocol-specific header */
-               ret = brcmf_proto_hdrpull(drvr, &ifidx, skb);
+               ret = brcmf_proto_hdrpull(drvr, drvr->fw_signals, &ifidx, skb);
                ifp = drvr->iflist[ifidx];
 
                if (ret || !ifp || !ifp->ndev) {
@@ -320,13 +323,8 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
                /* Strip header, count, deliver upward */
                skb_pull(skb, ETH_HLEN);
 
-               /* Process special event packets and then discard them */
-               brcmf_fweh_process_skb(drvr, skb, &ifidx);
-
-               if (drvr->iflist[ifidx]) {
-                       ifp = drvr->iflist[ifidx];
-                       ifp->ndev->last_rx = jiffies;
-               }
+               /* Process special event packets */
+               brcmf_fweh_process_skb(drvr, skb);
 
                if (!(ifp->ndev->flags & IFF_UP)) {
                        brcmu_pkt_buf_free_skb(skb);
@@ -349,31 +347,49 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
        }
 }
 
-void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
+void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
+                     bool success)
 {
-       u8 ifidx;
+       struct brcmf_if *ifp;
        struct ethhdr *eh;
+       u8 ifidx;
        u16 type;
-       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
-       struct brcmf_pub *drvr = bus_if->drvr;
-       struct brcmf_if *ifp;
+       int res;
 
-       brcmf_proto_hdrpull(drvr, &ifidx, txp);
+       res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp);
 
        ifp = drvr->iflist[ifidx];
        if (!ifp)
-               return;
+               goto done;
 
-       eh = (struct ethhdr *)(txp->data);
-       type = ntohs(eh->h_proto);
+       if (res == 0) {
+               eh = (struct ethhdr *)(txp->data);
+               type = ntohs(eh->h_proto);
 
-       if (type == ETH_P_PAE) {
-               atomic_dec(&ifp->pend_8021x_cnt);
-               if (waitqueue_active(&ifp->pend_8021x_wait))
-                       wake_up(&ifp->pend_8021x_wait);
+               if (type == ETH_P_PAE) {
+                       atomic_dec(&ifp->pend_8021x_cnt);
+                       if (waitqueue_active(&ifp->pend_8021x_wait))
+                               wake_up(&ifp->pend_8021x_wait);
+               }
        }
        if (!success)
                ifp->stats.tx_errors++;
+done:
+       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;
+
+       /* await txstatus signal for firmware if active */
+       if (brcmf_fws_fc_active(drvr->fws)) {
+               if (!success)
+                       brcmf_fws_bustxfail(drvr->fws, txp);
+       } else {
+               brcmf_txfinalize(drvr, txp, success);
+       }
 }
 
 static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev)
@@ -734,28 +750,35 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
                }
        }
 
-       /* Allocate netdev, including space for private structure */
-       ndev = alloc_netdev(sizeof(struct brcmf_if), name, ether_setup);
-       if (!ndev) {
-               brcmf_err("OOM - alloc_netdev\n");
-               return ERR_PTR(-ENOMEM);
+       if (!brcmf_p2p_enable && bssidx == 1) {
+               /* this is P2P_DEVICE interface */
+               brcmf_dbg(INFO, "allocate non-netdev interface\n");
+               ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
+       } else {
+               brcmf_dbg(INFO, "allocate netdev interface\n");
+               /* Allocate netdev, including space for private structure */
+               ndev = alloc_netdev(sizeof(*ifp), name, ether_setup);
+               if (!ndev) {
+                       brcmf_err("OOM - alloc_netdev\n");
+                       return ERR_PTR(-ENOMEM);
+               }
+
+               ifp = netdev_priv(ndev);
+               ifp->ndev = ndev;
        }
 
-       ifp = netdev_priv(ndev);
-       ifp->ndev = ndev;
        ifp->drvr = drvr;
        drvr->iflist[bssidx] = ifp;
        ifp->ifidx = ifidx;
        ifp->bssidx = bssidx;
 
-
        init_waitqueue_head(&ifp->pend_8021x_wait);
 
        if (mac_addr != NULL)
                memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
 
        brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n",
-                 current->pid, ifp->ndev->name, ifp->mac_addr);
+                 current->pid, name, ifp->mac_addr);
 
        return ifp;
 }
@@ -787,11 +810,13 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
                }
 
                unregister_netdev(ifp->ndev);
-               drvr->iflist[bssidx] = NULL;
                if (bssidx == 0)
                        brcmf_cfg80211_detach(drvr->config);
                free_netdev(ifp->ndev);
+       } else {
+               kfree(ifp);
        }
+       drvr->iflist[bssidx] = NULL;
 }
 
 int brcmf_attach(uint bus_hdrlen, struct device *dev)
@@ -873,6 +898,10 @@ int brcmf_bus_start(struct device *dev)
        if (ret < 0)
                goto fail;
 
+       drvr->fw_signals = true;
+       (void)brcmf_fws_init(drvr);
+       brcmf_fws_add_interface(ifp);
+
        drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev);
        if (drvr->config == NULL) {
                ret = -ENOMEM;
@@ -889,6 +918,10 @@ fail:
                brcmf_err("failed: %d\n", ret);
                if (drvr->config)
                        brcmf_cfg80211_detach(drvr->config);
+               if (drvr->fws) {
+                       brcmf_fws_del_interface(ifp);
+                       brcmf_fws_deinit(drvr);
+               }
                free_netdev(ifp->ndev);
                drvr->iflist[0] = NULL;
                if (p2p_ifp) {
@@ -944,14 +977,18 @@ void brcmf_detach(struct device *dev)
 
        /* make sure primary interface removed last */
        for (i = BRCMF_MAX_IFS-1; i > -1; i--)
-               if (drvr->iflist[i])
+               if (drvr->iflist[i]) {
+                       brcmf_fws_del_interface(drvr->iflist[i]);
                        brcmf_del_if(drvr, i);
+               }
 
        brcmf_bus_detach(drvr);
 
        if (drvr->prot)
                brcmf_proto_detach(drvr);
 
+       brcmf_fws_deinit(drvr);
+
        brcmf_debugfs_detach(drvr);
        bus_if->drvr = NULL;
        kfree(drvr);
index 48fa70302192d3f7e6c18f001f00b22dc176b009..ef9179883748f0aa9637900a6e7bab1b476cd937 100644 (file)
@@ -33,7 +33,7 @@ extern void brcmf_proto_stop(struct brcmf_pub *drvr);
 /* Add any protocol-specific data header.
  * Caller must reserve prot_hdrlen prepend space.
  */
-extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx,
+extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx, u8 offset,
                                struct sk_buff *txp);
 
 /* Sets dongle media info (drv_version, mac address). */
index 35fc68be158d5b548e5c273ee85f7e3bf057a162..4ff2d3c52ee60bb2227b4f1dfdd322e05219b135 100644 (file)
@@ -94,6 +94,7 @@ struct rte_console {
 
 #include "dhd_bus.h"
 #include "dhd_dbg.h"
+#include "tracepoint.h"
 
 #define TXQLEN         2048    /* bulk tx queue length */
 #define TXHI           (TXQLEN - 256)  /* turn on flow control above TXHI */
@@ -675,7 +676,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
        u8 clkctl, clkreq, devctl;
        unsigned long timeout;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        clkctl = 0;
 
@@ -713,7 +714,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
                        devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
                        brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
                                         devctl, &err);
-                       brcmf_dbg(INFO, "CLKCTL: set PENDING\n");
+                       brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");
                        bus->clkstate = CLK_PENDING;
 
                        return 0;
@@ -750,7 +751,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
 
                /* Mark clock available */
                bus->clkstate = CLK_AVAIL;
-               brcmf_dbg(INFO, "CLKCTL: turned ON\n");
+               brcmf_dbg(SDIO, "CLKCTL: turned ON\n");
 
 #if defined(DEBUG)
                if (!bus->alp_only) {
@@ -775,7 +776,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
                bus->clkstate = CLK_SDONLY;
                brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
                                 clkreq, &err);
-               brcmf_dbg(INFO, "CLKCTL: turned OFF\n");
+               brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");
                if (err) {
                        brcmf_err("Failed access turning clock off: %d\n",
                                  err);
@@ -788,7 +789,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
 /* Change idle/active SD state */
 static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on)
 {
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        if (on)
                bus->clkstate = CLK_SDONLY;
@@ -805,7 +806,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
        uint oldstate = bus->clkstate;
 #endif                         /* DEBUG */
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        /* Early exit if we're already there */
        if (bus->clkstate == target) {
@@ -849,7 +850,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
                break;
        }
 #ifdef DEBUG
-       brcmf_dbg(INFO, "%d -> %d\n", oldstate, bus->clkstate);
+       brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate);
 #endif                         /* DEBUG */
 
        return 0;
@@ -862,7 +863,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
        u8 fcbits;
        int ret;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(SDIO, "Enter\n");
 
        /* Read mailbox data and ack that we did so */
        ret = r_sdreg32(bus, &hmb_data,
@@ -875,7 +876,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
 
        /* Dongle recomposed rx frames, accept them again */
        if (hmb_data & HMB_DATA_NAKHANDLED) {
-               brcmf_dbg(INFO, "Dongle reports NAK handled, expect rtx of %d\n",
+               brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n",
                          bus->rx_seq);
                if (!bus->rxskip)
                        brcmf_err("unexpected NAKHANDLED!\n");
@@ -896,7 +897,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
                                  "expecting %d\n",
                                  bus->sdpcm_ver, SDPCM_PROT_VERSION);
                else
-                       brcmf_dbg(INFO, "Dongle ready, protocol version %d\n",
+                       brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",
                                  bus->sdpcm_ver);
        }
 
@@ -970,7 +971,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
        if (!retries)
                brcmf_err("count never zeroed: last 0x%04x\n", lastrbc);
        else
-               brcmf_dbg(INFO, "flush took %d iterations\n", 0xffff - retries);
+               brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries);
 
        if (rtx) {
                bus->sdcnt.rxrtx++;
@@ -1173,7 +1174,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
        /* If packets, issue read(s) and send up packet chain */
        /* Return sequence numbers consumed? */
 
-       brcmf_dbg(TRACE, "start: glomd %p glom %p\n",
+       brcmf_dbg(SDIO, "start: glomd %p glom %p\n",
                  bus->glomd, skb_peek(&bus->glom));
 
        /* If there's a descriptor, generate the packet chain */
@@ -1546,7 +1547,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
        struct sk_buff_head pktlist;    /* needed for bus interface */
        u16 pad;                /* Number of pad bytes to read */
        uint rxleft = 0;        /* Remaining number of frames allowed */
-       int sdret;              /* Return code from calls */
+       int ret;                /* Return code from calls */
        uint rxcount = 0;       /* Total frames read */
        struct brcmf_sdio_read *rd = &bus->cur_read, rd_new;
        u8 head_read = 0;
@@ -1577,15 +1578,15 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                /* read header first for unknow frame length */
                sdio_claim_host(bus->sdiodev->func[1]);
                if (!rd->len) {
-                       sdret = brcmf_sdcard_recv_buf(bus->sdiodev,
+                       ret = brcmf_sdcard_recv_buf(bus->sdiodev,
                                                      bus->sdiodev->sbwad,
                                                      SDIO_FUNC_2, F2SYNC,
                                                      bus->rxhdr,
                                                      BRCMF_FIRSTREAD);
                        bus->sdcnt.f2rxhdrs++;
-                       if (sdret < 0) {
+                       if (ret < 0) {
                                brcmf_err("RXHEADER FAILED: %d\n",
-                                         sdret);
+                                         ret);
                                bus->sdcnt.rx_hdrfail++;
                                brcmf_sdbrcm_rxfail(bus, true, true);
                                sdio_release_host(bus->sdiodev->func[1]);
@@ -1637,14 +1638,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                skb_pull(pkt, head_read);
                pkt_align(pkt, rd->len_left, BRCMF_SDALIGN);
 
-               sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
+               ret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
                                              SDIO_FUNC_2, F2SYNC, pkt);
                bus->sdcnt.f2rxdata++;
                sdio_release_host(bus->sdiodev->func[1]);
 
-               if (sdret < 0) {
+               if (ret < 0) {
                        brcmf_err("read %d bytes from channel %d failed: %d\n",
-                                 rd->len, rd->channel, sdret);
+                                 rd->len, rd->channel, ret);
                        brcmu_pkt_buf_free_skb(pkt);
                        sdio_claim_host(bus->sdiodev->func[1]);
                        brcmf_sdbrcm_rxfail(bus, true,
@@ -1775,13 +1776,12 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus)
 /* Writes a HW/SW header into the packet and sends it. */
 /* Assumes: (a) header space already there, (b) caller holds lock */
 static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
-                             uint chan, bool free_pkt)
+                             uint chan)
 {
        int ret;
        u8 *frame;
        u16 len, pad = 0;
        u32 swheader;
-       struct sk_buff *new;
        int i;
 
        brcmf_dbg(TRACE, "Enter\n");
@@ -1795,30 +1795,14 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
                        brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n",
                                  skb_headroom(pkt), pad);
                        bus->sdiodev->bus_if->tx_realloc++;
-                       new = brcmu_pkt_buf_get_skb(pkt->len + BRCMF_SDALIGN);
-                       if (!new) {
-                               brcmf_err("couldn't allocate new %d-byte packet\n",
-                                         pkt->len + BRCMF_SDALIGN);
-                               ret = -ENOMEM;
+                       ret = skb_cow(pkt, BRCMF_SDALIGN);
+                       if (ret)
                                goto done;
-                       }
-
-                       pkt_align(new, pkt->len, BRCMF_SDALIGN);
-                       memcpy(new->data, pkt->data, pkt->len);
-                       if (free_pkt)
-                               brcmu_pkt_buf_free_skb(pkt);
-                       /* free the pkt if canned one is not used */
-                       free_pkt = true;
-                       pkt = new;
-                       frame = (u8 *) (pkt->data);
-                       /* precondition: (frame % BRCMF_SDALIGN) == 0) */
-                       pad = 0;
-               } else {
-                       skb_push(pkt, pad);
-                       frame = (u8 *) (pkt->data);
-                       /* precondition: pad + SDPCM_HDRLEN <= pkt->len */
-                       memset(frame, 0, pad + SDPCM_HDRLEN);
+                       pad = ((unsigned long)frame % BRCMF_SDALIGN);
                }
+               skb_push(pkt, pad);
+               frame = (u8 *) (pkt->data);
+               memset(frame, 0, pad + SDPCM_HDRLEN);
        }
        /* precondition: pad < BRCMF_SDALIGN */
 
@@ -1833,8 +1817,8 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
            (((pad +
               SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
 
-       put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
-       put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+       *(((__le32 *) frame) + 1) = cpu_to_le32(swheader);
+       *(((__le32 *) frame) + 2) = 0;
 
 #ifdef DEBUG
        tx_packets[pkt->priority]++;
@@ -1900,11 +1884,7 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
 done:
        /* restore pkt buffer pointer before calling tx complete routine */
        skb_pull(pkt, SDPCM_HDRLEN + pad);
-       brcmf_txcomplete(bus->sdiodev->dev, pkt, ret != 0);
-
-       if (free_pkt)
-               brcmu_pkt_buf_free_skb(pkt);
-
+       brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0);
        return ret;
 }
 
@@ -1932,7 +1912,7 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
                spin_unlock_bh(&bus->txqlock);
                datalen = pkt->len - SDPCM_HDRLEN;
 
-               ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
+               ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL);
 
                /* In poll mode, need to check for other events */
                if (!bus->intr && cnt) {
@@ -2138,7 +2118,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                        bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
                }
 
-               brcmf_dbg(INFO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
+               brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
                          devctl, clkctl);
 
                if (SBSDIO_HTAV(clkctl)) {
@@ -2314,6 +2294,15 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
        }
 }
 
+static struct pktq *brcmf_sdbrcm_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 int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
 {
        int ret = -EBADE;
@@ -2343,7 +2332,6 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
        if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
                skb_pull(pkt, SDPCM_HDRLEN);
                brcmf_txcomplete(bus->sdiodev->dev, pkt, false);
-               brcmu_pkt_buf_free_skb(pkt);
                brcmf_err("out of bus->txq !!!\n");
                ret = -ENOSR;
        } else {
@@ -2400,7 +2388,7 @@ brcmf_sdbrcm_membytes(struct brcmf_sdio *bus, bool write, u32 address, u8 *data,
 
        /* Do the transfer(s) */
        while (size) {
-               brcmf_dbg(INFO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
+               brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
                          write ? "write" : "read", dsize,
                          sdaddr, address & SBSDIO_SBWINDOW_MASK);
                bcmerror = brcmf_sdcard_rwdata(bus->sdiodev, write,
@@ -2633,10 +2621,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
                                                 msecs_to_jiffies(2000));
 
                if (!bus->ctrl_frame_stat) {
-                       brcmf_dbg(INFO, "ctrl_frame_stat == false\n");
+                       brcmf_dbg(SDIO, "ctrl_frame_stat == false\n");
                        ret = 0;
                } else {
-                       brcmf_dbg(INFO, "ctrl_frame_stat == true\n");
+                       brcmf_dbg(SDIO, "ctrl_frame_stat == true\n");
                        ret = -1;
                }
        }
@@ -2707,7 +2695,7 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
 
        addr = le32_to_cpu(addr_le);
 
-       brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);
+       brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr);
 
        /*
         * Check if addr is valid.
@@ -2734,8 +2722,8 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
        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 mismatch: dhd %d dongle %d\n",
+       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;
@@ -2817,21 +2805,18 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
        int error, res;
        char buf[350];
        struct brcmf_trap_info tr;
-       int nbytes;
        loff_t pos = 0;
 
-       if ((sh->flags & SDPCM_SHARED_TRAP) == 0)
+       if ((sh->flags & SDPCM_SHARED_TRAP) == 0) {
+               brcmf_dbg(INFO, "no trap in firmware\n");
                return 0;
+       }
 
        error = brcmf_sdbrcm_membytes(bus, false, sh->trap_addr, (u8 *)&tr,
                                      sizeof(struct brcmf_trap_info));
        if (error < 0)
                return error;
 
-       nbytes = brcmf_sdio_dump_console(bus, sh, data, count);
-       if (nbytes < 0)
-               return nbytes;
-
        res = scnprintf(buf, sizeof(buf),
                        "dongle trap info: type 0x%x @ epc 0x%08x\n"
                        "  cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
@@ -2847,12 +2832,7 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
                        le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
                        le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
 
-       error = simple_read_from_buffer(data+nbytes, count, &pos, buf, res);
-       if (error < 0)
-               return error;
-
-       nbytes += error;
-       return nbytes;
+       return simple_read_from_buffer(data, count, &pos, buf, res);
 }
 
 static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
@@ -2934,14 +2914,20 @@ static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data,
        error = brcmf_sdio_assert_info(bus, &sh, data, count);
        if (error < 0)
                goto done;
-
        nbytes = error;
-       error = brcmf_sdio_trap_info(bus, &sh, data, count);
+
+       error = brcmf_sdio_trap_info(bus, &sh, data+nbytes, count);
+       if (error < 0)
+               goto done;
+       nbytes += error;
+
+       error = brcmf_sdio_dump_console(bus, &sh, data+nbytes, count);
        if (error < 0)
                goto done;
+       nbytes += error;
 
-       error += nbytes;
-       *ppos += error;
+       error = nbytes;
+       *ppos += nbytes;
 done:
        return error;
 }
@@ -3856,6 +3842,7 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
        .txdata = brcmf_sdbrcm_bus_txdata,
        .txctl = brcmf_sdbrcm_bus_txctl,
        .rxctl = brcmf_sdbrcm_bus_rxctl,
+       .gettxq = brcmf_sdbrcm_bus_gettxq,
 };
 
 void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
index e9d6f91a1f2beb5b9f2315c5394f3e69096b3ddd..5a64280e64850867e2a4590050f024687e28749e 100644 (file)
@@ -20,6 +20,8 @@
 
 #include "dhd.h"
 #include "dhd_dbg.h"
+#include "tracepoint.h"
+#include "fwsignal.h"
 #include "fweh.h"
 #include "fwil.h"
 
@@ -154,7 +156,7 @@ static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp,
                fweh = &ifp->drvr->fweh;
 
                /* handle the event if valid interface and handler */
-               if (ifp->ndev && fweh->evt_handler[code])
+               if (fweh->evt_handler[code])
                        err = fweh->evt_handler[code](ifp, emsg, data);
                else
                        brcmf_err("unhandled event %d ignored\n", code);
@@ -179,9 +181,9 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
        struct brcmf_if *ifp;
        int err = 0;
 
-       brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u\n",
-                 ifevent->action, ifevent->ifidx,
-                 ifevent->bssidx, ifevent->flags);
+       brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n",
+                 ifevent->action, ifevent->ifidx, ifevent->bssidx,
+                 ifevent->flags, ifevent->role);
 
        if (ifevent->ifidx >= BRCMF_MAX_IFS) {
                brcmf_err("invalid interface index: %u\n",
@@ -198,15 +200,20 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
                                   emsg->ifname, emsg->addr);
                if (IS_ERR(ifp))
                        return;
-
+               brcmf_fws_add_interface(ifp);
                if (!drvr->fweh.evt_handler[BRCMF_E_IF])
                        err = brcmf_net_attach(ifp, false);
        }
 
+       if (ifevent->action == BRCMF_E_IF_CHANGE)
+               brcmf_fws_reset_interface(ifp);
+
        err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
 
-       if (ifevent->action == BRCMF_E_IF_DEL)
+       if (ifevent->action == BRCMF_E_IF_DEL) {
+               brcmf_fws_del_interface(ifp);
                brcmf_del_if(drvr, ifevent->bssidx);
+       }
 }
 
 /**
@@ -400,13 +407,12 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp)
  *
  * @drvr: driver information object.
  * @event_packet: event packet to process.
- * @ifidx: index of the firmware interface (may change).
  *
  * 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, u8 *ifidx)
+                             struct brcmf_event *event_packet)
 {
        enum brcmf_fweh_event_code code;
        struct brcmf_fweh_info *fweh = &drvr->fweh;
@@ -418,7 +424,6 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
        /* get event info */
        code = get_unaligned_be32(&event_packet->msg.event_type);
        datalen = get_unaligned_be32(&event_packet->msg.datalen);
-       *ifidx = event_packet->msg.ifidx;
        data = &event_packet[1];
 
        if (code >= BRCMF_E_LAST)
@@ -435,7 +440,7 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
                return;
 
        event->code = code;
-       event->ifidx = *ifidx;
+       event->ifidx = event_packet->msg.ifidx;
 
        /* use memcpy to get aligned event message */
        memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
index 8c39b51dcccfcead4dd00efd8f2bf572476ecbeb..6ec5db9c60a52196e2cd1273efda7d7652b031ba 100644 (file)
@@ -187,10 +187,10 @@ 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, u8 *ifidx);
+                             struct brcmf_event *event_packet);
 
 static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
-                                         struct sk_buff *skb, u8 *ifidx)
+                                         struct sk_buff *skb)
 {
        struct brcmf_event *event_packet;
        u8 *data;
@@ -213,7 +213,7 @@ static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
        if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT)
                return;
 
-       brcmf_fweh_process_event(drvr, event_packet, ifidx);
+       brcmf_fweh_process_event(drvr, event_packet);
 }
 
 #endif /* FWEH_H_ */
index 8d1def935b8d0de1232572382602afe6816a5366..04f395930d86742372585ff42b3ce3837f0d606d 100644 (file)
@@ -25,6 +25,7 @@
 #include "dhd.h"
 #include "dhd_bus.h"
 #include "dhd_dbg.h"
+#include "tracepoint.h"
 #include "fwil.h"
 
 
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
new file mode 100644 (file)
index 0000000..b3c608e
--- /dev/null
@@ -0,0 +1,1951 @@
+/*
+ * 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 <uapi/linux/nl80211.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include "dhd.h"
+#include "dhd_proto.h"
+#include "dhd_dbg.h"
+#include "dhd_bus.h"
+#include "fwil.h"
+#include "fweh.h"
+#include "fwsignal.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 */
+
+/*
+ * 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               ((NL80211_NUM_ACS + 1) * 2)
+#define BRCMF_FWS_PSQ_LEN                              256
+
+#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST                        0x01
+#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED              0x02
+
+/**
+ * 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.
+ */
+enum brcmf_fws_skb_state {
+       BRCMF_FWS_SKBSTATE_NEW,
+       BRCMF_FWS_SKBSTATE_DELAYED,
+       BRCMF_FWS_SKBSTATE_SUPPRESSED
+};
+
+/**
+ * struct brcmf_skbuff_cb - control buffer associated with skbuff.
+ *
+ * @if_flags: holds interface index and packet related flags.
+ * @htod: host to device packet identifier (used in PKTTAG tlv).
+ * @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 if_flags;
+       u32 htod;
+       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 uses FIFO credit (non-pspoll).
+ *     b[7]   - interface in AP mode.
+ *     b[6:4] - AC FIFO number.
+ *     b[3:0] - interface index.
+ */
+#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK      0x0800
+#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_CREDITCHECK_MASK    0x0100
+#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_SHIFT   8
+#define BRCMF_SKB_IF_FLAGS_IF_AP_MASK          0x0080
+#define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT         7
+#define BRCMF_SKB_IF_FLAGS_FIFO_MASK           0x0070
+#define BRCMF_SKB_IF_FLAGS_FIFO_SHIFT          4
+#define BRCMF_SKB_IF_FLAGS_INDEX_MASK          0x000f
+#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT         0
+
+#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_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_PKTID_MASK            0x00FFFFFF
+#define BRCMF_FWS_TXSTAT_PKTID_SHIFT           0
+
+#define brcmf_txstatus_get_field(txs, field) \
+       brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
+                       BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
+
+/**
+ * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
+ *
+ * @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_AC_BK,
+       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.
+ */
+enum brcmf_fws_txstatus {
+       BRCMF_FWS_TXSTATUS_DISCARD,
+       BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
+       BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
+       BRCMF_FWS_TXSTATUS_FW_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 {
+       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 suppress_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.
+ * @gen: generation.
+ * @pkt: packet itself.
+ */
+struct brcmf_fws_hanger_item {
+       enum brcmf_fws_hanger_item_state state;
+       u8 gen;
+       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_info {
+       struct brcmf_pub *drvr;
+       struct brcmf_fws_stats stats;
+       struct brcmf_fws_hanger hanger;
+       enum brcmf_fws_fcmode fcmode;
+       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 deq_node_pos[BRCMF_FWS_FIFO_COUNT];
+       u32 fifo_credit_map;
+       u32 fifo_delay_map;
+};
+
+/*
+ * 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 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;
+
+       brcmf_dbg(TRACE, "enter\n");
+       memset(hanger, 0, sizeof(*hanger));
+       for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
+               hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+}
+
+static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
+{
+       u32 i;
+
+       brcmf_dbg(TRACE, "enter\n");
+       i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
+
+       while (i != h->slot_pos) {
+               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:
+       brcmf_dbg(TRACE, "exit: %d\n", i);
+       return i;
+}
+
+static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
+                                          struct sk_buff *pkt, u32 slot_id)
+{
+       brcmf_dbg(TRACE, "enter\n");
+       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 int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
+                                         u32 slot_id, struct sk_buff **pktout,
+                                         bool remove_item)
+{
+       brcmf_dbg(TRACE, "enter\n");
+       if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+               return -ENOENT;
+
+       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->items[slot_id].gen = 0xff;
+               h->popped++;
+       }
+       return 0;
+}
+
+static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
+                                                  u32 slot_id, u8 gen)
+{
+       brcmf_dbg(TRACE, "enter\n");
+
+       if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+               return -ENOENT;
+
+       h->items[slot_id].gen = gen;
+
+       if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) {
+               brcmf_err("entry not in use\n");
+               return -EINVAL;
+       }
+
+       h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
+       return 0;
+}
+
+static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger,
+                                             struct sk_buff *pkt, u32 slot_id,
+                                             int *gen)
+{
+       brcmf_dbg(TRACE, "enter\n");
+       *gen = 0xff;
+
+       if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+               return -ENOENT;
+
+       if (hanger->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+               brcmf_err("slot not in use\n");
+               return -EINVAL;
+       }
+
+       *gen = hanger->items[slot_id].gen;
+       return 0;
+}
+
+static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
+                                    bool (*fn)(struct sk_buff *, void *),
+                                    int ifidx)
+{
+       struct brcmf_fws_hanger *h = &fws->hanger;
+       struct sk_buff *skb;
+       int i;
+       enum brcmf_fws_hanger_item_state s;
+
+       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
+       for (i = 0; i < ARRAY_SIZE(h->items); i++) {
+               s = h->items[i].state;
+               if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
+                   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_init_mac_descriptor(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;
+       /* 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_clear_mac_descriptor(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;
+}
+
+static struct brcmf_fws_mac_descriptor *
+brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
+{
+       struct brcmf_fws_mac_descriptor *entry;
+       int i;
+
+       brcmf_dbg(TRACE, "enter: ea=%pM\n", ea);
+       if (ea == NULL)
+               return ERR_PTR(-EINVAL);
+
+       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_find_mac_desc(struct brcmf_fws_info *fws, int ifidx, u8 *da)
+{
+       struct brcmf_fws_mac_descriptor *entry = &fws->desc.other;
+       struct brcmf_if *ifp;
+       bool multicast;
+
+       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
+
+       multicast = is_multicast_ether_addr(da);
+       ifp = fws->drvr->iflist[ifidx ? ifidx + 1 : 0];
+       if (WARN_ON(!ifp))
+               goto done;
+
+       /* Multicast destination and P2P clients get the interface entry.
+        * STA gets the interface entry if there is no exact match. For
+        * example, TDLS destinations have their own entry.
+        */
+       entry = NULL;
+       if (multicast && ifp->fws_desc)
+               entry = ifp->fws_desc;
+
+       if (entry != NULL && multicast)
+               goto done;
+
+       entry = brcmf_fws_mac_descriptor_lookup(fws, da);
+       if (IS_ERR(entry))
+               entry = &fws->desc.other;
+
+done:
+       brcmf_dbg(TRACE, "exit: entry=%p\n", entry);
+       return entry;
+}
+
+static bool brcmf_fws_mac_desc_ready(struct brcmf_fws_mac_descriptor *entry,
+                                    int fifo)
+{
+       bool ready;
+
+       /*
+        * destination entry is ready when firmware says it is OPEN
+        * and there are no packets enqueued for it.
+        */
+       ready = entry->state == BRCMF_FWS_STATE_OPEN &&
+               !entry->suppressed &&
+               brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0;
+
+       /*
+        * Or when the destination entry is CLOSED, but firmware has
+        * specifically requested packets for this entry.
+        */
+       ready = ready || (entry->state == BRCMF_FWS_STATE_CLOSE &&
+               (entry->requested_credit + entry->requested_packet));
+       return ready;
+}
+
+static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws,
+                                      struct brcmf_fws_mac_descriptor *entry,
+                                      int ifidx)
+{
+       brcmf_dbg(TRACE, "enter: entry=(ea=%pM, ifid=%d), ifidx=%d\n",
+                 entry->ea, entry->interface_id, ifidx);
+       if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
+               brcmf_dbg(TRACE, "flush psq: ifidx=%d, qlen=%d\n",
+                         ifidx, entry->psq.len);
+               brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
+               entry->occupied = !!(entry->psq.len);
+       }
+}
+
+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;
+
+       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
+       txq = brcmf_bus_gettxq(fws->drvr->bus_if);
+       if (IS_ERR(txq)) {
+               brcmf_dbg(TRACE, "no txq to clean up\n");
+               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;
+
+       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
+       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_mac_desc_cleanup(fws, &table[i], ifidx);
+
+       brcmf_fws_mac_desc_cleanup(fws, &fws->desc.other, ifidx);
+       brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
+       brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
+}
+
+static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx,
+                                struct brcmf_fws_mac_descriptor *entry,
+                                int prec)
+{
+       brcmf_dbg(TRACE, "enter: ea=%pM\n", entry->ea);
+       if (entry->state == BRCMF_FWS_STATE_CLOSE) {
+               /* check delayedQ and suppressQ in one call using bitmap */
+               if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0)
+                       entry->traffic_pending_bmp =
+                               entry->traffic_pending_bmp & ~NBITVAL(prec);
+               else
+                       entry->traffic_pending_bmp =
+                               entry->traffic_pending_bmp | NBITVAL(prec);
+       }
+       /* request a TIM update to firmware at the next piggyback opportunity */
+       if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
+               entry->send_tim_signal = true;
+}
+
+static void
+brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
+                            u8 if_id)
+{
+       struct brcmf_if *ifp = fws->drvr->iflist[if_id];
+
+       brcmf_dbg(TRACE,
+                 "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx);
+       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)
+               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) {
+               brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx);
+               if (entry->occupied)
+                       brcmf_fws_clear_mac_descriptor(entry);
+               else
+                       fws->stats.mac_update_failed++;
+               return 0;
+       }
+
+       brcmf_dbg(TRACE,
+                 "add mac %pM handle %u idx %d\n", addr, mac_handle, ifidx);
+       existing = brcmf_fws_mac_descriptor_lookup(fws, addr);
+       if (IS_ERR(existing)) {
+               if (!entry->occupied) {
+                       entry->mac_handle = mac_handle;
+                       brcmf_fws_init_mac_descriptor(entry, addr, ifidx);
+                       brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
+                                       BRCMF_FWS_PSQ_LEN);
+               } else {
+                       fws->stats.mac_update_failed++;
+               }
+       } else {
+               if (entry != existing) {
+                       brcmf_dbg(TRACE, "relocate mac\n");
+                       memcpy(entry, existing,
+                              offsetof(struct brcmf_fws_mac_descriptor, psq));
+                       entry->mac_handle = mac_handle;
+                       brcmf_fws_clear_mac_descriptor(existing);
+               } 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 i;
+
+       mac_handle = data[0];
+       entry = &fws->desc.nodes[mac_handle & 0x1F];
+       if (!entry->occupied) {
+               fws->stats.mac_ps_update_failed++;
+               return -ESRCH;
+       }
+
+       /* a state update should wipe old credits? */
+       entry->requested_credit = 0;
+       if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
+               entry->state = BRCMF_FWS_STATE_OPEN;
+       } else {
+               entry->state = BRCMF_FWS_STATE_CLOSE;
+               for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++)
+                       brcmf_fws_tim_update(fws, entry, i);
+       }
+       return 0;
+}
+
+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];
+
+       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
+       if (ifidx >= BRCMF_MAX_IFS) {
+               ret = -ERANGE;
+               goto fail;
+       }
+
+       entry = &fws->desc.iface[ifidx];
+       if (!entry->occupied) {
+               ret = -ESRCH;
+               goto fail;
+       }
+
+       switch (type) {
+       case BRCMF_FWS_TYPE_INTERFACE_OPEN:
+               entry->state = BRCMF_FWS_STATE_OPEN;
+               return 0;
+       case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
+               entry->state = BRCMF_FWS_STATE_CLOSE;
+               return 0;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+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;
+       }
+
+       if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
+               entry->requested_credit = data[0];
+       else
+               entry->requested_packet = data[0];
+
+       entry->ac_bitmap = data[2];
+       return 0;
+}
+
+static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
+                                    u8 fifo, u8 credits)
+{
+       if (!credits)
+               return;
+
+       fws->fifo_credit_map |= 1 << fifo;
+       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)
+               queue_work(fws->fws_wq, &fws->fws_dequeue_work);
+}
+
+static void brcmf_skb_pick_up_credit(struct brcmf_fws_info *fws, int fifo,
+                                    struct sk_buff *p)
+{
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac;
+
+       if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
+               if (fws->fcmode != BRCMF_FWS_FCMODE_IMPLIED_CREDIT)
+                       return;
+               brcmf_fws_return_credits(fws, fifo, 1);
+       } else {
+               /*
+                * if this packet did not count against FIFO credit, it
+                * must have taken a requested_credit from the destination
+                * entry (for pspoll etc.)
+                */
+               if (!brcmf_skb_if_flags_get_field(p, REQUESTED))
+                       entry->requested_credit++;
+       }
+       brcmf_fws_schedule_deq(fws);
+}
+
+static int brcmf_fws_enq(struct brcmf_fws_info *fws,
+                        enum brcmf_fws_skb_state state, int fifo,
+                        struct sk_buff *p)
+{
+       int prec = 2 * fifo;
+       u32 *qfull_stat = &fws->stats.delayq_full_error;
+
+       struct brcmf_fws_mac_descriptor *entry;
+
+       entry = brcmf_skbcb(p)->mac;
+       if (entry == NULL) {
+               brcmf_err("no mac descriptor found for skb %p\n", p);
+               return -ENOENT;
+       }
+
+       brcmf_dbg(TRACE, "enter: ea=%pM, qlen=%d\n", entry->ea, entry->psq.len);
+       if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
+               prec += 1;
+               qfull_stat = &fws->stats.supprq_full_error;
+       }
+
+       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;
+       if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
+               entry->suppress_count++;
+
+       /*
+        * A packet has been pushed so update traffic
+        * availability bitmap, if applicable
+        */
+       brcmf_fws_tim_update(fws, entry, fifo);
+       brcmf_fws_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 use_credit = 1;
+       int num_nodes;
+       int node_pos;
+       int prec_out;
+       int pmsk = 3;
+       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)
+                       continue;
+
+               if (entry->suppressed)
+                       pmsk = 2;
+               p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
+               if (p == NULL) {
+                       if (entry->suppressed) {
+                               if (entry->suppr_transit_count >
+                                   entry->suppress_count)
+                                       return NULL;
+                               entry->suppressed = false;
+                               p = brcmu_pktq_mdeq(&entry->psq,
+                                                   1 << (fifo * 2), &prec_out);
+                       }
+               }
+               if  (p == NULL)
+                       continue;
+
+               /* did the packet come from suppress sub-queue? */
+               if (entry->requested_credit > 0) {
+                       entry->requested_credit--;
+                       /*
+                        * if the packet was pulled out while destination is in
+                        * closed state but had a non-zero packets requested,
+                        * then this should not count against the FIFO credit.
+                        * That is due to the fact that the firmware will
+                        * most likely hold onto this packet until a suitable
+                        * time later to push it to the appropriate AC FIFO.
+                        */
+                       if (entry->state == BRCMF_FWS_STATE_CLOSE)
+                               use_credit = 0;
+               } else if (entry->requested_packet > 0) {
+                       entry->requested_packet--;
+                       brcmf_skb_if_flags_set_field(p, REQUESTED, 1);
+                       if (entry->state == BRCMF_FWS_STATE_CLOSE)
+                               use_credit = 0;
+               }
+               brcmf_skb_if_flags_set_field(p, CREDITCHECK, use_credit);
+
+               /* 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);
+
+               /*
+                * 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(TRACE, "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)
+{
+       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->generation != genbit) {
+               entry->suppressed = true;
+               entry->suppress_count = brcmu_pktq_mlen(&entry->psq,
+                                                       1 << (fifo * 2 + 1));
+               entry->suppr_transit_count = entry->transit_count;
+       }
+
+       entry->generation = genbit;
+
+       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,
+                                                genbit);
+               entry->suppress_count++;
+       }
+
+       return ret;
+}
+
+static int
+brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
+                          u32 genbit)
+{
+       u32 fifo;
+       int ret;
+       bool remove_from_hanger = true;
+       struct sk_buff *skb;
+       struct brcmf_fws_mac_descriptor *entry = NULL;
+
+       brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n",
+                 flags, hslot);
+
+       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
+               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;
+       }
+
+       entry = brcmf_skbcb(skb)->mac;
+       if (WARN_ON(!entry)) {
+               brcmu_pkt_buf_free_skb(skb);
+               return -EINVAL;
+       }
+
+       /* pick up the implicit credit from this packet */
+       fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
+       brcmf_skb_pick_up_credit(fws, fifo, skb);
+
+       if (!remove_from_hanger)
+               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit);
+
+       if (remove_from_hanger || ret) {
+               entry->transit_count--;
+               if (entry->suppressed)
+                       entry->suppr_transit_count--;
+
+               brcmf_txfinalize(fws->drvr, 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 0;
+       }
+
+       brcmf_dbg(TRACE, "enter: data %pM\n", data);
+       for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
+               brcmf_fws_return_credits(fws, i, data[i]);
+
+       brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map,
+                 fws->fifo_delay_map);
+       brcmf_fws_schedule_deq(fws);
+       return 0;
+}
+
+static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
+{
+       __le32 status_le;
+       u32 status;
+       u32 hslot;
+       u32 genbit;
+       u8 flags;
+
+       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);
+
+       return brcmf_fws_txstatus_process(fws, flags, hslot, genbit);
+}
+
+static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
+{
+       __le32 timestamp;
+
+       memcpy(&timestamp, &data[2], sizeof(timestamp));
+       brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1],
+                 le32_to_cpu(timestamp));
+       return 0;
+}
+
+/* using macro so sparse checking does not complain
+ * about locking imbalance.
+ */
+#define brcmf_fws_lock(drvr, flags)                            \
+do {                                                           \
+       flags = 0;                                              \
+       spin_lock_irqsave(&((drvr)->fws_spinlock), (flags));    \
+} while (0)
+
+/* using macro so sparse checking does not complain
+ * about locking imbalance.
+ */
+#define brcmf_fws_unlock(drvr, flags) \
+       spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags))
+
+static int brcmf_fws_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;
+       ulong flags;
+       u8 *credits = data;
+
+       if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
+               brcmf_err("event payload too small (%d)\n", e->datalen);
+               return -EINVAL;
+       }
+
+       brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
+       brcmf_fws_lock(ifp->drvr, flags);
+       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(ifp->drvr, flags);
+       return 0;
+}
+
+int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
+                     struct sk_buff *skb)
+{
+       struct brcmf_fws_info *fws = drvr->fws;
+       ulong flags;
+       u8 *signal_data;
+       s16 data_len;
+       u8 type;
+       u8 len;
+       u8 *data;
+
+       brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n",
+                 ifidx, skb->len, signal_len);
+
+       WARN_ON(signal_len > skb->len);
+
+       /* if flow control disabled, skip to packet data and leave */
+       if (!signal_len || !drvr->fw_signals) {
+               skb_pull(skb, signal_len);
+               return 0;
+       }
+
+       /* lock during tlv parsing */
+       brcmf_fws_lock(drvr, flags);
+
+       fws->stats.header_pulls++;
+       data_len = signal_len;
+       signal_data = skb->data;
+
+       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(INFO, "tlv type=%d (%s), len=%d, data[0]=%d\n", type,
+                         brcmf_fws_get_tlv_name(type), len, *data);
+
+               /* abort parsing when length invalid */
+               if (data_len < len + 2)
+                       break;
+
+               if (len != brcmf_fws_get_tlv_len(fws, type))
+                       break;
+
+               switch (type) {
+               case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
+               case BRCMF_FWS_TYPE_COMP_TXSTATUS:
+                       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:
+                       brcmf_fws_macdesc_state_indicate(fws, type, data);
+                       break;
+               case BRCMF_FWS_TYPE_INTERFACE_OPEN:
+               case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
+                       brcmf_fws_interface_state_indicate(fws, type, data);
+                       break;
+               case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
+               case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
+                       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:
+                       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;
+               }
+
+               signal_data += len + 2;
+               data_len -= len + 2;
+       }
+
+       if (data_len != 0)
+               fws->stats.tlv_parse_failed++;
+
+       /* signalling processing result does
+        * not affect the actual ethernet packet.
+        */
+       skb_pull(skb, signal_len);
+
+       /* this may be a signal-only packet
+        */
+       if (skb->len == 0)
+               fws->stats.header_only_pkt++;
+
+       brcmf_fws_unlock(drvr, flags);
+       return 0;
+}
+
+static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
+{
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+       u8 *wlh;
+       u16 data_offset = 0;
+       u8 fillers;
+       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
+
+       brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n",
+                 entry->ea, entry->interface_id, le32_to_cpu(pkttag));
+       if (entry->send_tim_signal)
+               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+
+       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
+       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
+       fillers = round_up(data_offset, 4) - data_offset;
+       data_offset += fillers;
+
+       skb_push(skb, data_offset);
+       wlh = skb->data;
+
+       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
+       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
+       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
+       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
+
+       if (entry->send_tim_signal) {
+               entry->send_tim_signal = 0;
+               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
+               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+               wlh[2] = entry->mac_handle;
+               wlh[3] = entry->traffic_pending_bmp;
+               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
+               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
+       }
+       if (fillers)
+               memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
+
+       brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX),
+                           data_offset >> 2, skb);
+       return 0;
+}
+
+static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
+                                  struct sk_buff *p)
+{
+       struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
+       struct brcmf_fws_mac_descriptor *entry = skcb->mac;
+       int rc = 0;
+       bool header_needed;
+       int hslot = BRCMF_FWS_HANGER_MAXITEMS;
+       u8 free_ctr;
+       u8 ifidx;
+       u8 flags;
+
+       header_needed = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED;
+
+       if (header_needed) {
+               /* obtaining free slot may fail, but that will be caught
+                * by the hanger push. This assures the packet has a BDC
+                * header upon return.
+                */
+               hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
+               free_ctr = entry->seq[fifo];
+               brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
+               brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr);
+               brcmf_skb_htod_tag_set_field(p, GENERATION, 1);
+               entry->transit_count++;
+       }
+       brcmf_skb_if_flags_set_field(p, TRANSMIT, 1);
+       brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
+
+       flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
+       if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) {
+               /*
+               Indicate that this packet is being sent in response to an
+               explicit request from the firmware side.
+               */
+               flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
+       }
+       brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
+       if (header_needed) {
+               brcmf_fws_hdrpush(fws, p);
+               rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
+               if (rc)
+                       brcmf_err("hanger push failed: rc=%d\n", rc);
+       } else {
+               int gen;
+
+               /* remove old header */
+               rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p);
+               if (rc == 0) {
+                       hslot = brcmf_skb_htod_tag_get_field(p, HSLOT);
+                       brcmf_fws_hanger_get_genbit(&fws->hanger, p,
+                                                   hslot, &gen);
+                       brcmf_skb_htod_tag_set_field(p, GENERATION, gen);
+
+                       /* push new header */
+                       brcmf_fws_hdrpush(fws, p);
+               }
+       }
+
+       return rc;
+}
+
+static int
+brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
+{
+       /*
+       put the packet back to the head of queue
+
+       - suppressed packet goes back to suppress sub-queue
+       - pull out the header, if new or delayed packet
+
+       Note: hslot is used only when header removal is done.
+       */
+       struct brcmf_fws_mac_descriptor *entry;
+       enum brcmf_fws_skb_state state;
+       struct sk_buff *pktout;
+       int rc = 0;
+       int fifo;
+       int hslot;
+       u8 ifidx;
+
+       fifo = brcmf_skb_if_flags_get_field(skb, FIFO);
+       state = brcmf_skbcb(skb)->state;
+       entry = brcmf_skbcb(skb)->mac;
+
+       if (entry != NULL) {
+               if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
+                       /* wl-header is saved for suppressed packets */
+                       pktout = brcmu_pktq_penq_head(&entry->psq, 2 * fifo + 1,
+                                                     skb);
+                       if (pktout == NULL) {
+                               brcmf_err("suppress queue full\n");
+                               rc = -ENOSPC;
+                       }
+               } else {
+                       hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+
+                       /* remove header first */
+                       rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
+                       if (rc) {
+                               brcmf_err("header removal failed\n");
+                               /* free the hanger slot */
+                               brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
+                                                       &pktout, true);
+                               brcmf_txfinalize(fws->drvr, skb, false);
+                               rc = -EINVAL;
+                               goto fail;
+                       }
+
+                       /* delay-q packets are going to delay-q */
+                       pktout = brcmu_pktq_penq_head(&entry->psq,
+                                                     2 * fifo, skb);
+                       if (pktout == NULL) {
+                               brcmf_err("delay queue full\n");
+                               rc = -ENOSPC;
+                       }
+
+                       /* free the hanger slot */
+                       brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &pktout,
+                                               true);
+
+                       /* decrement sequence count */
+                       entry->seq[fifo]--;
+               }
+               /*
+               if this packet did not count against FIFO credit, it must have
+               taken a requested_credit from the firmware (for pspoll etc.)
+               */
+               if (!(brcmf_skbcb(skb)->if_flags &
+                     BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK))
+                       entry->requested_credit++;
+       } else {
+               brcmf_err("no mac entry linked\n");
+               rc = -ENOENT;
+       }
+
+
+fail:
+       if (rc)
+               fws->stats.rollback_failed++;
+       else
+               fws->stats.rollback_success++;
+       return rc;
+}
+
+static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
+                                   struct sk_buff *skb)
+{
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+       int *credit = &fws->fifo_credit[fifo];
+       int use_credit = 1;
+
+       brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit);
+
+       if (entry->requested_credit > 0) {
+               /*
+                * if the packet was pulled out while destination is in
+                * closed state but had a non-zero packets requested,
+                * then this should not count against the FIFO credit.
+                * That is due to the fact that the firmware will
+                * most likely hold onto this packet until a suitable
+                * time later to push it to the appropriate AC FIFO.
+                */
+               entry->requested_credit--;
+               if (entry->state == BRCMF_FWS_STATE_CLOSE)
+                       use_credit = 0;
+       } else if (entry->requested_packet > 0) {
+               entry->requested_packet--;
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+               if (entry->state == BRCMF_FWS_STATE_CLOSE)
+                       use_credit = 0;
+       }
+       brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit);
+       if (!use_credit) {
+               brcmf_dbg(TRACE, "exit: no creditcheck set\n");
+               return 0;
+       }
+
+       if (!(*credit)) {
+               brcmf_dbg(TRACE, "exit: credits depleted\n");
+               return -ENAVAIL;
+       }
+       (*credit)--;
+       if (!(*credit))
+               fws->fifo_credit_map &= ~(1 << fifo);
+       brcmf_dbg(TRACE, "exit: ac=%d, credits=%d\n", fifo, *credit);
+       return 0;
+}
+
+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;
+       struct brcmf_bus *bus = fws->drvr->bus_if;
+       int rc;
+
+       entry = skcb->mac;
+       if (IS_ERR(entry))
+               return PTR_ERR(entry);
+
+       rc = brcmf_fws_precommit_skb(fws, fifo, skb);
+       if (rc < 0) {
+               fws->stats.generic_error++;
+               goto rollback;
+       }
+
+       rc = brcmf_bus_txdata(bus, skb);
+       if (rc < 0)
+               goto rollback;
+
+       entry->seq[fifo]++;
+       fws->stats.pkt2bus++;
+       if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
+               fws->stats.send_pkts[fifo]++;
+               fws->stats.fifo_credits_sent[fifo]++;
+       }
+
+       return rc;
+
+rollback:
+       rc = brcmf_fws_rollback_toq(fws, skb);
+       return rc;
+}
+
+int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+       struct brcmf_pub *drvr = ifp->drvr;
+       struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
+       struct ethhdr *eh = (struct ethhdr *)(skb->data);
+       ulong flags;
+       u8 ifidx = ifp->ifidx;
+       int fifo = BRCMF_FWS_FIFO_BCMC;
+       bool multicast = is_multicast_ether_addr(eh->h_dest);
+
+       /* determine the priority */
+       if (!skb->priority)
+               skb->priority = cfg80211_classify8021d(skb);
+
+       drvr->tx_multicast += !!multicast;
+       if (ntohs(eh->h_proto) == ETH_P_PAE)
+               atomic_inc(&ifp->pend_8021x_cnt);
+
+       if (!brcmf_fws_fc_active(drvr->fws)) {
+               /* If the protocol uses a data header, apply it */
+               brcmf_proto_hdrpush(drvr, ifidx, 0, skb);
+
+               /* Use bus module to send data frame */
+               return brcmf_bus_txdata(drvr->bus_if, skb);
+       }
+
+       /* set control buffer information */
+       skcb->if_flags = 0;
+       skcb->mac = brcmf_fws_find_mac_desc(drvr->fws, ifidx, eh->h_dest);
+       skcb->state = BRCMF_FWS_SKBSTATE_NEW;
+       brcmf_skb_if_flags_set_field(skb, INDEX, ifidx);
+       if (!multicast)
+               fifo = brcmf_fws_prio2fifo[skb->priority];
+       brcmf_skb_if_flags_set_field(skb, FIFO, fifo);
+
+       brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest,
+                 multicast, fifo);
+
+       brcmf_fws_lock(drvr, flags);
+       if (!brcmf_fws_mac_desc_ready(skcb->mac, fifo) ||
+           (!multicast &&
+            brcmf_fws_consume_credit(drvr->fws, fifo, skb) < 0)) {
+               /* enqueue the packet in delayQ */
+               drvr->fws->fifo_delay_map |= 1 << fifo;
+               brcmf_fws_enq(drvr->fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
+       } else {
+               brcmf_fws_commit_skb(drvr->fws, fifo, skb);
+       }
+       brcmf_fws_unlock(drvr, flags);
+       return 0;
+}
+
+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_init_mac_descriptor(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;
+
+       brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n",
+                 ifp->bssidx, ifp->mac_addr);
+       if (!ifp->ndev || !ifp->drvr->fw_signals)
+               return;
+
+       entry = &fws->desc.iface[ifp->ifidx];
+       ifp->fws_desc = entry;
+       brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
+       brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
+                       BRCMF_FWS_PSQ_LEN);
+}
+
+void brcmf_fws_del_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;
+
+       ifp->fws_desc = NULL;
+       brcmf_fws_clear_mac_descriptor(entry);
+       brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
+}
+
+static void brcmf_fws_dequeue_worker(struct work_struct *worker)
+{
+       struct brcmf_fws_info *fws;
+       struct sk_buff *skb;
+       ulong flags;
+       int fifo;
+       int credit;
+
+       fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
+
+       brcmf_dbg(TRACE, "enter: fws=%p\n", fws);
+       brcmf_fws_lock(fws->drvr, flags);
+       for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) {
+               brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo,
+                         fws->fifo_credit[fifo]);
+               for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) {
+                       skb = brcmf_fws_deq(fws, fifo);
+                       if (!skb)
+                               break;
+                       if (!brcmf_fws_commit_skb(fws, fifo, skb) &&
+                           brcmf_skbcb(skb)->if_flags &
+                           BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
+                               credit++;
+               }
+               fws->fifo_credit[fifo] -= credit;
+       }
+       brcmf_fws_unlock(fws->drvr, flags);
+}
+
+int brcmf_fws_init(struct brcmf_pub *drvr)
+{
+       u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
+       int rc;
+
+       if (!drvr->fw_signals)
+               return 0;
+
+       spin_lock_init(&drvr->fws_spinlock);
+
+       drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
+       if (!drvr->fws) {
+               rc = -ENOMEM;
+               goto fail;
+       }
+
+       /* set linkage back */
+       drvr->fws->drvr = drvr;
+       drvr->fws->fcmode = fcmode;
+
+       drvr->fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
+       if (drvr->fws->fws_wq == NULL) {
+               brcmf_err("workqueue creation failed\n");
+               rc = -EBADF;
+               goto fail;
+       }
+       INIT_WORK(&drvr->fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
+
+       /* enable firmware signalling if fcmode active */
+       if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE)
+               tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
+                      BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
+                      BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
+
+       rc = brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv);
+       if (rc < 0) {
+               brcmf_err("failed to set bdcv2 tlv signaling\n");
+               goto fail;
+       }
+
+       if (brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
+                               brcmf_fws_notify_credit_map)) {
+               brcmf_err("register credit map handler failed\n");
+               goto fail;
+       }
+
+       brcmf_fws_hanger_init(&drvr->fws->hanger);
+       brcmf_fws_init_mac_descriptor(&drvr->fws->desc.other, NULL, 0);
+       brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
+                       BRCMF_FWS_PSQ_LEN);
+
+       /* create debugfs file for statistics */
+       brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
+
+       /* TODO: remove upon feature delivery */
+       brcmf_err("%s bdcv2 tlv signaling [%x]\n",
+                 drvr->fw_signals ? "enabled" : "disabled", tlv);
+       return 0;
+
+fail:
+       /* disable flow control entirely */
+       drvr->fw_signals = false;
+       brcmf_fws_deinit(drvr);
+       return rc;
+}
+
+void brcmf_fws_deinit(struct brcmf_pub *drvr)
+{
+       struct brcmf_fws_info *fws = drvr->fws;
+       ulong flags;
+
+       if (!fws)
+               return;
+
+       /* cleanup */
+       brcmf_fws_lock(drvr, flags);
+       brcmf_fws_cleanup(fws, -1);
+       drvr->fws = NULL;
+       brcmf_fws_unlock(drvr, flags);
+
+       /* free top structure */
+       kfree(fws);
+}
+
+bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
+{
+       if (!fws)
+               return false;
+
+       brcmf_dbg(TRACE, "enter: mode=%d\n", fws->fcmode);
+       return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
+}
+
+void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
+{
+       ulong flags;
+
+       brcmf_fws_lock(fws->drvr, flags);
+       brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED,
+                                  brcmf_skb_htod_tag_get_field(skb, HSLOT), 0);
+       /* the packet never reached firmware so reclaim credit */
+       if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT &&
+           brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
+               brcmf_fws_return_credits(fws,
+                                        brcmf_skb_htod_tag_get_field(skb,
+                                                                     FIFO),
+                                        1);
+               brcmf_fws_schedule_deq(fws);
+       }
+       brcmf_fws_unlock(fws->drvr, flags);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
new file mode 100644 (file)
index 0000000..fbe483d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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 FWSIGNAL_H_
+#define FWSIGNAL_H_
+
+int brcmf_fws_init(struct brcmf_pub *drvr);
+void brcmf_fws_deinit(struct brcmf_pub *drvr);
+bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
+int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
+                     struct sk_buff *skb);
+int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb);
+
+void brcmf_fws_reset_interface(struct brcmf_if *ifp);
+void brcmf_fws_add_interface(struct brcmf_if *ifp);
+void brcmf_fws_del_interface(struct brcmf_if *ifp);
+void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
+
+#endif /* FWSIGNAL_H_ */
index 4166e642068bf1e156531ba8fdfaee22868e3061..94ff045df2b3a29716a01bb25058403cae8e74e0 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include <linux/slab.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <net/cfg80211.h>
 
 #include <brcmu_wifi.h>
@@ -455,7 +456,9 @@ 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
@@ -473,28 +476,35 @@ static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac)
  * 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. These are
- * derived from the primary net device, ie. the permanent ethernet
- * address of the device.
+ * 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)
+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;
-       struct brcmf_if *p2p_ifp = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].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, pri_ifp->mac_addr, ETH_ALEN);
-       p2p->dev_addr[0] |= 0x02;
-       memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
+       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;
 }
 
@@ -773,7 +783,7 @@ exit:
  * validates the channels in the request.
  */
 static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
-                              struct net_device *ndev,
+                              struct brcmf_if *ifp,
                               struct cfg80211_scan_request *request,
                               u16 action)
 {
@@ -1261,7 +1271,7 @@ static void
 brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg)
 {
        struct brcmf_p2p_info *p2p = &cfg->p2p;
-       struct net_device *ndev = cfg->escan_info.ndev;
+       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) ||
@@ -1271,12 +1281,12 @@ brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg)
                 * So abort scan for off channel completion.
                 */
                if (p2p->af_sent_channel)
-                       brcmf_notify_escan_complete(cfg, ndev, true, true);
+                       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, ndev, true, true);
+               brcmf_notify_escan_complete(cfg, ifp, true, true);
        }
 }
 
@@ -1384,7 +1394,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
                /* After complete GO Negotiation, roll back to mpc mode */
                if ((action == P2P_PAF_GON_CONF) ||
                    (action == P2P_PAF_PROVDIS_RSP))
-                       brcmf_set_mpc(ifp->ndev, 1);
+                       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);
@@ -1421,7 +1431,8 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
                                              CHSPEC_IS2G(chanspec) ?
                                              IEEE80211_BAND_2GHZ :
                                              IEEE80211_BAND_5GHZ);
-       wdev = ifp->ndev->ieee80211_ptr;
+
+       wdev = &ifp->vif->wdev;
        cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len,
                         GFP_ATOMIC);
 
@@ -1637,6 +1648,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
                                 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;
@@ -1725,7 +1737,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
 
        /* To make sure to send successfully action frame, turn off mpc */
        if (config_af_params.mpc_onoff == 0)
-               brcmf_set_mpc(ndev, 0);
+               brcmf_set_mpc(ifp, 0);
 
        /* set status and destination address before sending af */
        if (p2p->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) {
@@ -1753,7 +1765,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
                 * care of current piggback algo, lets abort the scan here
                 * itself.
                 */
-               brcmf_notify_escan_complete(cfg, ndev, true, true);
+               brcmf_notify_escan_complete(cfg, ifp, true, true);
 
                /* update channel */
                af_params->channel = cpu_to_le32(afx_hdl->peer_chan);
@@ -1820,7 +1832,7 @@ exit:
        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(ndev, 1);
+               brcmf_set_mpc(ifp, 1);
 
        return ack;
 }
@@ -1839,7 +1851,6 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
        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;
        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);
@@ -1882,8 +1893,9 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
                                              CHSPEC_IS2G(chanspec) ?
                                              IEEE80211_BAND_2GHZ :
                                              IEEE80211_BAND_5GHZ);
-       wdev = ifp->ndev->ieee80211_ptr;
-       cfg80211_rx_mgmt(wdev, freq, 0, mgmt_frame, mgmt_frame_len, GFP_ATOMIC);
+
+       cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len,
+                        GFP_ATOMIC);
 
        brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
                  mgmt_frame_len, e->datalen, chanspec, freq);
@@ -1934,7 +1946,8 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
 
                p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
 
-               brcmf_p2p_generate_bss_mac(p2p);
+               brcmf_p2p_generate_bss_mac(p2p, NULL);
+               memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
                brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
 
                /* Initialize P2P Discovery in the firmware */
@@ -2040,13 +2053,13 @@ int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
                brcmf_err("vif for P2PAPI_BSSCFG_PRIMARY does not exist\n");
                return -EPERM;
        }
-       brcmf_notify_escan_complete(cfg, vif->ifp->ndev, true, true);
+       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->ndev, 0);
+       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  */
@@ -2123,14 +2136,106 @@ static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif)
        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);
+
+       /* 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_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);
+       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_delete_p2pdev() - delete P2P_DEVICE virtual interface.
+ *
+ * @vif: virtual interface object to delete.
+ */
+static void brcmf_p2p_delete_p2pdev(struct brcmf_cfg80211_vif *vif)
+{
+       struct brcmf_p2p_info *p2p = &vif->ifp->drvr->config->p2p;
+
+       cfg80211_unregister_wdev(&vif->wdev);
+       p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+       brcmf_free_vif(vif);
+}
+
 /**
  * brcmf_p2p_add_vif() - create a new P2P virtual interface.
  *
  * @wiphy: wiphy device of new interface.
  * @name: name of the new interface.
  * @type: nl80211 interface type.
- * @flags: TBD
- * @params: TBD
+ * @flags: not used.
+ * @params: contains mac address for P2P device.
  */
 struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
                                       enum nl80211_iftype type, u32 *flags,
@@ -2157,6 +2262,9 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
                iftype = BRCMF_FIL_P2P_IF_GO;
                mode = WL_MODE_AP;
                break;
+       case NL80211_IFTYPE_P2P_DEVICE:
+               return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy,
+                                              params->macaddr);
        default:
                return ERR_PTR(-EOPNOTSUPP);
        }
@@ -2244,6 +2352,8 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
                break;
 
        case NL80211_IFTYPE_P2P_DEVICE:
+               brcmf_p2p_delete_p2pdev(vif);
+               return 0;
        default:
                return -ENOTSUPP;
                break;
@@ -2275,3 +2385,33 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
 
        return err;
 }
+
+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);
+       mutex_lock(&cfg->usr_sync);
+       (void)brcmf_p2p_deinit_discovery(p2p);
+       brcmf_abort_scanning(cfg);
+       clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
+       mutex_unlock(&cfg->usr_sync);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c
new file mode 100644 (file)
index 0000000..b505db4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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/module.h> /* bug in tracepoint.h, it should include this */
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "tracepoint.h"
+#endif
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h
new file mode 100644 (file)
index 0000000..9df1f7a
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+#if !defined(BRCMF_TRACEPOINT_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define BRCMF_TRACEPOINT_H_
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#ifndef CONFIG_BRCM_TRACING
+
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+
+#endif /* CONFIG_BRCM_TRACING */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM   brcmfmac
+
+#define MAX_MSG_LEN            100
+
+TRACE_EVENT(brcmf_err,
+       TP_PROTO(const char *func, struct va_format *vaf),
+       TP_ARGS(func, vaf),
+       TP_STRUCT__entry(
+               __string(func, func)
+               __dynamic_array(char, msg, MAX_MSG_LEN)
+       ),
+       TP_fast_assign(
+               __assign_str(func, func);
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      MAX_MSG_LEN, vaf->fmt,
+                                      *vaf->va) >= MAX_MSG_LEN);
+       ),
+       TP_printk("%s: %s", __get_str(func), __get_str(msg))
+);
+
+TRACE_EVENT(brcmf_dbg,
+       TP_PROTO(u32 level, const char *func, struct va_format *vaf),
+       TP_ARGS(level, func, vaf),
+       TP_STRUCT__entry(
+               __field(u32, level)
+               __string(func, func)
+               __dynamic_array(char, msg, MAX_MSG_LEN)
+       ),
+       TP_fast_assign(
+               __entry->level = level;
+               __assign_str(func, func);
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      MAX_MSG_LEN, vaf->fmt,
+                                      *vaf->va) >= MAX_MSG_LEN);
+       ),
+       TP_printk("%s: %s", __get_str(func), __get_str(msg))
+);
+
+TRACE_EVENT(brcmf_hexdump,
+       TP_PROTO(void *data, size_t len),
+       TP_ARGS(data, len),
+       TP_STRUCT__entry(
+               __field(unsigned long, len)
+               __dynamic_array(u8, hdata, len)
+       ),
+       TP_fast_assign(
+               __entry->len = len;
+               memcpy(__get_dynamic_array(hdata), data, len);
+       ),
+       TP_printk("hexdump [length=%lu]", __entry->len)
+);
+
+#ifdef CONFIG_BRCM_TRACING
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE tracepoint
+
+#include <trace/define_trace.h>
+
+#endif /* CONFIG_BRCM_TRACING */
+
+#endif /* BRCMF_TRACEPOINT_H_ */
index 42289e9ea8863d0bee3d04a78dbcdf0e46d17c5d..01aed7ad6bec17ec4f2dc453518f3a72079f5c76 100644 (file)
@@ -112,11 +112,6 @@ struct brcmf_usbdev_info {
 static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
                                struct brcmf_usbreq  *req);
 
-MODULE_AUTHOR("Broadcom Corporation");
-MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac usb driver.");
-MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac usb cards");
-MODULE_LICENSE("Dual BSD/GPL");
-
 static struct brcmf_usbdev *brcmf_usb_get_buspub(struct device *dev)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
@@ -422,8 +417,6 @@ static void brcmf_usb_tx_complete(struct urb *urb)
        brcmf_usb_del_fromq(devinfo, req);
 
        brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0);
-
-       brcmu_pkt_buf_free_skb(req->skb);
        req->skb = NULL;
        brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount);
        if (devinfo->tx_freecount > devinfo->tx_high_watermark &&
@@ -577,15 +570,17 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
        int ret;
 
        brcmf_dbg(USB, "Enter, skb=%p\n", skb);
-       if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
-               return -EIO;
+       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) {
-               brcmu_pkt_buf_free_skb(skb);
                brcmf_err("no req to send\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto fail;
        }
 
        req->skb = skb;
@@ -598,18 +593,21 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
        if (ret) {
                brcmf_err("brcmf_usb_tx usb_submit_urb FAILED\n");
                brcmf_usb_del_fromq(devinfo, req);
-               brcmu_pkt_buf_free_skb(req->skb);
                req->skb = NULL;
                brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req,
-                                               &devinfo->tx_freecount);
-       } else {
-               if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
-                       !devinfo->tx_flowblock) {
-                       brcmf_txflowblock(dev, true);
-                       devinfo->tx_flowblock = true;
-               }
+                             &devinfo->tx_freecount);
+               goto fail;
        }
 
+       if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
+           !devinfo->tx_flowblock) {
+               brcmf_txflowblock(dev, true);
+               devinfo->tx_flowblock = true;
+       }
+       return 0;
+
+fail:
+       brcmf_txcomplete(dev, skb, false);
        return ret;
 }
 
@@ -1485,6 +1483,7 @@ static struct usb_device_id brcmf_usb_devid_table[] = {
        { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) },
        { }
 };
+
 MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
 MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
 MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
index 78da3eff75e8a1c5c126a60aab7bc90d7663a314..e4f1f3c9575aae6374b7844fb6281e19b02a862a 100644 (file)
@@ -26,6 +26,7 @@
 #include <brcmu_wifi.h>
 #include "dhd.h"
 #include "dhd_dbg.h"
+#include "tracepoint.h"
 #include "fwil_types.h"
 #include "p2p.h"
 #include "wl_cfg80211.h"
@@ -182,64 +183,6 @@ static struct ieee80211_channel __wl_5ghz_a_channels[] = {
        CHAN5G(216, 0),
 };
 
-static struct ieee80211_channel __wl_5ghz_n_channels[] = {
-       CHAN5G(32, 0), CHAN5G(34, 0),
-       CHAN5G(36, 0), CHAN5G(38, 0),
-       CHAN5G(40, 0), CHAN5G(42, 0),
-       CHAN5G(44, 0), CHAN5G(46, 0),
-       CHAN5G(48, 0), CHAN5G(50, 0),
-       CHAN5G(52, 0), CHAN5G(54, 0),
-       CHAN5G(56, 0), CHAN5G(58, 0),
-       CHAN5G(60, 0), CHAN5G(62, 0),
-       CHAN5G(64, 0), CHAN5G(66, 0),
-       CHAN5G(68, 0), CHAN5G(70, 0),
-       CHAN5G(72, 0), CHAN5G(74, 0),
-       CHAN5G(76, 0), CHAN5G(78, 0),
-       CHAN5G(80, 0), CHAN5G(82, 0),
-       CHAN5G(84, 0), CHAN5G(86, 0),
-       CHAN5G(88, 0), CHAN5G(90, 0),
-       CHAN5G(92, 0), CHAN5G(94, 0),
-       CHAN5G(96, 0), CHAN5G(98, 0),
-       CHAN5G(100, 0), CHAN5G(102, 0),
-       CHAN5G(104, 0), CHAN5G(106, 0),
-       CHAN5G(108, 0), CHAN5G(110, 0),
-       CHAN5G(112, 0), CHAN5G(114, 0),
-       CHAN5G(116, 0), CHAN5G(118, 0),
-       CHAN5G(120, 0), CHAN5G(122, 0),
-       CHAN5G(124, 0), CHAN5G(126, 0),
-       CHAN5G(128, 0), CHAN5G(130, 0),
-       CHAN5G(132, 0), CHAN5G(134, 0),
-       CHAN5G(136, 0), CHAN5G(138, 0),
-       CHAN5G(140, 0), CHAN5G(142, 0),
-       CHAN5G(144, 0), CHAN5G(145, 0),
-       CHAN5G(146, 0), CHAN5G(147, 0),
-       CHAN5G(148, 0), CHAN5G(149, 0),
-       CHAN5G(150, 0), CHAN5G(151, 0),
-       CHAN5G(152, 0), CHAN5G(153, 0),
-       CHAN5G(154, 0), CHAN5G(155, 0),
-       CHAN5G(156, 0), CHAN5G(157, 0),
-       CHAN5G(158, 0), CHAN5G(159, 0),
-       CHAN5G(160, 0), CHAN5G(161, 0),
-       CHAN5G(162, 0), CHAN5G(163, 0),
-       CHAN5G(164, 0), CHAN5G(165, 0),
-       CHAN5G(166, 0), CHAN5G(168, 0),
-       CHAN5G(170, 0), CHAN5G(172, 0),
-       CHAN5G(174, 0), CHAN5G(176, 0),
-       CHAN5G(178, 0), CHAN5G(180, 0),
-       CHAN5G(182, 0), CHAN5G(184, 0),
-       CHAN5G(186, 0), CHAN5G(188, 0),
-       CHAN5G(190, 0), CHAN5G(192, 0),
-       CHAN5G(194, 0), CHAN5G(196, 0),
-       CHAN5G(198, 0), CHAN5G(200, 0),
-       CHAN5G(202, 0), CHAN5G(204, 0),
-       CHAN5G(206, 0), CHAN5G(208, 0),
-       CHAN5G(210, 0), CHAN5G(212, 0),
-       CHAN5G(214, 0), CHAN5G(216, 0),
-       CHAN5G(218, 0), CHAN5G(220, 0),
-       CHAN5G(222, 0), CHAN5G(224, 0),
-       CHAN5G(226, 0), CHAN5G(228, 0),
-};
-
 static struct ieee80211_supported_band __wl_band_2ghz = {
        .band = IEEE80211_BAND_2GHZ,
        .channels = __wl_2ghz_channels,
@@ -256,12 +199,28 @@ static struct ieee80211_supported_band __wl_band_5ghz_a = {
        .n_bitrates = wl_a_rates_size,
 };
 
-static struct ieee80211_supported_band __wl_band_5ghz_n = {
-       .band = IEEE80211_BAND_5GHZ,
-       .channels = __wl_5ghz_n_channels,
-       .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
-       .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_PASSIVE_SCAN and NL80211_RRF_NO_IBSS for 5GHz channels (for
+ * 36..48 and 149..165). With respect to these flags, wpa_supplicant doesn't
+ * start p2p operations on 5GHz channels. All the changes in world regulatory
+ * 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, 40, 6, 20, 0),
+               /* IEEE 802.11a, channel 100..165 */
+               REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), }
 };
 
 static const u32 __wl_cipher_suites[] = {
@@ -523,17 +482,16 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
                return ERR_PTR(-EOPNOTSUPP);
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_P2P_DEVICE:
                return brcmf_p2p_add_vif(wiphy, name, type, flags, params);
        case NL80211_IFTYPE_UNSPECIFIED:
-       case NL80211_IFTYPE_P2P_DEVICE:
        default:
                return ERR_PTR(-EINVAL);
        }
 }
 
-void brcmf_set_mpc(struct net_device *ndev, int mpc)
+void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
 {
-       struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
 
        if (check_vif_up(ifp->vif)) {
@@ -546,10 +504,9 @@ void brcmf_set_mpc(struct net_device *ndev, int mpc)
        }
 }
 
-s32
-brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
-                           struct net_device *ndev,
-                           bool aborted, bool fw_abort)
+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;
@@ -580,7 +537,7 @@ brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
                /* 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(netdev_priv(ndev), BRCMF_C_SCAN,
+               err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
                                             &params_le, sizeof(params_le));
                if (err)
                        brcmf_err("Scan abort  failed\n");
@@ -594,12 +551,12 @@ brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
                cfg->sched_escan = false;
                if (!aborted)
                        cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
-               brcmf_set_mpc(ndev, 1);
+               brcmf_set_mpc(ifp, 1);
        } else if (scan_request) {
                brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
                          aborted ? "Aborted" : "Done");
                cfg80211_scan_done(scan_request, aborted);
-               brcmf_set_mpc(ndev, 1);
+               brcmf_set_mpc(ifp, 1);
        }
        if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
                brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
@@ -619,9 +576,9 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
 
        if (ndev) {
                if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
-                   cfg->escan_info.ndev == ndev)
-                       brcmf_notify_escan_complete(cfg, ndev, true,
-                                                   true);
+                   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);
        }
@@ -637,9 +594,9 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
                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:
-       case NL80211_IFTYPE_P2P_DEVICE:
        default:
                return -EINVAL;
        }
@@ -803,7 +760,7 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
 }
 
 static s32
-brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
+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 +
@@ -832,8 +789,7 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
        params->action = cpu_to_le16(action);
        params->sync_id = cpu_to_le16(0x1234);
 
-       err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "escan",
-                                      params, params_size);
+       err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
        if (err) {
                if (err == -EBUSY)
                        brcmf_dbg(INFO, "system busy : escan canceled\n");
@@ -848,7 +804,7 @@ exit:
 
 static s32
 brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
-              struct net_device *ndev, struct cfg80211_scan_request *request)
+              struct brcmf_if *ifp, struct cfg80211_scan_request *request)
 {
        s32 err;
        u32 passive_scan;
@@ -856,35 +812,35 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
        struct escan_info *escan = &cfg->escan_info;
 
        brcmf_dbg(SCAN, "Enter\n");
-       escan->ndev = ndev;
+       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(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN,
+       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_set_mpc(ndev, 0);
+       brcmf_set_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, ndev, request, WL_ESCAN_ACTION_START);
+       err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
        if (err)
-               brcmf_set_mpc(ndev, 1);
+               brcmf_set_mpc(ifp, 1);
        return err;
 }
 
 static s32
-brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
+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 = netdev_priv(ndev);
-       struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
+       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;
@@ -910,10 +866,8 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
        }
 
        /* If scan req comes for p2p0, send it over primary I/F */
-       if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) {
-               ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
-               ndev = ifp->ndev;
-       }
+       if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+               vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
 
        /* Arm scan timeout timer */
        mod_timer(&cfg->escan_timeout, jiffies +
@@ -934,11 +888,11 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
        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, ifp->vif);
+               err = brcmf_p2p_scan_prep(wiphy, request, vif);
                if (err)
                        goto scan_out;
 
-               err = brcmf_do_escan(cfg, wiphy, ndev, request);
+               err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
                if (err)
                        goto scan_out;
        } else {
@@ -962,7 +916,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
                        brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
                        goto scan_out;
                }
-               brcmf_set_mpc(ndev, 0);
+               brcmf_set_mpc(ifp, 0);
                err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
                                             &sr->ssid_le, sizeof(sr->ssid_le));
                if (err) {
@@ -972,7 +926,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
                        else
                                brcmf_err("WLC_SCAN error (%d)\n", err);
 
-                       brcmf_set_mpc(ndev, 1);
+                       brcmf_set_mpc(ifp, 1);
                        goto scan_out;
                }
        }
@@ -990,16 +944,15 @@ scan_out:
 static s32
 brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
 {
-       struct net_device *ndev = request->wdev->netdev;
+       struct brcmf_cfg80211_vif *vif;
        s32 err = 0;
 
        brcmf_dbg(TRACE, "Enter\n");
-
-       if (!check_vif_up(container_of(request->wdev,
-                                      struct brcmf_cfg80211_vif, wdev)))
+       vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
+       if (!check_vif_up(vif))
                return -EIO;
 
-       err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
+       err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
 
        if (err)
                brcmf_err("scan error (%d)\n", err);
@@ -2510,7 +2463,7 @@ void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
        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->ndev, true, true);
+               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);
@@ -2522,7 +2475,7 @@ static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
                        container_of(work, struct brcmf_cfg80211_info,
                                     escan_timeout_work);
 
-       brcmf_notify_escan_complete(cfg, cfg->escan_info.ndev, true, true);
+       brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
 }
 
 static void brcmf_escan_timeout(unsigned long data)
@@ -2573,7 +2526,6 @@ brcmf_cfg80211_escan_handler(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;
        s32 status;
        s32 err = 0;
        struct brcmf_escan_result_le *escan_result_le;
@@ -2586,9 +2538,8 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
 
        status = e->status;
 
-       if (!ndev || !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
-               brcmf_err("scan not ready ndev %p drv_status %x\n", ndev,
-                         !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status));
+       if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
+               brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
                return -EPERM;
        }
 
@@ -2659,7 +2610,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
                                cfg->escan_info.escan_buf;
                        brcmf_inform_bss(cfg);
                        aborted = status != BRCMF_E_STATUS_SUCCESS;
-                       brcmf_notify_escan_complete(cfg, ndev, aborted,
+                       brcmf_notify_escan_complete(cfg, ifp, aborted,
                                                    false);
                } else
                        brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
@@ -2737,7 +2688,7 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
                brcmf_abort_scanning(cfg);
 
        /* Turn off watchdog timer */
-       brcmf_set_mpc(ndev, 1);
+       brcmf_set_mpc(netdev_priv(ndev), 1);
 
 exit:
        brcmf_dbg(TRACE, "Exit\n");
@@ -2895,7 +2846,6 @@ 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 net_device *ndev = ifp->ndev;
        struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
        struct cfg80211_scan_request *request = NULL;
        struct cfg80211_ssid *ssid = NULL;
@@ -2979,7 +2929,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
                }
 
                set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
-               err = brcmf_do_escan(cfg, wiphy, ndev, request);
+               err = brcmf_do_escan(cfg, wiphy, ifp, request);
                if (err) {
                        clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
                        goto out_err;
@@ -3051,16 +3001,16 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
        int i;
        int ret = 0;
 
-       brcmf_dbg(SCAN, "Enter n_match_sets:%d   n_ssids:%d\n",
+       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 (!request || !request->n_ssids || !request->n_match_sets) {
+       if (!request->n_ssids || !request->n_match_sets) {
                brcmf_err("Invalid sched scan req!! n_ssids:%d\n",
-                         request ? request->n_ssids : 0);
+                         request->n_ssids);
                return -EINVAL;
        }
 
@@ -3136,7 +3086,7 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
        brcmf_dbg(SCAN, "enter\n");
        brcmf_dev_pno_clean(ndev);
        if (cfg->sched_escan)
-               brcmf_notify_escan_complete(cfg, ndev, true, true);
+               brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
        return 0;
 }
 
@@ -3708,7 +3658,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
                ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
        }
 
-       brcmf_set_mpc(ndev, 0);
+       brcmf_set_mpc(ifp, 0);
 
        /* find the RSN_IE */
        rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
@@ -3816,7 +3766,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
 
 exit:
        if (err)
-               brcmf_set_mpc(ndev, 1);
+               brcmf_set_mpc(ifp, 1);
        return err;
 }
 
@@ -3856,7 +3806,7 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
                if (err < 0)
                        brcmf_err("bss_enable config failed %d\n", err);
        }
-       brcmf_set_mpc(ndev, 1);
+       brcmf_set_mpc(ifp, 1);
        set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
        clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
 
@@ -3913,13 +3863,13 @@ brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
                                   struct wireless_dev *wdev,
                                   u16 frame_type, bool reg)
 {
-       struct brcmf_if *ifp = netdev_priv(wdev->netdev);
-       struct brcmf_cfg80211_vif *vif = ifp->vif;
+       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
@@ -3935,7 +3885,6 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        const struct ieee80211_mgmt *mgmt;
-       struct brcmf_if *ifp;
        struct brcmf_cfg80211_vif *vif;
        s32 err = 0;
        s32 ie_offset;
@@ -3971,8 +3920,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                ie_offset =  DOT11_MGMT_HDR_LEN +
                             DOT11_BCN_PRB_FIXED_LEN;
                ie_len = len - ie_offset;
-               ifp = netdev_priv(wdev->netdev);
-               vif = ifp->vif;
+               vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
                if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
                        vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
                err = brcmf_vif_set_mgmt_ie(vif,
@@ -4007,7 +3955,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                          *cookie, le16_to_cpu(action_frame->len),
                          chan->center_freq);
 
-               ack = brcmf_p2p_send_action_frame(cfg, wdev->netdev,
+               ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
                                                  af_params);
 
                cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
@@ -4079,6 +4027,8 @@ static struct cfg80211_ops wl_cfg80211_ops = {
        .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,
 #ifdef CONFIG_NL80211_TESTMODE
        .testmode_cmd = brcmf_cfg80211_testmode
 #endif
@@ -4162,6 +4112,11 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
                      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)
        }
 };
 
@@ -4187,13 +4142,6 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
        wiphy->iface_combinations = brcmf_iface_combos;
        wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
        wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
-       wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a;  /* Set
-                                               * it as 11a by default.
-                                               * This will be updated with
-                                               * 11n phy tables in
-                                               * "ifconfig up"
-                                               * if phy has 11n capability
-                                               */
        wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
        wiphy->cipher_suites = __wl_cipher_suites;
        wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
@@ -4203,6 +4151,9 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
        wiphy->mgmt_stypes = brcmf_txrx_stypes;
        wiphy->max_remain_on_channel_duration = 5000;
        brcmf_wiphy_pno_params(wiphy);
+       brcmf_dbg(INFO, "Registering custom regulatory\n");
+       wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
+       wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
        err = wiphy_register(wiphy);
        if (err < 0) {
                brcmf_err("Could not register wiphy device (%d)\n", err);
@@ -4621,9 +4572,11 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
 
                ifp->vif = vif;
                vif->ifp = ifp;
-               vif->wdev.netdev = ifp->ndev;
-               ifp->ndev->ieee80211_ptr = &vif->wdev;
-               SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
+               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;
@@ -4926,34 +4879,248 @@ dongle_scantime_out:
        return err;
 }
 
-static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
+
+static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
+{
+       struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+       struct ieee80211_channel *band_chan_arr;
+       struct brcmf_chanspec_list *list;
+       s32 err;
+       u8 *pbuf;
+       u32 i, j;
+       u32 total;
+       u16 chanspec;
+       enum ieee80211_band band;
+       u32 channel;
+       u32 *n_cnt;
+       bool ht40_allowed;
+       u32 index;
+       u32 ht40_flag;
+       bool update;
+       u32 array_size;
+
+       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 exit;
+       }
+
+       __wl_band_2ghz.n_channels = 0;
+       __wl_band_5ghz_a.n_channels = 0;
+
+       total = le32_to_cpu(list->count);
+       for (i = 0; i < total; i++) {
+               chanspec = (u16)le32_to_cpu(list->element[i]);
+               channel = CHSPEC_CHANNEL(chanspec);
+
+               if (CHSPEC_IS40(chanspec)) {
+                       if (CHSPEC_SB_UPPER(chanspec))
+                               channel += CH_10MHZ_APART;
+                       else
+                               channel -= CH_10MHZ_APART;
+               } else if (CHSPEC_IS80(chanspec)) {
+                       brcmf_dbg(INFO, "HT80 center channel : %d\n",
+                                 channel);
+                       continue;
+               }
+               if (CHSPEC_IS2G(chanspec) && (channel >= CH_MIN_2G_CHANNEL) &&
+                   (channel <= CH_MAX_2G_CHANNEL)) {
+                       band_chan_arr = __wl_2ghz_channels;
+                       array_size = ARRAY_SIZE(__wl_2ghz_channels);
+                       n_cnt = &__wl_band_2ghz.n_channels;
+                       band = IEEE80211_BAND_2GHZ;
+                       ht40_allowed = (bw_cap == WLC_N_BW_40ALL);
+               } else if (CHSPEC_IS5G(chanspec) &&
+                          channel >= CH_MIN_5G_CHANNEL) {
+                       band_chan_arr = __wl_5ghz_a_channels;
+                       array_size = ARRAY_SIZE(__wl_5ghz_a_channels);
+                       n_cnt = &__wl_band_5ghz_a.n_channels;
+                       band = IEEE80211_BAND_5GHZ;
+                       ht40_allowed = !(bw_cap == WLC_N_BW_20ALL);
+               } else {
+                       brcmf_err("Invalid channel Sepc. 0x%x.\n", chanspec);
+                       continue;
+               }
+               if (!ht40_allowed && CHSPEC_IS40(chanspec))
+                       continue;
+               update = false;
+               for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
+                       if (band_chan_arr[j].hw_value == channel) {
+                               update = true;
+                               break;
+                       }
+               }
+               if (update)
+                       index = j;
+               else
+                       index = *n_cnt;
+               if (index <  array_size) {
+                       band_chan_arr[index].center_freq =
+                               ieee80211_channel_to_frequency(channel, band);
+                       band_chan_arr[index].hw_value = channel;
+
+                       if (CHSPEC_IS40(chanspec) && ht40_allowed) {
+                               /* assuming the order is HT20, HT40 Upper,
+                                * HT40 lower from chanspecs
+                                */
+                               ht40_flag = band_chan_arr[index].flags &
+                                           IEEE80211_CHAN_NO_HT40;
+                               if (CHSPEC_SB_UPPER(chanspec)) {
+                                       if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+                                               band_chan_arr[index].flags &=
+                                                       ~IEEE80211_CHAN_NO_HT40;
+                                       band_chan_arr[index].flags |=
+                                               IEEE80211_CHAN_NO_HT40PLUS;
+                               } else {
+                                       /* It should be one of
+                                        * IEEE80211_CHAN_NO_HT40 or
+                                        * IEEE80211_CHAN_NO_HT40PLUS
+                                        */
+                                       band_chan_arr[index].flags &=
+                                                       ~IEEE80211_CHAN_NO_HT40;
+                                       if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+                                               band_chan_arr[index].flags |=
+                                                   IEEE80211_CHAN_NO_HT40MINUS;
+                               }
+                       } else {
+                               band_chan_arr[index].flags =
+                                                       IEEE80211_CHAN_NO_HT40;
+                               if (band == IEEE80211_BAND_2GHZ)
+                                       channel |= WL_CHANSPEC_BAND_2G;
+                               else
+                                       channel |= WL_CHANSPEC_BAND_5G;
+                               channel |= WL_CHANSPEC_BW_20;
+                               err = brcmf_fil_bsscfg_int_get(ifp,
+                                                              "per_chan_info",
+                                                              &channel);
+                               if (!err) {
+                                       if (channel & WL_CHAN_RADAR)
+                                               band_chan_arr[index].flags |=
+                                                       (IEEE80211_CHAN_RADAR |
+                                                       IEEE80211_CHAN_NO_IBSS);
+                                       if (channel & WL_CHAN_PASSIVE)
+                                               band_chan_arr[index].flags |=
+                                                   IEEE80211_CHAN_PASSIVE_SCAN;
+                               }
+                       }
+                       if (!update)
+                               (*n_cnt)++;
+               }
+       }
+exit:
+       kfree(pbuf);
+       return err;
+}
+
+
+static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
 {
        struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
        struct wiphy *wiphy;
        s32 phy_list;
+       u32 band_list[3];
+       u32 nmode;
+       u32 bw_cap = 0;
        s8 phy;
-       s32 err = 0;
+       s32 err;
+       u32 nband;
+       s32 i;
+       struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS];
+       s32 index;
 
        err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
                                     &phy_list, sizeof(phy_list));
        if (err) {
-               brcmf_err("error (%d)\n", err);
+               brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err);
                return err;
        }
 
        phy = ((char *)&phy_list)[0];
-       brcmf_dbg(INFO, "%c phy\n", phy);
-       if (phy == 'n' || phy == 'a') {
-               wiphy = cfg_to_wiphy(cfg);
-               wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
+       brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy);
+
+
+       err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST,
+                                    &band_list, sizeof(band_list));
+       if (err) {
+               brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err);
+               return err;
        }
+       brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n",
+                 band_list[0], band_list[1], band_list[2]);
+
+       err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
+       if (err) {
+               brcmf_err("nmode error (%d)\n", err);
+       } else {
+               err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &bw_cap);
+               if (err)
+                       brcmf_err("mimo_bw_cap error (%d)\n", err);
+       }
+       brcmf_dbg(INFO, "nmode=%d, mimo_bw_cap=%d\n", nmode, bw_cap);
+
+       err = brcmf_construct_reginfo(cfg, bw_cap);
+       if (err) {
+               brcmf_err("brcmf_construct_reginfo failed (%d)\n", err);
+               return err;
+       }
+
+       nband = band_list[0];
+       memset(bands, 0, sizeof(bands));
+
+       for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) {
+               index = -1;
+               if ((band_list[i] == WLC_BAND_5G) &&
+                   (__wl_band_5ghz_a.n_channels > 0)) {
+                       index = IEEE80211_BAND_5GHZ;
+                       bands[index] = &__wl_band_5ghz_a;
+                       if ((bw_cap == WLC_N_BW_40ALL) ||
+                           (bw_cap == WLC_N_BW_20IN2G_40IN5G))
+                               bands[index]->ht_cap.cap |=
+                                                       IEEE80211_HT_CAP_SGI_40;
+               } else if ((band_list[i] == WLC_BAND_2G) &&
+                          (__wl_band_2ghz.n_channels > 0)) {
+                       index = IEEE80211_BAND_2GHZ;
+                       bands[index] = &__wl_band_2ghz;
+                       if (bw_cap == WLC_N_BW_40ALL)
+                               bands[index]->ht_cap.cap |=
+                                                       IEEE80211_HT_CAP_SGI_40;
+               }
+
+               if ((index >= 0) && nmode) {
+                       bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+                       bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+                       bands[index]->ht_cap.ht_supported = true;
+                       bands[index]->ht_cap.ampdu_factor =
+                                               IEEE80211_HT_MAX_AMPDU_64K;
+                       bands[index]->ht_cap.ampdu_density =
+                                               IEEE80211_HT_MPDU_DENSITY_16;
+                       /* An HT shall support all EQM rates for one spatial
+                        * stream
+                        */
+                       bands[index]->ht_cap.mcs.rx_mask[0] = 0xff;
+               }
+       }
+
+       wiphy = cfg_to_wiphy(cfg);
+       wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ];
+       wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ];
+       wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
 
        return err;
 }
 
+
 static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
 {
-       return wl_update_wiphybands(cfg);
+       return brcmf_update_wiphybands(cfg);
 }
 
 static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
index 8b5d4989906c54e9f95df3096e9567f1738fd2c3..2907437ef4381d9f5b9a8033ef91f9968b126a1e 100644 (file)
@@ -238,9 +238,8 @@ struct escan_info {
        u32 escan_state;
        u8 escan_buf[WL_ESCAN_BUF_SIZE];
        struct wiphy *wiphy;
-       struct net_device *ndev;
-       s32 (*run)(struct brcmf_cfg80211_info *cfg,
-                  struct net_device *ndev,
+       struct brcmf_if *ifp;
+       s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
                   struct cfg80211_scan_request *request, u16 action);
 };
 
@@ -493,9 +492,9 @@ 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 net_device *ndev,
-                               bool aborted, bool fw_abort);
-void brcmf_set_mpc(struct net_device *ndev, int mpc);
+                               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);
 
 #endif                         /* _wl_cfg80211_h_ */
index d3d4151c3eda8bdd94f06110f465bfba98a96cea..cba19d839b77951e14db3fc01115c475a79b09eb 100644 (file)
@@ -43,6 +43,10 @@ BRCMSMAC_OFILES := \
        brcms_trace_events.o \
        debug.o
 
+ifdef CONFIG_BCMA_DRIVER_GPIO
+BRCMSMAC_OFILES += led.o
+endif
+
 MODULEPFX := brcmsmac
 
 obj-$(CONFIG_BRCMSMAC) += $(MODULEPFX).o
index f0888a9ee32e1b62d3b635c4b7aa47ca0b2c09a4..e4fd1ee3d690c48c11bd7a08b1ed477ab5632682 100644 (file)
 #define        IS_SIM(chippkg) \
        ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID))
 
-#ifdef DEBUG
-#define        SI_MSG(fmt, ...)        pr_debug(fmt, ##__VA_ARGS__)
-#else
-#define        SI_MSG(fmt, ...)        no_printk(fmt, ##__VA_ARGS__)
-#endif                         /* DEBUG */
-
 #define        GOODCOREADDR(x, b) \
        (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \
                IS_ALIGNED((x), SI_CORE_SIZE))
index 3f659e09f1cc3fd9656dcf9fc6a0c5ee1f4ea333..9035cc4d6ff3efd26e1ca8948c9c35b1194487d5 100644 (file)
@@ -457,6 +457,7 @@ struct d11regs {
 /*== maccontrol register ==*/
 #define        MCTL_GMODE              (1U << 31)
 #define        MCTL_DISCARD_PMQ        (1 << 30)
+#define        MCTL_TBTTHOLD           (1 << 28)
 #define        MCTL_WAKE               (1 << 26)
 #define        MCTL_HPS                (1 << 25)
 #define        MCTL_PROMISC            (1 << 24)
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/led.c b/drivers/net/wireless/brcm80211/brcmsmac/led.c
new file mode 100644 (file)
index 0000000..74b17ce
--- /dev/null
@@ -0,0 +1,126 @@
+#include <net/mac80211.h>
+#include <linux/bcma/bcma_driver_chipcommon.h>
+#include <linux/gpio.h>
+
+#include "mac80211_if.h"
+#include "pub.h"
+#include "main.h"
+#include "led.h"
+
+       /* number of leds */
+#define  BRCMS_LED_NO          4
+       /* behavior mask */
+#define  BRCMS_LED_BEH_MASK    0x7f
+       /* activelow (polarity) bit */
+#define  BRCMS_LED_AL_MASK     0x80
+       /* radio enabled */
+#define  BRCMS_LED_RADIO       3
+
+static void brcms_radio_led_ctrl(struct brcms_info *wl, bool state)
+{
+       if (wl->radio_led.gpio == -1)
+               return;
+
+       if (wl->radio_led.active_low)
+               state = !state;
+
+       if (state)
+               gpio_set_value(wl->radio_led.gpio, 1);
+       else
+               gpio_set_value(wl->radio_led.gpio, 0);
+}
+
+
+/* Callback from the LED subsystem. */
+static void brcms_led_brightness_set(struct led_classdev *led_dev,
+                                  enum led_brightness brightness)
+{
+       struct brcms_info *wl = container_of(led_dev,
+               struct brcms_info, led_dev);
+       brcms_radio_led_ctrl(wl, brightness);
+}
+
+void brcms_led_unregister(struct brcms_info *wl)
+{
+       if (wl->led_dev.dev)
+               led_classdev_unregister(&wl->led_dev);
+       if (wl->radio_led.gpio != -1)
+               gpio_free(wl->radio_led.gpio);
+}
+
+int brcms_led_register(struct brcms_info *wl)
+{
+       int i, err;
+       struct brcms_led *radio_led = &wl->radio_led;
+       /* get CC core */
+       struct bcma_drv_cc *cc_drv  = &wl->wlc->hw->d11core->bus->drv_cc;
+       struct gpio_chip *bcma_gpio = &cc_drv->gpio;
+       struct ssb_sprom *sprom = &wl->wlc->hw->d11core->bus->sprom;
+       u8 *leds[] = { &sprom->gpio0,
+               &sprom->gpio1,
+               &sprom->gpio2,
+               &sprom->gpio3 };
+       unsigned gpio = -1;
+       bool active_low = false;
+
+       /* none by default */
+       radio_led->gpio = -1;
+       radio_led->active_low = false;
+
+       if (!bcma_gpio || !gpio_is_valid(bcma_gpio->base))
+               return -ENODEV;
+
+       /* find radio enabled LED */
+       for (i = 0; i < BRCMS_LED_NO; i++) {
+               u8 led = *leds[i];
+               if ((led & BRCMS_LED_BEH_MASK) == BRCMS_LED_RADIO) {
+                       gpio = bcma_gpio->base + i;
+                       if (led & BRCMS_LED_AL_MASK)
+                               active_low = true;
+                       break;
+               }
+       }
+
+       if (gpio == -1 || !gpio_is_valid(gpio))
+               return -ENODEV;
+
+       /* request and configure LED gpio */
+       err = gpio_request_one(gpio,
+                               active_low ? GPIOF_OUT_INIT_HIGH
+                                       : GPIOF_OUT_INIT_LOW,
+                               "radio on");
+       if (err) {
+               wiphy_err(wl->wiphy, "requesting led gpio %d failed (err: %d)\n",
+                         gpio, err);
+               return err;
+       }
+       err = gpio_direction_output(gpio, 1);
+       if (err) {
+               wiphy_err(wl->wiphy, "cannot set led gpio %d to output (err: %d)\n",
+                         gpio, err);
+               return err;
+       }
+
+       snprintf(wl->radio_led.name, sizeof(wl->radio_led.name),
+                "brcmsmac-%s:radio", wiphy_name(wl->wiphy));
+
+       wl->led_dev.name = wl->radio_led.name;
+       wl->led_dev.default_trigger =
+               ieee80211_get_radio_led_name(wl->pub->ieee_hw);
+       wl->led_dev.brightness_set = brcms_led_brightness_set;
+       err = led_classdev_register(wiphy_dev(wl->wiphy), &wl->led_dev);
+
+       if (err) {
+               wiphy_err(wl->wiphy, "cannot register led device: %s (err: %d)\n",
+                         wl->radio_led.name, err);
+               return err;
+       }
+
+       wiphy_info(wl->wiphy, "registered radio enabled led device: %s gpio: %d\n",
+                  wl->radio_led.name,
+                  gpio);
+       radio_led->gpio = gpio;
+       radio_led->active_low = active_low;
+
+       return 0;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/led.h b/drivers/net/wireless/brcm80211/brcmsmac/led.h
new file mode 100644 (file)
index 0000000..17a0b1f
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 _BRCM_LED_H_
+#define _BRCM_LED_H_
+struct brcms_led {
+       char name[32];
+       unsigned gpio;
+       bool active_low;
+};
+
+#ifdef CONFIG_BCMA_DRIVER_GPIO
+void brcms_led_unregister(struct brcms_info *wl);
+int brcms_led_register(struct brcms_info *wl);
+#else
+static inline void brcms_led_unregister(struct brcms_info *wl) {};
+static inline int brcms_led_register(struct brcms_info *wl)
+{
+       return -ENOTSUPP;
+};
+#endif
+
+#endif /* _BRCM_LED_H_ */
index e2340b231aa163cfba03f8b8f90c0822003a385d..c837be242cba6a90775365710e10f25f6dec675c 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010 Broadcom Corporation
+ * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -34,6 +35,7 @@
 #include "mac80211_if.h"
 #include "main.h"
 #include "debug.h"
+#include "led.h"
 
 #define N_TX_QUEUES    4 /* #tx queues on mac80211<->driver interface */
 #define BRCMS_FLUSH_TIMEOUT    500 /* msec */
@@ -487,18 +489,26 @@ brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
        struct brcms_info *wl = hw->priv;
 
-       /* Just STA for now */
-       if (vif->type != NL80211_IFTYPE_STATION) {
+       /* Just STA, AP and ADHOC for now */
+       if (vif->type != NL80211_IFTYPE_STATION &&
+           vif->type != NL80211_IFTYPE_AP &&
+           vif->type != NL80211_IFTYPE_ADHOC) {
                brcms_err(wl->wlc->hw->d11core,
-                         "%s: Attempt to add type %d, only STA for now\n",
+                         "%s: Attempt to add type %d, only STA, AP and AdHoc for now\n",
                          __func__, vif->type);
                return -EOPNOTSUPP;
        }
 
        spin_lock_bh(&wl->lock);
-       memcpy(wl->pub->cur_etheraddr, vif->addr, sizeof(vif->addr));
        wl->mute_tx = false;
        brcms_c_mute(wl->wlc, false);
+       if (vif->type == NL80211_IFTYPE_STATION)
+               brcms_c_start_station(wl->wlc, vif->addr);
+       else if (vif->type == NL80211_IFTYPE_AP)
+               brcms_c_start_ap(wl->wlc, vif->addr, vif->bss_conf.bssid,
+                                vif->bss_conf.ssid, vif->bss_conf.ssid_len);
+       else if (vif->type == NL80211_IFTYPE_ADHOC)
+               brcms_c_start_adhoc(wl->wlc, vif->addr);
        spin_unlock_bh(&wl->lock);
 
        return 0;
@@ -650,14 +660,43 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
                brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid);
                spin_unlock_bh(&wl->lock);
        }
-       if (changed & BSS_CHANGED_BEACON)
+       if (changed & BSS_CHANGED_SSID) {
+               /* BSSID changed, for whatever reason (IBSS and managed mode) */
+               spin_lock_bh(&wl->lock);
+               brcms_c_set_ssid(wl->wlc, info->ssid, info->ssid_len);
+               spin_unlock_bh(&wl->lock);
+       }
+       if (changed & BSS_CHANGED_BEACON) {
                /* Beacon data changed, retrieve new beacon (beaconing modes) */
-               brcms_err(core, "%s: beacon changed\n", __func__);
+               struct sk_buff *beacon;
+               u16 tim_offset = 0;
+
+               spin_lock_bh(&wl->lock);
+               beacon = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL);
+               brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset,
+                                      info->dtim_period);
+               spin_unlock_bh(&wl->lock);
+       }
+
+       if (changed & BSS_CHANGED_AP_PROBE_RESP) {
+               struct sk_buff *probe_resp;
+
+               spin_lock_bh(&wl->lock);
+               probe_resp = ieee80211_proberesp_get(hw, vif);
+               brcms_c_set_new_probe_resp(wl->wlc, probe_resp);
+               spin_unlock_bh(&wl->lock);
+       }
 
        if (changed & BSS_CHANGED_BEACON_ENABLED) {
                /* Beaconing should be enabled/disabled (beaconing modes) */
                brcms_err(core, "%s: Beacon enabled: %s\n", __func__,
                          info->enable_beacon ? "true" : "false");
+               if (info->enable_beacon &&
+                   hw->wiphy->flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) {
+                       brcms_c_enable_probe_resp(wl->wlc, true);
+               } else {
+                       brcms_c_enable_probe_resp(wl->wlc, false);
+               }
        }
 
        if (changed & BSS_CHANGED_CQM) {
@@ -855,7 +894,7 @@ static bool brcms_tx_flush_completed(struct brcms_info *wl)
        return result;
 }
 
-static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop)
+static void brcms_ops_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct brcms_info *wl = hw->priv;
        int ret;
@@ -870,6 +909,28 @@ static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop)
                           "ret=%d\n", jiffies_to_msecs(ret));
 }
 
+static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct brcms_info *wl = hw->priv;
+       u64 tsf;
+
+       spin_lock_bh(&wl->lock);
+       tsf = brcms_c_tsf_get(wl->wlc);
+       spin_unlock_bh(&wl->lock);
+
+       return tsf;
+}
+
+static void brcms_ops_set_tsf(struct ieee80211_hw *hw,
+                          struct ieee80211_vif *vif, u64 tsf)
+{
+       struct brcms_info *wl = hw->priv;
+
+       spin_lock_bh(&wl->lock);
+       brcms_c_tsf_set(wl->wlc, tsf);
+       spin_unlock_bh(&wl->lock);
+}
+
 static const struct ieee80211_ops brcms_ops = {
        .tx = brcms_ops_tx,
        .start = brcms_ops_start,
@@ -886,6 +947,8 @@ static const struct ieee80211_ops brcms_ops = {
        .ampdu_action = brcms_ops_ampdu_action,
        .rfkill_poll = brcms_ops_rfkill_poll,
        .flush = brcms_ops_flush,
+       .get_tsf = brcms_ops_get_tsf,
+       .set_tsf = brcms_ops_set_tsf,
 };
 
 void brcms_dpc(unsigned long data)
@@ -1004,7 +1067,16 @@ static int ieee_hw_init(struct ieee80211_hw *hw)
 
        /* channel change time is dependent on chip and band  */
        hw->channel_change_time = 7 * 1000;
-       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                    BIT(NL80211_IFTYPE_AP) |
+                                    BIT(NL80211_IFTYPE_ADHOC);
+
+       /*
+        * deactivate sending probe responses by ucude, because this will
+        * cause problems when WPS is used.
+        *
+        * hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+        */
 
        hw->rate_control_algorithm = "minstrel_ht";
 
@@ -1151,6 +1223,8 @@ static int brcms_bcma_probe(struct bcma_device *pdev)
                pr_err("%s: brcms_attach failed!\n", __func__);
                return -ENODEV;
        }
+       brcms_led_register(wl);
+
        return 0;
 }
 
index 947ccacf43e6f498dacfaceed5c3853c00c99d96..4090032e81a29f5d4e9aaf0b9258ae8d8c80bfcc 100644 (file)
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/workqueue.h>
+#include <linux/leds.h>
 
 #include "ucode_loader.h"
+#include "led.h"
 /*
  * Starting index for 5G rates in the
  * legacy rate table.
@@ -81,6 +83,8 @@ struct brcms_info {
        struct wiphy *wiphy;
        struct brcms_ucode ucode;
        bool mute_tx;
+       struct brcms_led radio_led;
+       struct led_classdev led_dev;
 };
 
 /* misc callbacks */
index 8ef02dca8f8cab02eca81a71de91bac67612451b..59d438409dfbe144c20dba2b71b8094859d97dd0 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010 Broadcom Corporation
+ * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -448,6 +449,10 @@ static void brcms_c_detach_mfree(struct brcms_c_info *wlc)
        kfree(wlc->corestate);
        kfree(wlc->hw->bandstate[0]);
        kfree(wlc->hw);
+       if (wlc->beacon)
+               dev_kfree_skb_any(wlc->beacon);
+       if (wlc->probe_resp)
+               dev_kfree_skb_any(wlc->probe_resp);
 
        /* free the wlc */
        kfree(wlc);
@@ -1069,7 +1074,7 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
 
 static void brcms_c_tbtt(struct brcms_c_info *wlc)
 {
-       if (!wlc->bsscfg->BSS)
+       if (wlc->bsscfg->type == BRCMS_TYPE_ADHOC)
                /*
                 * DirFrmQ is now valid...defer setting until end
                 * of ATIM window
@@ -2163,6 +2168,32 @@ void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode)
        }
 }
 
+void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr)
+{
+       memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
+       wlc->bsscfg->type = BRCMS_TYPE_STATION;
+}
+
+void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid,
+                     u8 *ssid, size_t ssid_len)
+{
+       brcms_c_set_ssid(wlc, ssid, ssid_len);
+
+       memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
+       memcpy(wlc->bsscfg->BSSID, bssid, sizeof(wlc->bsscfg->BSSID));
+       wlc->bsscfg->type = BRCMS_TYPE_AP;
+
+       brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, MCTL_AP | MCTL_INFRA);
+}
+
+void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr)
+{
+       memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
+       wlc->bsscfg->type = BRCMS_TYPE_ADHOC;
+
+       brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, 0);
+}
+
 /* Initialize GPIOs that are controlled by D11 core */
 static void brcms_c_gpio_init(struct brcms_c_info *wlc)
 {
@@ -3043,8 +3074,6 @@ static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail)
  */
 static bool brcms_c_ps_allowed(struct brcms_c_info *wlc)
 {
-       struct brcms_bss_cfg *cfg = wlc->bsscfg;
-
        /* disallow PS when one of the following global conditions meets */
        if (!wlc->pub->associated)
                return false;
@@ -3053,16 +3082,11 @@ static bool brcms_c_ps_allowed(struct brcms_c_info *wlc)
        if (wlc->filter_flags & FIF_PROMISC_IN_BSS)
                return false;
 
-       if (cfg->associated) {
-               /*
-                * disallow PS when one of the following
-                * bsscfg specific conditions meets
-                */
-               if (!cfg->BSS)
-                       return false;
+       if (wlc->bsscfg->type == BRCMS_TYPE_AP)
+               return false;
 
+       if (wlc->bsscfg->type == BRCMS_TYPE_ADHOC)
                return false;
-       }
 
        return true;
 }
@@ -3771,7 +3795,7 @@ static int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg)
        struct brcms_c_info *wlc = bsscfg->wlc;
 
        /* enter the MAC addr into the RXE match registers */
-       brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, bsscfg->cur_etheraddr);
+       brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, wlc->pub->cur_etheraddr);
 
        brcms_c_ampdu_macaddr_upd(wlc);
 
@@ -3787,6 +3811,15 @@ static void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg)
        brcms_c_set_addrmatch(bsscfg->wlc, RCM_BSSID_OFFSET, bsscfg->BSSID);
 }
 
+void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len)
+{
+       u8 len = min_t(u8, sizeof(wlc->bsscfg->SSID), ssid_len);
+       memset(wlc->bsscfg->SSID, 0, sizeof(wlc->bsscfg->SSID));
+
+       memcpy(wlc->bsscfg->SSID, ssid, len);
+       wlc->bsscfg->SSID_len = len;
+}
+
 static void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot)
 {
        wlc_hw->shortslot = shortslot;
@@ -3821,7 +3854,7 @@ static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec)
        if (wlc->home_chanspec != chanspec) {
                wlc->home_chanspec = chanspec;
 
-               if (wlc->bsscfg->associated)
+               if (wlc->pub->associated)
                        wlc->bsscfg->current_bss->chanspec = chanspec;
        }
 }
@@ -4091,10 +4124,14 @@ void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci,
                                          *shm_entry++);
        }
 
-       if (suspend) {
+       if (suspend)
                brcms_c_suspend_mac_and_wait(wlc);
+
+       brcms_c_update_beacon(wlc);
+       brcms_c_update_probe_resp(wlc, false);
+
+       if (suspend)
                brcms_c_enable_mac(wlc);
-       }
 }
 
 static void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend)
@@ -4332,7 +4369,6 @@ static void brcms_c_info_init(struct brcms_c_info *wlc, int unit)
 
        /* WME QoS mode is Auto by default */
        wlc->pub->_ampdu = AMPDU_AGG_HOST;
-       wlc->pub->bcmerror = 0;
 }
 
 static uint brcms_c_attach_module(struct brcms_c_info *wlc)
@@ -5072,8 +5108,8 @@ int brcms_c_up(struct brcms_c_info *wlc)
                                struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
                                mboolset(wlc->pub->radio_disabled,
                                         WL_RADIO_HW_DISABLE);
-
-                               if (bsscfg->enable && bsscfg->BSS)
+                               if (bsscfg->type == BRCMS_TYPE_STATION ||
+                                   bsscfg->type == BRCMS_TYPE_ADHOC)
                                        brcms_err(wlc->hw->d11core,
                                                  "wl%d: up: rfdisable -> "
                                                  "bsscfg_disable()\n",
@@ -5434,7 +5470,7 @@ static void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc)
        u8 r;
        bool war = false;
 
-       if (wlc->bsscfg->associated)
+       if (wlc->pub->associated)
                r = wlc->bsscfg->current_bss->rateset.rates[0];
        else
                r = wlc->default_bss->rateset.rates[0];
@@ -5528,7 +5564,7 @@ int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs)
        /* merge rateset coming in with the current mcsset */
        if (wlc->pub->_n_enab & SUPPORT_11N) {
                struct brcms_bss_info *mcsset_bss;
-               if (wlc->bsscfg->associated)
+               if (wlc->pub->associated)
                        mcsset_bss = wlc->bsscfg->current_bss;
                else
                        mcsset_bss = wlc->default_bss;
@@ -5543,12 +5579,36 @@ int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs)
        return bcmerror;
 }
 
+static void brcms_c_time_lock(struct brcms_c_info *wlc)
+{
+       bcma_set32(wlc->hw->d11core, D11REGOFFS(maccontrol), MCTL_TBTTHOLD);
+       /* Commit the write */
+       bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol));
+}
+
+static void brcms_c_time_unlock(struct brcms_c_info *wlc)
+{
+       bcma_mask32(wlc->hw->d11core, D11REGOFFS(maccontrol), ~MCTL_TBTTHOLD);
+       /* Commit the write */
+       bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol));
+}
+
 int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period)
 {
+       u32 bcnint_us;
+
        if (period == 0)
                return -EINVAL;
 
        wlc->default_bss->beacon_period = period;
+
+       bcnint_us = period << 10;
+       brcms_c_time_lock(wlc);
+       bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfprep),
+                    (bcnint_us << CFPREP_CBI_SHIFT));
+       bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfpstart), bcnint_us);
+       brcms_c_time_unlock(wlc);
+
        return 0;
 }
 
@@ -7291,72 +7351,110 @@ brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len)
        }
 }
 
-/*     Max buffering needed for beacon template/prb resp template is 142 bytes.
- *
- *     PLCP header is 6 bytes.
- *     802.11 A3 header is 24 bytes.
- *     Max beacon frame body template length is 112 bytes.
- *     Max probe resp frame body template length is 110 bytes.
- *
- *      *len on input contains the max length of the packet available.
- *
- *     The *len value is set to the number of bytes in buf used, and starts
- *     with the PLCP and included up to, but not including, the 4 byte FCS.
- */
-static void
-brcms_c_bcn_prb_template(struct brcms_c_info *wlc, u16 type,
-                        u32 bcn_rspec,
-                        struct brcms_bss_cfg *cfg, u16 *buf, int *len)
+int brcms_c_get_header_len(void)
 {
-       static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
-       struct cck_phy_hdr *plcp;
-       struct ieee80211_mgmt *h;
-       int hdr_len, body_len;
-
-       hdr_len = D11_PHY_HDR_LEN + DOT11_MAC_HDR_LEN;
+       return TXOFF;
+}
 
-       /* calc buffer size provided for frame body */
-       body_len = *len - hdr_len;
-       /* return actual size */
-       *len = hdr_len + body_len;
+static void brcms_c_beacon_write(struct brcms_c_info *wlc,
+                                struct sk_buff *beacon, u16 tim_offset,
+                                u16 dtim_period, bool bcn0, bool bcn1)
+{
+       size_t len;
+       struct ieee80211_tx_info *tx_info;
+       struct brcms_hardware *wlc_hw = wlc->hw;
+       struct ieee80211_hw *ieee_hw = brcms_c_pub(wlc)->ieee_hw;
 
-       /* format PHY and MAC headers */
-       memset(buf, 0, hdr_len);
+       /* Get tx_info */
+       tx_info = IEEE80211_SKB_CB(beacon);
 
-       plcp = (struct cck_phy_hdr *) buf;
+       len = min_t(size_t, beacon->len, BCN_TMPL_LEN);
+       wlc->bcn_rspec = ieee80211_get_tx_rate(ieee_hw, tx_info)->hw_value;
 
-       /*
-        * PLCP for Probe Response frames are filled in from
-        * core's rate table
-        */
-       if (type == IEEE80211_STYPE_BEACON)
-               /* fill in PLCP */
-               brcms_c_compute_plcp(wlc, bcn_rspec,
-                                (DOT11_MAC_HDR_LEN + body_len + FCS_LEN),
-                                (u8 *) plcp);
+       brcms_c_compute_plcp(wlc, wlc->bcn_rspec,
+                            len + FCS_LEN - D11_PHY_HDR_LEN, beacon->data);
 
        /* "Regular" and 16 MBSS but not for 4 MBSS */
        /* Update the phytxctl for the beacon based on the rspec */
-       brcms_c_beacon_phytxctl_txant_upd(wlc, bcn_rspec);
+       brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec);
 
-       h = (struct ieee80211_mgmt *)&plcp[1];
+       if (bcn0) {
+               /* write the probe response into the template region */
+               brcms_b_write_template_ram(wlc_hw, T_BCN0_TPL_BASE,
+                                           (len + 3) & ~3, beacon->data);
 
-       /* fill in 802.11 header */
-       h->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | type);
+               /* write beacon length to SCR */
+               brcms_b_write_shm(wlc_hw, M_BCN0_FRM_BYTESZ, (u16) len);
+       }
+       if (bcn1) {
+               /* write the probe response into the template region */
+               brcms_b_write_template_ram(wlc_hw, T_BCN1_TPL_BASE,
+                                           (len + 3) & ~3, beacon->data);
 
-       /* DUR is 0 for multicast bcn, or filled in by MAC for prb resp */
-       /* A1 filled in by MAC for prb resp, broadcast for bcn */
-       if (type == IEEE80211_STYPE_BEACON)
-               memcpy(&h->da, &ether_bcast, ETH_ALEN);
-       memcpy(&h->sa, &cfg->cur_etheraddr, ETH_ALEN);
-       memcpy(&h->bssid, &cfg->BSSID, ETH_ALEN);
+               /* write beacon length to SCR */
+               brcms_b_write_shm(wlc_hw, M_BCN1_FRM_BYTESZ, (u16) len);
+       }
 
-       /* SEQ filled in by MAC */
+       if (tim_offset != 0) {
+               brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON,
+                                 tim_offset + D11B_PHY_HDR_LEN);
+               brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, dtim_period);
+       } else {
+               brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON,
+                                 len + D11B_PHY_HDR_LEN);
+               brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, 0);
+       }
 }
 
-int brcms_c_get_header_len(void)
+static void brcms_c_update_beacon_hw(struct brcms_c_info *wlc,
+                                    struct sk_buff *beacon, u16 tim_offset,
+                                    u16 dtim_period)
 {
-       return TXOFF;
+       struct brcms_hardware *wlc_hw = wlc->hw;
+       struct bcma_device *core = wlc_hw->d11core;
+
+       /* Hardware beaconing for this config */
+       u32 both_valid = MCMD_BCN0VLD | MCMD_BCN1VLD;
+
+       /* Check if both templates are in use, if so sched. an interrupt
+        *      that will call back into this routine
+        */
+       if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid)
+               /* clear any previous status */
+               bcma_write32(core, D11REGOFFS(macintstatus), MI_BCNTPL);
+
+       if (wlc->beacon_template_virgin) {
+               wlc->beacon_template_virgin = false;
+               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true,
+                                    true);
+               /* mark beacon0 valid */
+               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD);
+               return;
+       }
+
+       /* Check that after scheduling the interrupt both of the
+        *      templates are still busy. if not clear the int. & remask
+        */
+       if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) {
+               wlc->defmacintmask |= MI_BCNTPL;
+               return;
+       }
+
+       if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN0VLD)) {
+               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true,
+                                    false);
+               /* mark beacon0 valid */
+               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD);
+               return;
+       }
+       if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN1VLD)) {
+               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period,
+                                    false, true);
+               /* mark beacon0 valid */
+               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN1VLD);
+               return;
+       }
+       return;
 }
 
 /*
@@ -7366,9 +7464,57 @@ void brcms_c_update_beacon(struct brcms_c_info *wlc)
 {
        struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
 
-       if (bsscfg->up && !bsscfg->BSS)
+       if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP ||
+                            bsscfg->type == BRCMS_TYPE_ADHOC)) {
                /* Clear the soft intmask */
                wlc->defmacintmask &= ~MI_BCNTPL;
+               if (!wlc->beacon)
+                       return;
+               brcms_c_update_beacon_hw(wlc, wlc->beacon,
+                                        wlc->beacon_tim_offset,
+                                        wlc->beacon_dtim_period);
+       }
+}
+
+void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon,
+                           u16 tim_offset, u16 dtim_period)
+{
+       if (!beacon)
+               return;
+       if (wlc->beacon)
+               dev_kfree_skb_any(wlc->beacon);
+       wlc->beacon = beacon;
+
+       /* add PLCP */
+       skb_push(wlc->beacon, D11_PHY_HDR_LEN);
+       wlc->beacon_tim_offset = tim_offset;
+       wlc->beacon_dtim_period = dtim_period;
+       brcms_c_update_beacon(wlc);
+}
+
+void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc,
+                               struct sk_buff *probe_resp)
+{
+       if (!probe_resp)
+               return;
+       if (wlc->probe_resp)
+               dev_kfree_skb_any(wlc->probe_resp);
+       wlc->probe_resp = probe_resp;
+
+       /* add PLCP */
+       skb_push(wlc->probe_resp, D11_PHY_HDR_LEN);
+       brcms_c_update_probe_resp(wlc, false);
+}
+
+void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable)
+{
+       /*
+        * prevent ucode from sending probe responses by setting the timeout
+        * to 1, it can not send it in that time frame.
+        */
+       wlc->prb_resp_timeout = enable ? BRCMS_PRB_RESP_TIMEOUT : 1;
+       brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout);
+       /* TODO: if (enable) => also deactivate receiving of probe request */
 }
 
 /* Write ssid into shared memory */
@@ -7390,30 +7536,19 @@ brcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg)
 static void
 brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc,
                              struct brcms_bss_cfg *cfg,
+                             struct sk_buff *probe_resp,
                              bool suspend)
 {
-       u16 *prb_resp;
-       int len = BCN_TMPL_LEN;
-
-       prb_resp = kmalloc(BCN_TMPL_LEN, GFP_ATOMIC);
-       if (!prb_resp)
-               return;
-
-       /*
-        * write the probe response to hardware, or save in
-        * the config structure
-        */
+       int len;
 
-       /* create the probe response template */
-       brcms_c_bcn_prb_template(wlc, IEEE80211_STYPE_PROBE_RESP, 0,
-                                cfg, prb_resp, &len);
+       len = min_t(size_t, probe_resp->len, BCN_TMPL_LEN);
 
        if (suspend)
                brcms_c_suspend_mac_and_wait(wlc);
 
        /* write the probe response into the template region */
        brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE,
-                                   (len + 3) & ~3, prb_resp);
+                                   (len + 3) & ~3, probe_resp->data);
 
        /* write the length of the probe response frame (+PLCP/-FCS) */
        brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len);
@@ -7427,13 +7562,11 @@ brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc,
         * PLCP header for the call to brcms_c_mod_prb_rsp_rate_table()
         * by subtracting the PLCP len and adding the FCS.
         */
-       len += (-D11_PHY_HDR_LEN + FCS_LEN);
-       brcms_c_mod_prb_rsp_rate_table(wlc, (u16) len);
+       brcms_c_mod_prb_rsp_rate_table(wlc,
+                                     (u16)len + FCS_LEN - D11_PHY_HDR_LEN);
 
        if (suspend)
                brcms_c_enable_mac(wlc);
-
-       kfree(prb_resp);
 }
 
 void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend)
@@ -7441,8 +7574,13 @@ void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend)
        struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
 
        /* update AP or IBSS probe responses */
-       if (bsscfg->up && !bsscfg->BSS)
-               brcms_c_bss_update_probe_resp(wlc, bsscfg, suspend);
+       if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP ||
+                            bsscfg->type == BRCMS_TYPE_ADHOC)) {
+               if (!wlc->probe_resp)
+                       return;
+               brcms_c_bss_update_probe_resp(wlc, bsscfg, wlc->probe_resp,
+                                             suspend);
+       }
 }
 
 int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
@@ -7481,7 +7619,6 @@ void brcms_c_scan_stop(struct brcms_c_info *wlc)
 void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state)
 {
        wlc->pub->associated = state;
-       wlc->bsscfg->associated = state;
 }
 
 /*
@@ -7526,6 +7663,36 @@ void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval)
                brcms_c_bcn_li_upd(wlc);
 }
 
+u64 brcms_c_tsf_get(struct brcms_c_info *wlc)
+{
+       u32 tsf_h, tsf_l;
+       u64 tsf;
+
+       brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h);
+
+       tsf = tsf_h;
+       tsf <<= 32;
+       tsf |= tsf_l;
+
+       return tsf;
+}
+
+void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf)
+{
+       u32 tsf_h, tsf_l;
+
+       brcms_c_time_lock(wlc);
+
+       tsf_l = tsf;
+       tsf_h = (tsf >> 32);
+
+       /* read the tsf timer low, then high to get an atomic read */
+       bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerlow), tsf_l);
+       bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerhigh), tsf_h);
+
+       brcms_c_time_unlock(wlc);
+}
+
 int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr)
 {
        uint qdbm;
@@ -7737,6 +7904,10 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded)
                brcms_rfkill_set_hw_state(wlc->wl);
        }
 
+       /* BCN template is available */
+       if (macintstatus & MI_BCNTPL)
+               brcms_c_update_beacon(wlc);
+
        /* it isn't done and needs to be resched if macintstatus is non-zero */
        return wlc->macintstatus != 0;
 
@@ -7765,7 +7936,7 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx)
        brcms_c_set_bssid(wlc->bsscfg);
 
        /* Update tsf_cfprep if associated and up */
-       if (wlc->pub->associated && wlc->bsscfg->up) {
+       if (wlc->pub->associated && wlc->pub->up) {
                u32 bi;
 
                /* get beacon period and convert to uS */
@@ -7810,9 +7981,14 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx)
 
        /* read the ucode version if we have not yet done so */
        if (wlc->ucode_rev == 0) {
-               wlc->ucode_rev =
-                   brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR) << NBITS(u16);
-               wlc->ucode_rev |= brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR);
+               u16 rev;
+               u16 patch;
+
+               rev = brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR);
+               patch = brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR);
+               wlc->ucode_rev = (rev << NBITS(u16)) | patch;
+               snprintf(wlc->wiphy->fw_version,
+                        sizeof(wlc->wiphy->fw_version), "%u.%u", rev, patch);
        }
 
        /* ..now really unleash hell (allow the MAC out of suspend) */
@@ -7868,6 +8044,7 @@ brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit,
        pub->unit = unit;
        pub->_piomode = piomode;
        wlc->bandinit_pending = false;
+       wlc->beacon_template_virgin = true;
 
        /* populate struct brcms_c_info with default values  */
        brcms_c_info_init(wlc, unit);
index fb447747c2c6e9975b681a7d42f0dc6ae5ad00bd..b5d7a38b53fe3d8baf6f8cbc28ba8cef0aaa2b8a 100644 (file)
@@ -492,6 +492,8 @@ struct brcms_c_info {
        bool radio_monitor;
        bool going_down;
 
+       bool beacon_template_virgin;
+
        struct brcms_timer *wdtimer;
        struct brcms_timer *radio_timer;
 
@@ -561,6 +563,11 @@ struct brcms_c_info {
 
        struct wiphy *wiphy;
        struct scb pri_scb;
+
+       struct sk_buff *beacon;
+       u16 beacon_tim_offset;
+       u16 beacon_dtim_period;
+       struct sk_buff *probe_resp;
 };
 
 /* antsel module specific state */
@@ -576,14 +583,17 @@ struct antsel_info {
        struct brcms_antselcfg antcfg_cur; /* current antenna config (auto) */
 };
 
+enum brcms_bss_type {
+       BRCMS_TYPE_STATION,
+       BRCMS_TYPE_AP,
+       BRCMS_TYPE_ADHOC,
+};
+
 /*
  * BSS configuration state
  *
  * wlc: wlc to which this bsscfg belongs to.
- * up: is this configuration up operational
- * enable: is this configuration enabled
- * associated: is BSS in ASSOCIATED state
- * BSS: infraustructure or adhoc
+ * type: interface type
  * SSID_len: the length of SSID
  * SSID: SSID string
  *
@@ -599,14 +609,10 @@ struct antsel_info {
  */
 struct brcms_bss_cfg {
        struct brcms_c_info *wlc;
-       bool up;
-       bool enable;
-       bool associated;
-       bool BSS;
+       enum brcms_bss_type type;
        u8 SSID_len;
        u8 SSID[IEEE80211_MAX_SSID_LEN];
        u8 BSSID[ETH_ALEN];
-       u8 cur_etheraddr[ETH_ALEN];
        struct brcms_bss_info *current_bss;
 };
 
@@ -631,7 +637,6 @@ extern u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only,
 extern void brcms_c_inval_dma_pkts(struct brcms_hardware *hw,
                               struct ieee80211_sta *sta,
                               void (*dma_callback_fn));
-extern void brcms_c_update_beacon(struct brcms_c_info *wlc);
 extern void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend);
 extern int brcms_c_set_nmode(struct brcms_c_info *wlc);
 extern void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc,
index 91937c5025ceaf5f2a00738c9800ee1247984da4..b0fd807f2b2b6bb18e8dcabb80164fdb6578d16d 100644 (file)
@@ -198,8 +198,6 @@ u16 read_radio_reg(struct brcms_phy *pi, u16 addr)
 
 void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val)
 {
-       struct si_info *sii = container_of(pi->sh->sih, struct si_info, pub);
-
        if ((D11REV_GE(pi->sh->corerev, 24)) ||
            (D11REV_IS(pi->sh->corerev, 22)
             && (pi->pubpi.phy_type != PHY_TYPE_SSN))) {
@@ -211,7 +209,7 @@ void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val)
                bcma_write16(pi->d11core, D11REGOFFS(phy4wdatalo), val);
        }
 
-       if ((sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) &&
+       if ((pi->d11core->bus->hosttype == BCMA_HOSTTYPE_PCI) &&
            (++pi->phy_wreg >= pi->phy_wreg_limit)) {
                (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol));
                pi->phy_wreg = 0;
@@ -297,10 +295,8 @@ void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val)
        if (addr == 0x72)
                (void)bcma_read16(pi->d11core, D11REGOFFS(phyregdata));
 #else
-       struct si_info *sii = container_of(pi->sh->sih, struct si_info, pub);
-
        bcma_write32(pi->d11core, D11REGOFFS(phyregaddr), addr | (val << 16));
-       if ((sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) &&
+       if ((pi->d11core->bus->hosttype == BCMA_HOSTTYPE_PCI) &&
            (++pi->phy_wreg >= pi->phy_wreg_limit)) {
                pi->phy_wreg = 0;
                (void)bcma_read16(pi->d11core, D11REGOFFS(phyversion));
@@ -374,7 +370,6 @@ struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp)
        if (sh == NULL)
                return NULL;
 
-       sh->sih = shp->sih;
        sh->physhim = shp->physhim;
        sh->unit = shp->unit;
        sh->corerev = shp->corerev;
@@ -2911,29 +2906,24 @@ void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode)
                                mod_phy_reg(pi, 0x44c, (0x1 << 2), (1) << 2);
 
                        }
-                       ai_cc_reg(pi->sh->sih,
-                                 offsetof(struct chipcregs, gpiocontrol),
-                                 ~0x0, 0x0);
-                       ai_cc_reg(pi->sh->sih,
-                                 offsetof(struct chipcregs, gpioout),
-                                 0x40, 0x40);
-                       ai_cc_reg(pi->sh->sih,
-                                 offsetof(struct chipcregs, gpioouten),
-                                 0x40, 0x40);
+
+                       bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc,
+                                                0x0, 0x0);
+                       bcma_chipco_gpio_out(&pi->d11core->bus->drv_cc,
+                                            ~0x40, 0x40);
+                       bcma_chipco_gpio_outen(&pi->d11core->bus->drv_cc,
+                                              ~0x40, 0x40);
                } else {
                        mod_phy_reg(pi, 0x44c, (0x1 << 2), (0) << 2);
 
                        mod_phy_reg(pi, 0x44d, (0x1 << 2), (0) << 2);
 
-                       ai_cc_reg(pi->sh->sih,
-                                 offsetof(struct chipcregs, gpioout),
-                                 0x40, 0x00);
-                       ai_cc_reg(pi->sh->sih,
-                                 offsetof(struct chipcregs, gpioouten),
-                                 0x40, 0x0);
-                       ai_cc_reg(pi->sh->sih,
-                                 offsetof(struct chipcregs, gpiocontrol),
-                                 ~0x0, 0x40);
+                       bcma_chipco_gpio_out(&pi->d11core->bus->drv_cc,
+                                            ~0x40, 0x00);
+                       bcma_chipco_gpio_outen(&pi->d11core->bus->drv_cc,
+                                              ~0x40, 0x00);
+                       bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc,
+                                                0x0, 0x40);
                }
        }
 }
index af00e2c2b2667fd3bc9fbde56d37adfeaffca935..1dc767c31653b29a9458cc08b9d113ab1c6979ad 100644 (file)
@@ -488,7 +488,6 @@ struct lcnphy_cal_results {
 struct shared_phy {
        struct brcms_phy *phy_head;
        uint unit;
-       struct si_pub *sih;
        struct phy_shim_info *physhim;
        uint corerev;
        u32 machwcap;
index 18d37645e2cde25f2d291a94bd51a3e3675db5be..3d6b16ce46878979fd5104d1472fdaa9769b0e7e 100644 (file)
@@ -1595,11 +1595,15 @@ wlc_lcnphy_set_chanspec_tweaks(struct brcms_phy *pi, u16 chanspec)
        if (channel == 1 || channel == 2 || channel == 3 ||
            channel == 4 || channel == 9 ||
            channel == 10 || channel == 11 || channel == 12) {
-               si_pmu_pllcontrol(pi->sh->sih, 0x2, 0xffffffff, 0x03000c04);
-               si_pmu_pllcontrol(pi->sh->sih, 0x3, 0xffffff, 0x0);
-               si_pmu_pllcontrol(pi->sh->sih, 0x4, 0xffffffff, 0x200005c0);
-
-               si_pmu_pllupd(pi->sh->sih);
+               bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x2,
+                                     0x03000c04);
+               bcma_chipco_pll_maskset(&pi->d11core->bus->drv_cc, 0x3,
+                                       ~0x00ffffff, 0x0);
+               bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x4,
+                                     0x200005c0);
+
+               bcma_cc_set32(&pi->d11core->bus->drv_cc, BCMA_CC_PMU_CTL,
+                             BCMA_CC_PMU_CTL_PLL_UPD);
                write_phy_reg(pi, 0x942, 0);
                wlc_lcnphy_txrx_spur_avoidance_mode(pi, false);
                pi_lcn->lcnphy_spurmod = false;
@@ -1607,11 +1611,15 @@ wlc_lcnphy_set_chanspec_tweaks(struct brcms_phy *pi, u16 chanspec)
 
                write_phy_reg(pi, 0x425, 0x5907);
        } else {
-               si_pmu_pllcontrol(pi->sh->sih, 0x2, 0xffffffff, 0x03140c04);
-               si_pmu_pllcontrol(pi->sh->sih, 0x3, 0xffffff, 0x333333);
-               si_pmu_pllcontrol(pi->sh->sih, 0x4, 0xffffffff, 0x202c2820);
-
-               si_pmu_pllupd(pi->sh->sih);
+               bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x2,
+                                     0x03140c04);
+               bcma_chipco_pll_maskset(&pi->d11core->bus->drv_cc, 0x3,
+                                       ~0x00ffffff, 0x333333);
+               bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x4,
+                                     0x202c2820);
+
+               bcma_cc_set32(&pi->d11core->bus->drv_cc, BCMA_CC_PMU_CTL,
+                             BCMA_CC_PMU_CTL_PLL_UPD);
                write_phy_reg(pi, 0x942, 0);
                wlc_lcnphy_txrx_spur_avoidance_mode(pi, true);
 
@@ -4755,9 +4763,10 @@ void wlc_phy_init_lcnphy(struct brcms_phy *pi)
 
        wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec);
 
-       si_pmu_regcontrol(pi->sh->sih, 0, 0xf, 0x9);
+       bcma_chipco_regctl_maskset(&pi->d11core->bus->drv_cc, 0, ~0xf, 0x9);
 
-       si_pmu_chipcontrol(pi->sh->sih, 0, 0xffffffff, 0x03CDDDDD);
+       bcma_chipco_chipctl_maskset(&pi->d11core->bus->drv_cc, 0, 0x0,
+                                   0x03CDDDDD);
 
        if ((pi->sh->boardflags & BFL_FEM)
            && wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
@@ -4968,7 +4977,7 @@ bool wlc_phy_attach_lcnphy(struct brcms_phy *pi)
                pi->hwpwrctrl_capable = true;
        }
 
-       pi->xtalfreq = si_pmu_alp_clock(pi->sh->sih);
+       pi->xtalfreq = bcma_chipco_get_alp_clock(&pi->d11core->bus->drv_cc);
        pi_lcn->lcnphy_papd_rxGnCtrl_init = 0;
 
        pi->pi_fptr.init = wlc_phy_init_lcnphy;
index 65db9b7458dcb75b3f7956b46a8e3d5f321c4287..3e9f5b25be637d0891352be92cea018e8855bfb4 100644 (file)
@@ -19321,14 +19321,13 @@ void wlc_phy_init_nphy(struct brcms_phy *pi)
             (pi->sh->chippkg == BCMA_PKG_ID_BCM4718))) {
                if ((pi->sh->boardflags & BFL_EXTLNA) &&
                    (CHSPEC_IS2G(pi->radio_chanspec)))
-                       ai_cc_reg(pi->sh->sih,
-                                 offsetof(struct chipcregs, chipcontrol),
-                                 0x40, 0x40);
+                       bcma_cc_set32(&pi->d11core->bus->drv_cc,
+                                     BCMA_CC_CHIPCTL, 0x40);
        }
 
        if ((!PHY_IPA(pi)) && (pi->sh->chip == BCMA_CHIP_ID_BCM5357))
-               si_pmu_chipcontrol(pi->sh->sih, 1, CCTRL5357_EXTPA,
-                                  CCTRL5357_EXTPA);
+               bcma_chipco_chipctl_maskset(&pi->d11core->bus->drv_cc, 1,
+                                           ~CCTRL5357_EXTPA, CCTRL5357_EXTPA);
 
        if ((pi->nphy_gband_spurwar2_en) && CHSPEC_IS2G(pi->radio_chanspec) &&
            CHSPEC_IS40(pi->radio_chanspec)) {
@@ -21133,7 +21132,6 @@ wlc_phy_chanspec_nphy_setup(struct brcms_phy *pi, u16 chanspec,
                            const struct nphy_sfo_cfg *ci)
 {
        u16 val;
-       struct si_info *sii = container_of(pi->sh->sih, struct si_info, pub);
 
        val = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand;
        if (CHSPEC_IS5G(chanspec) && !val) {
@@ -21221,11 +21219,11 @@ wlc_phy_chanspec_nphy_setup(struct brcms_phy *pi, u16 chanspec,
 
                if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) ||
                    (pi->sh->chip == BCMA_CHIP_ID_BCM43225)) {
-                       bcma_pmu_spuravoid_pllupdate(&sii->icbus->drv_cc,
+                       bcma_pmu_spuravoid_pllupdate(&pi->d11core->bus->drv_cc,
                                                     spuravoid);
                } else {
                        wlapi_bmac_core_phypll_ctl(pi->sh->physhim, false);
-                       bcma_pmu_spuravoid_pllupdate(&sii->icbus->drv_cc,
+                       bcma_pmu_spuravoid_pllupdate(&pi->d11core->bus->drv_cc,
                                                     spuravoid);
                        wlapi_bmac_core_phypll_ctl(pi->sh->physhim, true);
                }
index 7e9df566c733e13d3e161974e5e659a6bedeeb8d..71b80381f3ad7883c929cd40120f43282f22dd5b 100644 (file)
@@ -115,60 +115,6 @@ u16 si_pmu_fast_pwrup_delay(struct si_pub *sih)
        return (u16) delay;
 }
 
-/* Read/write a chipcontrol reg */
-u32 si_pmu_chipcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val)
-{
-       ai_cc_reg(sih, offsetof(struct chipcregs, chipcontrol_addr), ~0, reg);
-       return ai_cc_reg(sih, offsetof(struct chipcregs, chipcontrol_data),
-                        mask, val);
-}
-
-/* Read/write a regcontrol reg */
-u32 si_pmu_regcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val)
-{
-       ai_cc_reg(sih, offsetof(struct chipcregs, regcontrol_addr), ~0, reg);
-       return ai_cc_reg(sih, offsetof(struct chipcregs, regcontrol_data),
-                        mask, val);
-}
-
-/* Read/write a pllcontrol reg */
-u32 si_pmu_pllcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val)
-{
-       ai_cc_reg(sih, offsetof(struct chipcregs, pllcontrol_addr), ~0, reg);
-       return ai_cc_reg(sih, offsetof(struct chipcregs, pllcontrol_data),
-                        mask, val);
-}
-
-/* PMU PLL update */
-void si_pmu_pllupd(struct si_pub *sih)
-{
-       ai_cc_reg(sih, offsetof(struct chipcregs, pmucontrol),
-                 PCTL_PLL_PLLCTL_UPD, PCTL_PLL_PLLCTL_UPD);
-}
-
-/* query alp/xtal clock frequency */
-u32 si_pmu_alp_clock(struct si_pub *sih)
-{
-       u32 clock = ALP_CLOCK;
-
-       /* bail out with default */
-       if (!(ai_get_cccaps(sih) & CC_CAP_PMU))
-               return clock;
-
-       switch (ai_get_chip_id(sih)) {
-       case BCMA_CHIP_ID_BCM43224:
-       case BCMA_CHIP_ID_BCM43225:
-       case BCMA_CHIP_ID_BCM4313:
-               /* always 20Mhz */
-               clock = 20000 * 1000;
-               break;
-       default:
-               break;
-       }
-
-       return clock;
-}
-
 u32 si_pmu_measure_alpclk(struct si_pub *sih)
 {
        struct si_info *sii = container_of(sih, struct si_info, pub);
index f7cff873578b2cb5f42756a8b09a9427a0d316c2..20e2012d5a3a2d2d83c8b7544f6828d14d3f6f33 100644 (file)
 #include "types.h"
 
 extern u16 si_pmu_fast_pwrup_delay(struct si_pub *sih);
-extern void si_pmu_sprom_enable(struct si_pub *sih, bool enable);
-extern u32 si_pmu_chipcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val);
-extern u32 si_pmu_regcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val);
-extern u32 si_pmu_alp_clock(struct si_pub *sih);
-extern void si_pmu_pllupd(struct si_pub *sih);
-extern u32 si_pmu_pllcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val);
 extern u32 si_pmu_measure_alpclk(struct si_pub *sih);
 
 #endif /* _BRCM_PMU_H_ */
index b0f14b7b8616e63ed5f2ed954ff47eddab7ff0d0..d36ea5e1cc494231e56dd18bb2f0dadc65f4304d 100644 (file)
@@ -164,8 +164,6 @@ struct brcms_pub {
 
        u8 cur_etheraddr[ETH_ALEN];     /* our local ethernet address */
 
-       int bcmerror;           /* last bcm error */
-
        u32 radio_disabled;     /* bit vector for radio disabled reasons */
 
        u16 boardrev;   /* version # of particular board */
@@ -326,10 +324,25 @@ extern void brcms_c_set_shortslot_override(struct brcms_c_info *wlc,
                                    s8 sslot_override);
 extern void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc,
                                        u8 interval);
+extern u64 brcms_c_tsf_get(struct brcms_c_info *wlc);
+extern void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf);
 extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr);
 extern int brcms_c_get_tx_power(struct brcms_c_info *wlc);
 extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc);
 extern void brcms_c_mute(struct brcms_c_info *wlc, bool on);
 extern bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc);
+extern void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr);
+extern void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr,
+                            const u8 *bssid, u8 *ssid, size_t ssid_len);
+extern void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr);
+extern void brcms_c_update_beacon(struct brcms_c_info *wlc);
+extern void brcms_c_set_new_beacon(struct brcms_c_info *wlc,
+                                  struct sk_buff *beacon, u16 tim_offset,
+                                  u16 dtim_period);
+extern void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc,
+                                      struct sk_buff *probe_resp);
+extern void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable);
+extern void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid,
+                            size_t ssid_len);
 
 #endif                         /* _BRCM_PUB_H_ */
index 3e6405e06ac028687e1d12627874491e969d6e94..bf5e50fc21ba8653413c943dd774ae35000d1064 100644 (file)
@@ -116,6 +116,31 @@ struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec)
 }
 EXPORT_SYMBOL(brcmu_pktq_pdeq);
 
+/*
+ * precedence based dequeue with match function. Passing a NULL pointer
+ * for the match function parameter is considered to be a wildcard so
+ * any packet on the queue is returned. In that case it is no different
+ * from brcmu_pktq_pdeq() above.
+ */
+struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec,
+                                     bool (*match_fn)(struct sk_buff *skb,
+                                                      void *arg), void *arg)
+{
+       struct sk_buff_head *q;
+       struct sk_buff *p, *next;
+
+       q = &pq->q[prec].skblist;
+       skb_queue_walk_safe(q, p, next) {
+               if (match_fn == NULL || match_fn(p, arg)) {
+                       skb_unlink(p, q);
+                       pq->len--;
+                       return p;
+               }
+       }
+       return NULL;
+}
+EXPORT_SYMBOL(brcmu_pktq_pdeq_match);
+
 struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec)
 {
        struct sk_buff_head *q;
index 477b92ad3d62c37b9ec99b3e7b48b0b60f388311..898cacb8d01df299f1e4dce5ebc72fc008e5b4f2 100644 (file)
@@ -120,6 +120,10 @@ extern struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
                                      struct sk_buff *p);
 extern struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec);
 extern struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec);
+extern struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec,
+                                            bool (*match_fn)(struct sk_buff *p,
+                                                             void *arg),
+                                            void *arg);
 
 /* packet primitives */
 extern struct sk_buff *brcmu_pkt_buf_get_skb(uint len);
@@ -173,6 +177,29 @@ extern void brcmu_pktq_flush(struct pktq *pq, bool dir,
 /* ip address */
 struct ipv4_addr;
 
+/*
+ * bitfield macros using masking and shift
+ *
+ * remark: the mask parameter should be a shifted mask.
+ */
+static inline void brcmu_maskset32(u32 *var, u32 mask, u8 shift, u32 value)
+{
+       value = (value << shift) & mask;
+       *var = (*var & ~mask) | value;
+}
+static inline u32 brcmu_maskget32(u32 var, u32 mask, u8 shift)
+{
+       return (var & mask) >> shift;
+}
+static inline void brcmu_maskset16(u16 *var, u16 mask, u8 shift, u16 value)
+{
+       value = (value << shift) & mask;
+       *var = (*var & ~mask) | value;
+}
+static inline u16 brcmu_maskget16(u16 var, u16 mask, u8 shift)
+{
+       return (var & mask) >> shift;
+}
 
 /* externs */
 /* format/print */
index c11a290a1edf6c07e38cfd1035f783ddb9918ac2..0505cc065e0d6d18ae3c292ab234a7e8b9761ba8 100644 (file)
@@ -32,8 +32,9 @@
 #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 BRCM_MAX_2G_CHANNEL    CH_MAX_2G_CHANNEL       /* legacy define */
+#define CH_MIN_5G_CHANNEL              34
 
 /* bandstate array indices */
 #define BAND_2G_INDEX          0       /* wlc->bandstate[x] index */
@@ -60,6 +61,7 @@
 #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_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
+
+/* 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_IS20(chspec) \
        (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
 
-#ifndef CHSPEC_IS40
 #define CHSPEC_IS40(chspec) \
        (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
-#endif
+
+#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)
index c353b5f19c8c639480be47121f06c166dfc9f71c..b37a582ccbe7872eca2e5f5f8da14abe32f335e1 100644 (file)
@@ -3477,7 +3477,7 @@ static struct attribute_group il3945_attribute_group = {
        .attrs = il3945_sysfs_entries,
 };
 
-struct ieee80211_ops il3945_mac_ops = {
+static struct ieee80211_ops il3945_mac_ops __read_mostly = {
        .tx = il3945_mac_tx,
        .start = il3945_mac_start,
        .stop = il3945_mac_stop,
index e0b9d7fa5de0d43d74e130b6c91d4ad0078ecf19..dc1e6da9976af0977ae197475694a8f544ae1643 100644 (file)
@@ -2379,10 +2379,8 @@ il3945_hw_set_hw_params(struct il_priv *il)
        il->_3945.shared_virt =
            dma_alloc_coherent(&il->pci_dev->dev, sizeof(struct il3945_shared),
                               &il->_3945.shared_phys, GFP_KERNEL);
-       if (!il->_3945.shared_virt) {
-               IL_ERR("failed to allocate pci memory\n");
+       if (!il->_3945.shared_virt)
                return -ENOMEM;
-       }
 
        il->hw_params.bcast_id = IL3945_BROADCAST_ID;
 
index 1d45075e0d5b51a21e234b44c4603bb0c0481ce6..9a8703def0ba1a1825c264b5e5806003412c5e91 100644 (file)
@@ -150,10 +150,6 @@ struct il3945_frame {
        struct list_head list;
 };
 
-#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
-
 #define SUP_RATE_11A_MAX_NUM_CHANNELS  8
 #define SUP_RATE_11B_MAX_NUM_CHANNELS  4
 #define SUP_RATE_11G_MAX_NUM_CHANNELS  12
index 7941eb3a01665b6e22d63c9d5e658297747935fa..6affa7e8f017e830c9ac96a403182aa4bb215e8e 100644 (file)
@@ -612,7 +612,7 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
 
 /* Called for N_RX (legacy ABG frames), or
  * N_RX_MPDU (HT high-throughput N frames). */
-void
+static void
 il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
 {
        struct ieee80211_hdr *header;
@@ -744,7 +744,7 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
 
 /* 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. */
-void
+static void
 il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb)
 {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
@@ -1250,7 +1250,7 @@ il4965_dump_fh(struct il_priv *il, char **buf, bool display)
        return 0;
 }
 
-void
+static void
 il4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb)
 {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
@@ -1357,7 +1357,7 @@ il4965_accumulative_stats(struct il_priv *il, __le32 * stats)
 }
 #endif
 
-void
+static void
 il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb)
 {
        const int recalib_seconds = 60;
@@ -1399,7 +1399,7 @@ il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb)
                il4965_temperature_calib(il);
 }
 
-void
+static void
 il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb)
 {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
@@ -1921,8 +1921,8 @@ drop_unlock:
 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);
+       ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma,
+                                      GFP_KERNEL);
        if (!ptr->addr)
                return -ENOMEM;
        ptr->size = size;
@@ -2050,7 +2050,7 @@ il4965_txq_ctx_reset(struct il_priv *il)
                il_tx_queue_reset(il, txq_id);
 }
 
-void
+static void
 il4965_txq_ctx_unmap(struct il_priv *il)
 {
        int txq_id;
@@ -2258,7 +2258,7 @@ il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif,
 
        spin_lock_irqsave(&il->sta_lock, flags);
        tid_data = &il->stations[sta_id].tid[tid];
-       *ssn = SEQ_TO_SN(tid_data->seq_number);
+       *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);
@@ -2408,7 +2408,7 @@ il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id)
                /* aggregated HW queue */
                if (txq_id == tid_data->agg.txq_id &&
                    q->read_ptr == q->write_ptr) {
-                       u16 ssn = SEQ_TO_SN(tid_data->seq_number);
+                       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);
@@ -2627,7 +2627,8 @@ il4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr)
 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) & MAX_SN;
+       return le32_to_cpup(&tx_resp->u.status +
+                           tx_resp->frame_count) & IEEE80211_MAX_SN;
 }
 
 static inline u32
@@ -2717,15 +2718,15 @@ il4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg,
                        hdr = (struct ieee80211_hdr *) skb->data;
 
                        sc = le16_to_cpu(hdr->seq_ctrl);
-                       if (idx != (SEQ_TO_SN(sc) & 0xff)) {
+                       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,
-                                      SEQ_TO_SN(sc), hdr->seq_ctrl);
+                                      IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl);
                                return -1;
                        }
 
                        D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx,
-                                  SEQ_TO_SN(sc));
+                                  IEEE80211_SEQ_TO_SN(sc));
 
                        sh = idx - start;
                        if (sh > 64) {
@@ -2895,7 +2896,7 @@ il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags,
  * Handles block-acknowledge notification from device, which reports success
  * of frames sent via aggregation.
  */
-void
+static void
 il4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb)
 {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
@@ -6316,7 +6317,7 @@ il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq,
               scd_retry ? "BA" : "AC", txq_id, tx_fifo_id);
 }
 
-const struct ieee80211_ops il4965_mac_ops = {
+static const struct ieee80211_ops il4965_mac_ops = {
        .tx = il4965_mac_tx,
        .start = il4965_mac_start,
        .stop = il4965_mac_stop,
index e006ea831320ae5a3c3dd15f87a9c00a3eb4d3d8..3613b3a81ad2f17cd6762320929f0eb4d8eeed1a 100644 (file)
@@ -1122,7 +1122,7 @@ il_set_power(struct il_priv *il, struct il_powertable_cmd *cmd)
                               sizeof(struct il_powertable_cmd), cmd);
 }
 
-int
+static int
 il_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force)
 {
        int ret;
@@ -2566,15 +2566,13 @@ il_rx_queue_alloc(struct il_priv *il)
        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);
+       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);
+       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;
 
@@ -2941,10 +2939,9 @@ il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id)
         * shared with device */
        txq->tfds =
            dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL);
-       if (!txq->tfds) {
-               IL_ERR("Fail to alloc TFDs\n");
+       if (!txq->tfds)
                goto error;
-       }
+
        txq->q.id = id;
 
        return 0;
@@ -4704,8 +4701,7 @@ out:
 }
 EXPORT_SYMBOL(il_mac_change_interface);
 
-void
-il_mac_flush(struct ieee80211_hw *hw, bool drop)
+void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct il_priv *il = hw->priv;
        unsigned long timeout = jiffies + msecs_to_jiffies(500);
@@ -4891,7 +4887,7 @@ il_add_beacon_time(struct il_priv *il, u32 base, u32 addon,
 }
 EXPORT_SYMBOL(il_add_beacon_time);
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 
 static int
 il_pci_suspend(struct device *device)
@@ -4942,7 +4938,7 @@ il_pci_resume(struct device *device)
 SIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume);
 EXPORT_SYMBOL(il_pm_ops);
 
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
 
 static void
 il_update_qos(struct il_priv *il)
index 96f2025d936e340dcbc0cb3693f5d38bb8817c0c..f8246f2d88f9baf0cbb006621fbfbc3845966e26 100644 (file)
@@ -541,10 +541,6 @@ struct il_frame {
        struct list_head list;
 };
 
-#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
-
 enum {
        CMD_SYNC = 0,
        CMD_SIZE_NORMAL = 0,
@@ -1724,7 +1720,7 @@ void il_mac_remove_interface(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif);
 int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                            enum nl80211_iftype newtype, bool newp2p);
-void il_mac_flush(struct ieee80211_hw *hw, bool drop);
+void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
 int il_alloc_txq_mem(struct il_priv *il);
 void il_free_txq_mem(struct il_priv *il);
 
@@ -2235,9 +2231,8 @@ il_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc)
                return -EINVAL;
        }
 
-       desc->v_addr =
-           dma_alloc_coherent(&pci_dev->dev, desc->len, &desc->p_addr,
-                              GFP_KERNEL);
+       desc->v_addr = dma_alloc_coherent(&pci_dev->dev, desc->len,
+                                         &desc->p_addr, GFP_KERNEL);
        return (desc->v_addr != NULL) ? 0 : -ENOMEM;
 }
 
index ba319cba3f1eed94c56c2e671f21828f26b147c4..56c2040a955b89263e86930d6dc146f2e869b9fc 100644 (file)
@@ -6,7 +6,6 @@ config IWLWIFI
        select LEDS_CLASS
        select LEDS_TRIGGERS
        select MAC80211_LEDS
-       select IWLDVM
        ---help---
          Select to build the driver supporting the:
 
@@ -45,6 +44,7 @@ config IWLWIFI
 config IWLDVM
        tristate "Intel Wireless WiFi DVM Firmware support"
        depends on IWLWIFI
+       default IWLWIFI
        help
          This is the driver supporting the DVM firmware which is
          currently the only firmware available for existing devices.
@@ -58,6 +58,15 @@ config IWLMVM
 
          Say yes if you have such a device.
 
+# 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 IWLWIFI && IWLDVM=n && IWLMVM=n
+
 menu "Debugging Options"
        depends on IWLWIFI
 
index 6c7800044a04d0287e3e896792f9660d0ee83f3b..3b5613ea458b2093244dc22ce298390f61ed515a 100644 (file)
@@ -7,8 +7,7 @@ iwlwifi-objs            += iwl-notif-wait.o
 iwlwifi-objs           += iwl-eeprom-read.o iwl-eeprom-parse.o
 iwlwifi-objs           += iwl-phy-db.o iwl-nvm-parse.o
 iwlwifi-objs           += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
-iwlwifi-objs           += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o
-iwlwifi-objs           += pcie/7000.o
+iwlwifi-objs           += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o iwl-7000.o
 
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o
index 41ec27cb6efe8559120d34de3274c8dca4e4f185..019d433900ef516ef9759b35a09cd5834b9cd485 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 6468de8634b01be6fb85d4daabbd58a40d1159f6..d6c4cf2ad7c530c32f95cd96491a96ea23af15ec 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 65e920cab2b78780c7813d030950001403c40821..cfddde194940e2a183c1685bf4eb445a4550f9b9 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 84e2c0fcfef6053af57ef3c8ee0bca6468d79725..95ca026ecc9d6be7383e2986385e84d374cb6964 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -1526,6 +1526,7 @@ struct iwl_compressed_ba_resp {
        __le16 scd_ssn;
        u8 txed;        /* number of frames sent */
        u8 txed_2_done; /* number of frames acked */
+       __le16 reserved1;
 } __packed;
 
 /*
index 20806cae11b72a50eee5525e3384613ad3c3181f..7b8178be119f4d0a110a2beaae3a91165df45bf1 100644 (file)
@@ -19,7 +19,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -2324,6 +2324,28 @@ static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file,
        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, CMD_SYNC, 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);
@@ -2343,6 +2365,7 @@ 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
@@ -2400,6 +2423,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir)
        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
index 44ca0e57f9f76fd8b3b58c183a630fdb057f78ad..87c006c9c573df1fee6541ac0fbdab9567b378a8 100644 (file)
@@ -19,7 +19,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 323e4a33fcaca4efb94b8bdeb815d223893b3f54..a7294fa4d7e54851566c6560ffd7d825927b8f25 100644 (file)
@@ -1100,7 +1100,7 @@ static void iwlagn_configure_filter(struct ieee80211_hw *hw,
                        FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
 }
 
-static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop)
+static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
 
@@ -1137,7 +1137,8 @@ done:
 static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif,
                                     struct ieee80211_channel *channel,
-                                    int duration)
+                                    int duration,
+                                    enum ieee80211_roc_type type)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
        struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
index 3a4aa5239c4562c0c10a3b98004b6b11f907486c..d69b55866714d9936b34bde9b53f176191278341 100644 (file)
@@ -19,7 +19,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index dc6f965a123a872f6ebeca8dbafe8635f244ccec..b89b9d9b99697926b84f9ddc87dbb316db2f8eeb 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index d1a670d7b10cfd05f28c07624616f436ebf6c321..70b7f68c4958b76b854d34559535ec27b510f791 100644 (file)
@@ -19,7 +19,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -418,7 +418,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
                                " Tx flags = 0x%08x, agg.state = %d",
                                info->flags, tid_data->agg.state);
                        IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d",
-                               sta_id, tid, SEQ_TO_SN(tid_data->seq_number));
+                               sta_id, tid,
+                               IEEE80211_SEQ_TO_SN(tid_data->seq_number));
                        goto drop_unlock_sta;
                }
 
@@ -569,7 +570,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
                return 0;
        }
 
-       tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
+       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)) {
@@ -651,7 +652,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
 
        spin_lock_bh(&priv->sta_lock);
        tid_data = &priv->tid_data[sta_id][tid];
-       tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
+       tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
        tid_data->agg.txq_id = txq_id;
 
        *ssn = tid_data->agg.ssn;
@@ -911,7 +912,7 @@ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
 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) & MAX_SN;
+                           tx_resp->frame_count) & IEEE80211_MAX_SN;
 }
 
 static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
@@ -1148,7 +1149,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
 
        if (tx_resp->frame_count == 1) {
                u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl);
-               next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10);
+               next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10);
 
                if (is_agg) {
                        /* If this is an aggregation queue, we can rely on the
index 1a4ac9236a4446e2d8b30722cad23e826756a660..0a1cdc5e856baa6f37966cac3021d9db2558c6a9 100644 (file)
@@ -19,7 +19,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
new file mode 100644 (file)
index 0000000..c080ae3
--- /dev/null
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called 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,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_WATCHDOG_DISABLED,
+       .max_event_log_size = 128,
+};
+
+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
+
+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
+
+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
new file mode 100644 (file)
index 0000000..a6ddd2f
--- /dev/null
@@ -0,0 +1,242 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called 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,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_DEF_WD_TIMEOUT,
+       .max_event_log_size = 512,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .hd_v2 = 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,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_LONG_WD_TIMEOUT,
+       .max_event_log_size = 512,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .hd_v2 = 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_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,
+};
+
+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,                \
+       .need_temp_offset_calib = true,                         \
+       .temp_offset_v2 = true,                                 \
+       .led_mode = IWL_LED_RF_STATE
+
+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,                    \
+       .bt_params = &iwl2030_bt_params,                        \
+       .eeprom_params = &iwl20x0_eeprom_params,                \
+       .need_temp_offset_calib = true,                         \
+       .temp_offset_v2 = true,                                 \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .adv_pm = true
+
+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,                \
+       .need_temp_offset_calib = true,                         \
+       .temp_offset_v2 = true,                                 \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .adv_pm = true,                                         \
+       .rx_with_siso_diversity = true
+
+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,                    \
+       .bt_params = &iwl2030_bt_params,                        \
+       .eeprom_params = &iwl20x0_eeprom_params,                \
+       .need_temp_offset_calib = true,                         \
+       .temp_offset_v2 = true,                                 \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .adv_pm = true,                                         \
+       .rx_with_siso_diversity = true
+
+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
new file mode 100644 (file)
index 0000000..403f3f2
--- /dev/null
@@ -0,0 +1,179 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called 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,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_WATCHDOG_DISABLED,
+       .max_event_log_size = 512,
+       .no_idle_support = 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
+
+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,                \
+       .no_xtal_calib = true,                                  \
+       .led_mode = IWL_LED_BLINK,                              \
+       .internal_wimax_coex = true
+
+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
new file mode 100644 (file)
index 0000000..b5ab8d1
--- /dev/null
@@ -0,0 +1,402 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called 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,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_DEF_WD_TIMEOUT,
+       .max_event_log_size = 512,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+};
+
+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,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1500,
+       .wd_timeout = IWL_DEF_WD_TIMEOUT,
+       .max_event_log_size = 1024,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+};
+
+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,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_LONG_WD_TIMEOUT,
+       .max_event_log_size = 512,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+};
+
+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_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,
+};
+
+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,                \
+       .need_temp_offset_calib = true,                         \
+       .led_mode = IWL_LED_RF_STATE
+
+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,                 \
+       .bt_params = &iwl6000_bt_params,                        \
+       .eeprom_params = &iwl6000_eeprom_params,                \
+       .need_temp_offset_calib = true,                         \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .adv_pm = true                                          \
+
+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,                 \
+       .bt_params = &iwl6000_bt_params,                        \
+       .eeprom_params = &iwl6000_eeprom_params,                \
+       .need_temp_offset_calib = true,                         \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .adv_pm = true
+
+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 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
+
+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
+
+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
+
+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
new file mode 100644 (file)
index 0000000..50263e8
--- /dev/null
@@ -0,0 +1,146 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/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  6
+#define IWL3160_UCODE_API_MAX  6
+
+/* Oldest version we won't warn about */
+#define IWL7260_UCODE_API_OK   6
+#define IWL3160_UCODE_API_OK   6
+
+/* Lowest firmware API version supported */
+#define IWL7260_UCODE_API_MIN  6
+#define IWL3160_UCODE_API_MIN  6
+
+/* 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 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"
+
+static const struct iwl_base_params iwl7000_base_params = {
+       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .num_of_queues = IWLAGN_NUM_QUEUES,
+       .pll_cfg_val = 0,
+       .shadow_ram_support = true,
+       .led_compensation = 57,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_LONG_WD_TIMEOUT,
+       .max_event_log_size = 512,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+};
+
+static const struct iwl_ht_params iwl7000_ht_params = {
+       .use_rts_for_aggregation = true, /* use rts/cts protection */
+       .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+#define IWL_DEVICE_7000                                                \
+       .ucode_api_max = IWL7260_UCODE_API_MAX,                 \
+       .ucode_api_ok = IWL7260_UCODE_API_OK,                   \
+       .ucode_api_min = IWL7260_UCODE_API_MIN,                 \
+       .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,                    \
+       /* TODO: .bt_params? */                                 \
+       .need_temp_offset_calib = true,                         \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .adv_pm = true                                          \
+
+
+const struct iwl_cfg iwl7260_2ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC7260",
+       .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,
+};
+
+const struct iwl_cfg iwl3160_ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC3160",
+       .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,
+};
+
+MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
index e9975c54c276755e5edbaa4102ebdc28b8edfdb9..6d73f943cefafb2dfb2489b4f1b31a846f833230 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 743b483433588a6818aa05a2c285f4c40e8e6666..c38aa8f775545b37b6b6ea215b435a63481c7717 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -275,4 +275,51 @@ struct iwl_cfg {
        const bool temp_offset_v2;
 };
 
+/*
+ * This list declares the config structures for all devices.
+ */
+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 iwl105_bgn_cfg;
+extern const struct iwl_cfg iwl105_bgn_d_cfg;
+extern const struct iwl_cfg iwl135_bgn_cfg;
+extern const struct iwl_cfg iwl7260_2ac_cfg;
+extern const struct iwl_cfg iwl3160_ac_cfg;
+
 #endif /* __IWL_CONFIG_H__ */
index df3463a38704d76c3a30662fa14eee996da01ebc..20e845d4da04304b0493b3d8c6fa3f29b333a427 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 87535a67de7663f4e134a81f04237fcf72bd7b80..8a44f594528df6c4f503face66300c93d79f1f44 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -66,6 +66,7 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/export.h>
+#include "iwl-drv.h"
 #include "iwl-debug.h"
 #include "iwl-devtrace.h"
 
@@ -85,11 +86,11 @@ void __iwl_ ##fn(struct device *dev, const char *fmt, ...)  \
 }
 
 __iwl_fn(warn)
-EXPORT_SYMBOL_GPL(__iwl_warn);
+IWL_EXPORT_SYMBOL(__iwl_warn);
 __iwl_fn(info)
-EXPORT_SYMBOL_GPL(__iwl_info);
+IWL_EXPORT_SYMBOL(__iwl_info);
 __iwl_fn(crit)
-EXPORT_SYMBOL_GPL(__iwl_crit);
+IWL_EXPORT_SYMBOL(__iwl_crit);
 
 void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only,
                const char *fmt, ...)
@@ -110,7 +111,7 @@ void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only,
        trace_iwlwifi_err(&vaf);
        va_end(args);
 }
-EXPORT_SYMBOL_GPL(__iwl_err);
+IWL_EXPORT_SYMBOL(__iwl_err);
 
 #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING)
 void __iwl_dbg(struct device *dev,
@@ -133,5 +134,5 @@ void __iwl_dbg(struct device *dev,
        trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf);
        va_end(args);
 }
-EXPORT_SYMBOL_GPL(__iwl_dbg);
+IWL_EXPORT_SYMBOL(__iwl_dbg);
 #endif
index 81aa91fab5aafcc1dc7fa597c85878d01ab83685..4491c1c72cc7d6e00ade51930342b16233172690 100644 (file)
@@ -298,7 +298,7 @@ TRACE_EVENT(iwlwifi_dbg,
                                       MAX_MSG_LEN, vaf->fmt,
                                       *vaf->va) >= MAX_MSG_LEN);
        ),
-       TP_printk("%s", (char *)__get_dynamic_array(msg))
+       TP_printk("%s", __get_str(msg))
 );
 
 #undef TRACE_SYSTEM
index fbfd2d1371176e247a22e5fca00ee5809088c306..498300577ac03d1089174fc1a7f286b43bf4d961 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -1102,7 +1102,7 @@ void iwl_drv_stop(struct iwl_drv *drv)
 
 /* shared module parameters */
 struct iwl_mod_params iwlwifi_mod_params = {
-       .restart_fw = 1,
+       .restart_fw = true,
        .plcp_check = true,
        .bt_coex_active = true,
        .power_level = IWL_POWER_INDEX_1,
@@ -1111,7 +1111,7 @@ struct iwl_mod_params iwlwifi_mod_params = {
        .wd_disable = true,
        /* the rest are 0 by default */
 };
-EXPORT_SYMBOL_GPL(iwlwifi_mod_params);
+IWL_EXPORT_SYMBOL(iwlwifi_mod_params);
 
 int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops)
 {
@@ -1135,7 +1135,7 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops)
        mutex_unlock(&iwlwifi_opmode_table_mtx);
        return -EIO;
 }
-EXPORT_SYMBOL_GPL(iwl_opmode_register);
+IWL_EXPORT_SYMBOL(iwl_opmode_register);
 
 void iwl_opmode_deregister(const char *name)
 {
@@ -1157,7 +1157,7 @@ void iwl_opmode_deregister(const char *name)
        }
        mutex_unlock(&iwlwifi_opmode_table_mtx);
 }
-EXPORT_SYMBOL_GPL(iwl_opmode_deregister);
+IWL_EXPORT_SYMBOL(iwl_opmode_deregister);
 
 static int __init iwl_drv_init(void)
 {
@@ -1207,8 +1207,8 @@ MODULE_PARM_DESC(11n_disable,
 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, int, S_IRUGO);
-MODULE_PARM_DESC(fw_restart, "restart firmware in case of error");
+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);
@@ -1266,7 +1266,3 @@ module_param_named(auto_agg, iwlwifi_mod_params.auto_agg,
                bool, S_IRUGO);
 MODULE_PARM_DESC(auto_agg,
                 "enable agg w/o check traffic load (default: enable)");
-
-module_param_named(5ghz_disable, iwlwifi_mod_params.disable_5ghz,
-               bool, S_IRUGO);
-MODULE_PARM_DESC(5ghz_disable, "disable 5GHz band (default: 0 [enabled])");
index 594a5c71b272ed3c703f522f283d43b5bbc7af53..7d145091630880ceafe9b4626f37df95e976e4e2 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -63,6 +63,8 @@
 #ifndef __iwl_drv_h__
 #define __iwl_drv_h__
 
+#include <linux/module.h>
+
 /* for all modules */
 #define DRV_NAME        "iwlwifi"
 #define IWLWIFI_VERSION "in-tree:"
@@ -123,4 +125,17 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
  */
 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__ */
index 034f2ff4f43d381d93f0a680c24f1edcf35805fc..600c9fdd7f710fc33091971f0162e20951534e29 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -62,6 +62,7 @@
 #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"
 
@@ -749,7 +750,7 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
        }
 
        ht_info->ht_supported = true;
-       ht_info->cap = 0;
+       ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40;
 
        if (iwlwifi_mod_params.amsdu_size_8K)
                ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
@@ -909,7 +910,7 @@ iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
        kfree(data);
        return NULL;
 }
-EXPORT_SYMBOL_GPL(iwl_parse_eeprom_data);
+IWL_EXPORT_SYMBOL(iwl_parse_eeprom_data);
 
 /* helper functions */
 int iwl_nvm_check_version(struct iwl_nvm_data *data,
@@ -928,4 +929,4 @@ int iwl_nvm_check_version(struct iwl_nvm_data *data,
                data->calib_version,  trans->cfg->nvm_calib_ver);
        return -EINVAL;
 }
-EXPORT_SYMBOL_GPL(iwl_nvm_check_version);
+IWL_EXPORT_SYMBOL(iwl_nvm_check_version);
index 683fe6a8c58fe5e173df2c78eee16dfc128220c1..37f115390b1923f18b9e8fb990bf6a326c55ceed 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index ef4806f27cf86fe5a6a66b44ac6136739962c4da..e5f2e362ab0b293665636aad41f844e29dfbf34e 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -63,6 +63,7 @@
 #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"
@@ -460,4 +461,4 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size)
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(iwl_read_eeprom);
+IWL_EXPORT_SYMBOL(iwl_read_eeprom);
index b2588c5cbf931b044b145bf9d0584b0de01a1a72..8e941f8bd7d62158dc92e5a102b54b71b047e174 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index f5592fb3b1ed42cb7c2e86935e61512914e373b1..484d318245fb91e21705a7c11dad7c30d66d69d7 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 90873eca35f77ddf029b530e7fe633acb318f11b..8b6c6fd95ed038ac1052827a2edb2a39f04f849a 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index b545178e46e3daf9853ed872035fccbb0dedc57a..c4c446d41eb081bf0f7eb37034a3137faf5a281e 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
  *     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
  */
 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),
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -152,6 +154,19 @@ struct iwl_tlv_calib_ctrl {
        __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,
+};
+
 /**
  * struct iwl_fw - variables associated with the firmware
  *
@@ -188,4 +203,16 @@ struct iwl_fw {
        bool mvm_fw;
 };
 
+static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw)
+{
+       return (fw->phy_config & FW_PHY_CFG_TX_CHAIN) >>
+               FW_PHY_CFG_TX_CHAIN_POS;
+}
+
+static inline u8 iwl_fw_valid_rx_ant(const struct iwl_fw *fw)
+{
+       return (fw->phy_config & FW_PHY_CFG_RX_CHAIN) >>
+               FW_PHY_CFG_RX_CHAIN_POS;
+}
+
 #endif  /* __iwl_fw_h__ */
index 276410d82de498186bb009788ddbb5ff5484c235..305c81f2c2b4af8245046c794a31174d33164e94 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/device.h>
 #include <linux/export.h>
 
+#include "iwl-drv.h"
 #include "iwl-io.h"
 #include "iwl-csr.h"
 #include "iwl-debug.h"
@@ -49,7 +50,7 @@ int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
 
        return -ETIMEDOUT;
 }
-EXPORT_SYMBOL_GPL(iwl_poll_bit);
+IWL_EXPORT_SYMBOL(iwl_poll_bit);
 
 u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
 {
@@ -62,7 +63,7 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
 
        return value;
 }
-EXPORT_SYMBOL_GPL(iwl_read_direct32);
+IWL_EXPORT_SYMBOL(iwl_read_direct32);
 
 void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
 {
@@ -73,7 +74,7 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
                iwl_trans_release_nic_access(trans, &flags);
        }
 }
-EXPORT_SYMBOL_GPL(iwl_write_direct32);
+IWL_EXPORT_SYMBOL(iwl_write_direct32);
 
 int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
                        int timeout)
@@ -89,7 +90,7 @@ int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
 
        return -ETIMEDOUT;
 }
-EXPORT_SYMBOL_GPL(iwl_poll_direct_bit);
+IWL_EXPORT_SYMBOL(iwl_poll_direct_bit);
 
 static inline u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs)
 {
@@ -115,7 +116,7 @@ u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
        }
        return val;
 }
-EXPORT_SYMBOL_GPL(iwl_read_prph);
+IWL_EXPORT_SYMBOL(iwl_read_prph);
 
 void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
 {
@@ -126,7 +127,7 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
                iwl_trans_release_nic_access(trans, &flags);
        }
 }
-EXPORT_SYMBOL_GPL(iwl_write_prph);
+IWL_EXPORT_SYMBOL(iwl_write_prph);
 
 void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
 {
@@ -138,7 +139,7 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
                iwl_trans_release_nic_access(trans, &flags);
        }
 }
-EXPORT_SYMBOL_GPL(iwl_set_bits_prph);
+IWL_EXPORT_SYMBOL(iwl_set_bits_prph);
 
 void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
                            u32 bits, u32 mask)
@@ -151,7 +152,7 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
                iwl_trans_release_nic_access(trans, &flags);
        }
 }
-EXPORT_SYMBOL_GPL(iwl_set_bits_mask_prph);
+IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph);
 
 void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
 {
@@ -164,4 +165,4 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
                iwl_trans_release_nic_access(trans, &flags);
        }
 }
-EXPORT_SYMBOL_GPL(iwl_clear_bits_prph);
+IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
index 2c2a729092f5e894013c39f1eccb0a739a5e56d4..d6f6c37c09fd3a395406b67f2e98300b4926d53d 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -103,13 +103,12 @@ enum iwl_power_level {
  * @ant_coupling: antenna coupling in dB, default = 0
  * @bt_ch_announce: BT channel inhibition, default = enable
  * @auto_agg: enable agg. without check, default = true
- * @disable_5ghz: disable 5GHz capability, default = false
  */
 struct iwl_mod_params {
        int sw_crypto;
        unsigned int disable_11n;
        int amsdu_size_8K;
-       int restart_fw;
+       bool restart_fw;
        bool plcp_check;
        int  wd_disable;
        bool bt_coex_active;
@@ -120,7 +119,6 @@ struct iwl_mod_params {
        int ant_coupling;
        bool bt_ch_announce;
        bool auto_agg;
-       bool disable_5ghz;
 };
 
 #endif /* #__iwl_modparams_h__ */
index c3affbc62cdf736c16018080005c4d9e08884d53..940b8a9d5285ad7a54ecd3cce7623bc19e59a0b6 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -63,6 +63,7 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 
+#include "iwl-drv.h"
 #include "iwl-notif-wait.h"
 
 
@@ -72,7 +73,7 @@ void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait)
        INIT_LIST_HEAD(&notif_wait->notif_waits);
        init_waitqueue_head(&notif_wait->notif_waitq);
 }
-EXPORT_SYMBOL_GPL(iwl_notification_wait_init);
+IWL_EXPORT_SYMBOL(iwl_notification_wait_init);
 
 void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait,
                                  struct iwl_rx_packet *pkt)
@@ -117,7 +118,7 @@ void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait,
        if (triggered)
                wake_up_all(&notif_wait->notif_waitq);
 }
-EXPORT_SYMBOL_GPL(iwl_notification_wait_notify);
+IWL_EXPORT_SYMBOL(iwl_notification_wait_notify);
 
 void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait)
 {
@@ -130,7 +131,7 @@ void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait)
 
        wake_up_all(&notif_wait->notif_waitq);
 }
-EXPORT_SYMBOL_GPL(iwl_abort_notification_waits);
+IWL_EXPORT_SYMBOL(iwl_abort_notification_waits);
 
 void
 iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait,
@@ -154,7 +155,7 @@ iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait,
        list_add(&wait_entry->list, &notif_wait->notif_waits);
        spin_unlock_bh(&notif_wait->notif_wait_lock);
 }
-EXPORT_SYMBOL_GPL(iwl_init_notification_wait);
+IWL_EXPORT_SYMBOL(iwl_init_notification_wait);
 
 int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait,
                          struct iwl_notification_wait *wait_entry,
@@ -178,7 +179,7 @@ int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait,
                return -ETIMEDOUT;
        return 0;
 }
-EXPORT_SYMBOL_GPL(iwl_wait_notification);
+IWL_EXPORT_SYMBOL(iwl_wait_notification);
 
 void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait,
                             struct iwl_notification_wait *wait_entry)
@@ -187,4 +188,4 @@ void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait,
        list_del(&wait_entry->list);
        spin_unlock_bh(&notif_wait->notif_wait_lock);
 }
-EXPORT_SYMBOL_GPL(iwl_remove_notification);
+IWL_EXPORT_SYMBOL(iwl_remove_notification);
index c2ce764463a33bab64159fa7dbdc02e23f2b724a..2e2f1c8c99f989023ab80a1244eb5b5b55164810 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index a70213bdb83c47469fc0f2f50c9b844fcc753aa0..6199a0a597a6f5c60df627960d616e674903a8ae 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -62,6 +62,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#include "iwl-drv.h"
 #include "iwl-modparams.h"
 #include "iwl-nvm-parse.h"
 
@@ -149,6 +150,8 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = {
  * @NVM_CHANNEL_DFS: dynamic freq selection candidate
  * @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),
@@ -158,6 +161,8 @@ enum iwl_nvm_channel_flags {
        NVM_CHANNEL_DFS = BIT(7),
        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)   \
@@ -210,6 +215,10 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                        else
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
                }
+               if (!(ch_flags & NVM_CHANNEL_80MHZ))
+                       channel->flags |= IEEE80211_CHAN_NO_80MHZ;
+               if (!(ch_flags & NVM_CHANNEL_160MHZ))
+                       channel->flags |= IEEE80211_CHAN_NO_160MHZ;
 
                if (!(ch_flags & NVM_CHANNEL_IBSS))
                        channel->flags |= IEEE80211_CHAN_NO_IBSS;
@@ -245,6 +254,43 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
        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)
+{
+       /* For now, assume new devices with NVM are VHT capable */
+
+       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 |
+                      7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+       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 (data->valid_rx_ant == 1 || cfg->rx_with_siso_diversity) {
+               vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
+                               IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
+               /* 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 *nvm_sw)
 {
@@ -268,6 +314,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
        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);
+       iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap);
 
        if (n_channels != n_used)
                IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
@@ -343,4 +390,4 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
 
        return data;
 }
-EXPORT_SYMBOL_GPL(iwl_parse_nvm_data);
+IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
index b2692bd287faf97651108d08506bd16df7addfb5..e57fb989661e0617487cd314b351f3915975e424 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 4a680019e1174a122ee2d5062d8bab909306a95f..98c7aa7346da1ebb78e6ab98dd4c6fffb25d296b 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 3392011a876841bae6ac4d75eb11fa4a764cc07b..25745daa0d5da4142abef88f506b8bee614d8905 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -65,6 +65,7 @@
 #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"
@@ -149,7 +150,7 @@ struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
        /* TODO: add default values of the phy db. */
        return phy_db;
 }
-EXPORT_SYMBOL(iwl_phy_db_init);
+IWL_EXPORT_SYMBOL(iwl_phy_db_init);
 
 /*
  * get phy db section: returns a pointer to a phy db section specified by
@@ -215,7 +216,7 @@ void iwl_phy_db_free(struct iwl_phy_db *phy_db)
 
        kfree(phy_db);
 }
-EXPORT_SYMBOL(iwl_phy_db_free);
+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)
@@ -260,7 +261,7 @@ int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
 
        return 0;
 }
-EXPORT_SYMBOL(iwl_phy_db_set_section);
+IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
 
 static int is_valid_channel(u16 ch_id)
 {
@@ -495,4 +496,4 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
                       "Finished sending phy db non channel data\n");
        return 0;
 }
-EXPORT_SYMBOL(iwl_send_phy_db_data);
+IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);
index d0e43d96ab38340657b85a524ddb2b5c770b6df6..ce983af79644310050a11297813927e3ece3fecf 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index f76e9cad7757f52db20d010519c040d4ed7185c1..386f2a7c87cb731b39aec7dd03c53f03e5e17725 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index ce0c67b425ee8950a8e25fc68f2ca46a7860873c..5cfd55b86ed3d33a779ab512bbc2df7c637f0f53 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -64,6 +64,7 @@
 #include <linux/export.h>
 #include <net/netlink.h>
 
+#include "iwl-drv.h"
 #include "iwl-io.h"
 #include "iwl-fh.h"
 #include "iwl-prph.h"
@@ -271,7 +272,7 @@ static int iwl_test_fw_cmd(struct iwl_test *tst, struct nlattr **tb)
 
        reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
        skb = iwl_test_alloc_reply(tst, reply_len + 20);
-       reply_buf = kmalloc(reply_len, GFP_KERNEL);
+       reply_buf = kmemdup(&pkt->hdr, reply_len, GFP_KERNEL);
        if (!skb || !reply_buf) {
                kfree_skb(skb);
                kfree(reply_buf);
@@ -279,7 +280,6 @@ static int iwl_test_fw_cmd(struct iwl_test *tst, struct nlattr **tb)
        }
 
        /* The reply is in a page, that we cannot send to user space. */
-       memcpy(reply_buf, &(pkt->hdr), reply_len);
        iwl_free_resp(&cmd);
 
        if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
@@ -653,7 +653,7 @@ int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb,
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(iwl_test_parse);
+IWL_EXPORT_SYMBOL(iwl_test_parse);
 
 /*
  * Handle test commands.
@@ -715,7 +715,7 @@ int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb)
        }
        return result;
 }
-EXPORT_SYMBOL_GPL(iwl_test_handle_cmd);
+IWL_EXPORT_SYMBOL(iwl_test_handle_cmd);
 
 static int iwl_test_trace_dump(struct iwl_test *tst, struct sk_buff *skb,
                               struct netlink_callback *cb)
@@ -803,7 +803,7 @@ int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb,
        }
        return result;
 }
-EXPORT_SYMBOL_GPL(iwl_test_dump);
+IWL_EXPORT_SYMBOL(iwl_test_dump);
 
 /*
  * Multicast a spontaneous messages from the device to the user space.
@@ -849,4 +849,4 @@ void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb)
        if (tst->notify)
                iwl_test_send_rx(tst, rxb);
 }
-EXPORT_SYMBOL_GPL(iwl_test_rx);
+IWL_EXPORT_SYMBOL(iwl_test_rx);
index 7fbf4d717caa8a54d441ed07c71d2be855c5f498..8fbd217048407813381b88fdccff4d07ef6ae1f7 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index a963f45c6849cb12b00bc4984e49a9cc716c4df4..98f48a9afc98a496950a13808778b17dc1ad42e9 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 0cac2b7af78b99eb1e8c8951a9638c12033e382c..7f9c254292a809e1e58c929467a01189b77638e5 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
  * completely agnostic to these differences.
  * The transport does provide helper functionnality (i.e. SYNC / ASYNC mode),
  */
-#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
 #define SEQ_TO_QUEUE(s)        (((s) >> 8) & 0x1f)
 #define QUEUE_TO_SEQ(q)        (((q) & 0x1f) << 8)
 #define SEQ_TO_INDEX(s)        ((s) & 0xff)
index 807b250ec3960f1a21c35b26c5fa2db4ba994b3c..2acc44b40986868437a92be40adcb30ecaa1a6fd 100644 (file)
@@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM)   += iwlmvm.o
 iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
 iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
 iwlmvm-y += scan.o time-event.o rs.o
-iwlmvm-y += power.o
+iwlmvm-y += power.o bt-coex.o
 iwlmvm-y += led.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
index 73d24aacb90a238825cae18da4f0d0f97978ee7c..93fd1457954bcd43cc5f833de4f8b1b188b84d96 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
new file mode 100644 (file)
index 0000000..1700232
--- /dev/null
@@ -0,0 +1,348 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include "fw-api-bt-coex.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-debug.h"
+
+#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant)                 \
+       [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) |    \
+                  ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
+
+static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
+                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
+                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
+                      BT_COEX_PRIO_TBL_PRIO_LOW, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
+                      BT_COEX_PRIO_TBL_PRIO_LOW, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
+                      BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
+                      BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
+                      BT_COEX_PRIO_TBL_DISABLED, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
+       0, 0, 0, 0, 0, 0,
+};
+
+#undef EVENT_PRIO_ANT
+
+int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
+{
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
+                                   sizeof(struct iwl_bt_coex_prio_tbl_cmd),
+                                   &iwl_bt_prio_tbl);
+}
+
+static int iwl_send_bt_env(struct iwl_mvm *mvm, u8 action, u8 type)
+{
+       struct iwl_bt_coex_prot_env_cmd env_cmd;
+       int ret;
+
+       env_cmd.action = action;
+       env_cmd.type = type;
+       ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PROT_ENV, CMD_SYNC,
+                                  sizeof(env_cmd), &env_cmd);
+       if (ret)
+               IWL_ERR(mvm, "failed to send BT env command\n");
+       return ret;
+}
+
+enum iwl_bt_kill_msk {
+       BT_KILL_MSK_DEFAULT,
+       BT_KILL_MSK_SCO_HID_A2DP,
+       BT_KILL_MSK_REDUCED_TXPOW,
+       BT_KILL_MSK_MAX,
+};
+
+static const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
+       [BT_KILL_MSK_DEFAULT] = 0xffff0000,
+       [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
+       [BT_KILL_MSK_REDUCED_TXPOW] = 0,
+};
+
+static const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
+       [BT_KILL_MSK_DEFAULT] = 0xffff0000,
+       [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
+       [BT_KILL_MSK_REDUCED_TXPOW] = 0,
+};
+
+#define IWL_BT_DEFAULT_BOOST (0xf0f0f0f0)
+
+/* Tight Coex */
+static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = {
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaeaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xcc00ff28),
+       cpu_to_le32(0x0000aaaa),
+       cpu_to_le32(0xcc00aaaa),
+       cpu_to_le32(0x0000aaaa),
+       cpu_to_le32(0xc0004000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0xf0005000),
+       cpu_to_le32(0xf0005000),
+};
+
+/* Loose Coex */
+static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = {
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(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 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = {
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x00000000),
+};
+
+/* BT Antenna Coupling Threshold (dB) */
+#define IWL_BT_ANTENNA_COUPLING_THRESHOLD      (35)
+#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD       (3)
+
+
+int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
+{
+       struct iwl_bt_coex_cmd cmd = {
+               .max_kill = 5,
+               .bt3_time_t7_value = 1,
+               .bt3_prio_sample_time = 2,
+               .bt3_timer_t2_value = 0xc,
+       };
+       int ret;
+
+       cmd.flags = iwlwifi_mod_params.bt_coex_active ?
+                       BT_COEX_NW : BT_COEX_DISABLE;
+       cmd.flags |= iwlwifi_mod_params.bt_ch_announce ? BT_CH_PRIMARY_EN : 0;
+       cmd.flags |= BT_SYNC_2_BT_DISABLE;
+
+       cmd.valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
+                                       BT_VALID_BT_PRIO_BOOST |
+                                       BT_VALID_MAX_KILL |
+                                       BT_VALID_3W_TMRS |
+                                       BT_VALID_KILL_ACK |
+                                       BT_VALID_KILL_CTS |
+                                       BT_VALID_REDUCED_TX_POWER |
+                                       BT_VALID_LUT);
+
+       if (iwlwifi_mod_params.ant_coupling > IWL_BT_ANTENNA_COUPLING_THRESHOLD)
+               memcpy(&cmd.decision_lut, iwl_loose_lookup,
+                      sizeof(iwl_tight_lookup));
+       else
+               memcpy(&cmd.decision_lut, iwl_tight_lookup,
+                      sizeof(iwl_tight_lookup));
+
+       cmd.bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
+       cmd.kill_ack_msk =
+               cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
+       cmd.kill_cts_msk =
+               cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
+
+       /* go to CALIB state in internal BT-Coex state machine */
+       ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
+                             BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+       if (ret)
+               return ret;
+
+       ret  = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
+                              BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+       if (ret)
+               return ret;
+
+       return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+struct iwl_bt_notif_iterator_data {
+       struct iwl_mvm *mvm;
+       struct iwl_bt_coex_profile_notif *notif;
+};
+
+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_notif_iterator_data *data = _data;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum ieee80211_smps_mode smps_mode;
+       enum ieee80211_band band;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       if (chanctx_conf && chanctx_conf->def.chan)
+               band = chanctx_conf->def.chan->band;
+       else
+               band = -1;
+       rcu_read_unlock();
+
+       if (band != IEEE80211_BAND_2GHZ)
+               return;
+
+       smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+       if (data->notif->bt_status)
+               smps_mode = IEEE80211_SMPS_DYNAMIC;
+
+       if (data->notif->bt_traffic_load >= IWL_BT_LOAD_FORCE_SISO_THRESHOLD)
+               smps_mode = IEEE80211_SMPS_STATIC;
+
+       IWL_DEBUG_COEX(data->mvm,
+                      "mac %d: bt_status %d traffic_load %d smps_req %d\n",
+                      mvmvif->id,  data->notif->bt_status,
+                      data->notif->bt_traffic_load, smps_mode);
+
+       ieee80211_request_smps(vif, smps_mode);
+}
+
+int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
+                            struct iwl_rx_cmd_buffer *rxb,
+                            struct iwl_device_cmd *dev_cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
+       struct iwl_bt_notif_iterator_data data = {
+               .mvm = mvm,
+               .notif = notif,
+       };
+       struct iwl_bt_coex_cmd cmd = {};
+       enum iwl_bt_kill_msk bt_kill_msk;
+
+       IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
+       IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not ");
+       IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
+       IWL_DEBUG_COEX(mvm, "\tBT traffic load %d\n", notif->bt_traffic_load);
+       IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
+                      notif->bt_agg_traffic_load);
+       IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+
+       /* remember this notification for future use: rssi fluctuations */
+       memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
+
+       ieee80211_iterate_active_interfaces_atomic(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_bt_notif_iterator, &data);
+
+       /* Low latency BT profile is active: give higher prio to BT */
+       if (BT_MBOX_MSG(notif, 3, SCO_STATE)  ||
+           BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
+           BT_MBOX_MSG(notif, 3, SNIFF_STATE))
+               bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
+       else
+               bt_kill_msk = BT_KILL_MSK_DEFAULT;
+
+       /* Don't send HCMD if there is no update */
+       if (bt_kill_msk == mvm->bt_kill_msk)
+               return 0;
+
+       IWL_DEBUG_COEX(mvm,
+                      "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
+                      bt_kill_msk,
+                      BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
+                      BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
+                      BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
+
+       mvm->bt_kill_msk = bt_kill_msk;
+       cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
+       cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
+
+       cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
+
+       if (iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC, sizeof(cmd), &cmd))
+               IWL_ERR(mvm, "Failed to sent BT Coex CMD\n");
+
+       /* This handler is ASYNC */
+       return 0;
+}
index 994c8c263dc0e6744afa176e0be5ef4e89d6638d..bf087abe39f37bd30a0223833c62d322af12af75 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
  *****************************************************************************/
 
 #include <linux/etherdevice.h>
+#include <linux/ip.h>
 #include <net/cfg80211.h>
 #include <net/ipv6.h>
+#include <net/tcp.h>
 #include "iwl-modparams.h"
 #include "fw-api.h"
 #include "mvm.h"
@@ -402,6 +404,233 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
                                    sizeof(cmd), &cmd);
 }
 
+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 iwl_mvm *mvm,
+                                    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, },
+               .flags = CMD_SYNC,
+       };
+       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(
+               mvm, vif, tcp, cfg->syn_tx.data, NULL,
+               &cfg->syn_tx.info.tcp_pseudo_header_checksum,
+               MVM_TCP_TX_SYN);
+       cfg->syn_tx.info.tcp_payload_length = 0;
+
+       /* SYN/ACK (RX) */
+       iwl_mvm_build_tcp_packet(
+               mvm, vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
+               &cfg->synack_rx.info.tcp_pseudo_header_checksum,
+               MVM_TCP_RX_SYNACK);
+       cfg->synack_rx.info.tcp_payload_length = 0;
+
+       /* KEEPALIVE/ACK (TX) */
+       iwl_mvm_build_tcp_packet(
+               mvm, vif, tcp, cfg->keepalive_tx.data, NULL,
+               &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(
+               mvm, 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(
+               mvm, 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(
+               mvm, 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;
+}
+
 struct iwl_d3_iter_data {
        struct iwl_mvm *mvm;
        struct ieee80211_vif *vif;
@@ -637,9 +866,21 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
 
        if (wowlan->rfkill_release)
-               d3_cfg_cmd.wakeup_flags |=
+               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);
+       }
+
        iwl_mvm_cancel_scan(mvm);
 
        iwl_trans_stop_device(mvm->trans);
@@ -755,6 +996,10 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        if (ret)
                goto out;
 
+       ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
+       if (ret)
+               goto out;
+
        /* must be last -- this switches firmware state */
        ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC,
                                   sizeof(d3_cfg_cmd), &d3_cfg_cmd);
@@ -874,6 +1119,15 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        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 = le32_to_cpu(status->wake_packet_bufsize);
                int pktlen = le32_to_cpu(status->wake_packet_length);
index c1bdb5582126c388e7b43b590a537e17688d3e0c..b080b4ba545806e3ddf4ebd980cc2975837c29ae 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -69,12 +69,6 @@ struct iwl_dbgfs_mvm_ctx {
        struct ieee80211_vif *vif;
 };
 
-static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
-{
-       file->private_data = inode->i_private;
-       return 0;
-}
-
 static ssize_t iwl_dbgfs_tx_flush_write(struct file *file,
                                        const char __user *user_buf,
                                        size_t count, loff_t *ppos)
@@ -306,10 +300,130 @@ static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
        return 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 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;
+       struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
+       char *buf;
+       int ret, pos = 0, bufsz = sizeof(char) * 1024;
+
+       buf = kmalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+
+       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);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
+                                        notif->bt_status);
+       pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
+                                        notif->bt_open_conn);
+       pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
+                                        notif->bt_traffic_load);
+       pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
+                                        notif->bt_agg_traffic_load);
+       pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+                                        notif->bt_ci_compliance);
+
+       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_fw_restart_write(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       bool restart_fw = iwlwifi_mod_params.restart_fw;
+       int ret;
+
+       iwlwifi_mod_params.restart_fw = true;
+
+       mutex_lock(&mvm->mutex);
+
+       /* take the return value to make compiler happy - it will fail anyway */
+       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL);
+
+       mutex_unlock(&mvm->mutex);
+
+       iwlwifi_mod_params.restart_fw = restart_fw;
+
+       return count;
+}
+
 #define MVM_DEBUGFS_READ_FILE_OPS(name)                                        \
 static const struct file_operations iwl_dbgfs_##name##_ops = { \
        .read = iwl_dbgfs_##name##_read,                                \
-       .open = iwl_dbgfs_open_file_generic,                            \
+       .open = simple_open,                                            \
        .llseek = generic_file_llseek,                                  \
 }
 
@@ -317,14 +431,14 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {    \
 static const struct file_operations iwl_dbgfs_##name##_ops = { \
        .write = iwl_dbgfs_##name##_write,                              \
        .read = iwl_dbgfs_##name##_read,                                \
-       .open = iwl_dbgfs_open_file_generic,                            \
+       .open = simple_open,                                            \
        .llseek = generic_file_llseek,                                  \
 };
 
 #define MVM_DEBUGFS_WRITE_FILE_OPS(name)                               \
 static const struct file_operations iwl_dbgfs_##name##_ops = { \
        .write = iwl_dbgfs_##name##_write,                              \
-       .open = iwl_dbgfs_open_file_generic,                            \
+       .open = simple_open,                                            \
        .llseek = generic_file_llseek,                                  \
 };
 
@@ -345,8 +459,10 @@ MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush);
 MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram);
 MVM_DEBUGFS_READ_FILE_OPS(stations);
+MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
+MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
 
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
 {
@@ -358,8 +474,10 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
+       MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
 
        /*
         * Create a symlink with mac80211. It will be removed when mac80211
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h
new file mode 100644 (file)
index 0000000..05c61d6
--- /dev/null
@@ -0,0 +1,319 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __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_CH_PRIMARY_EN:
+ * @BT_CH_SECONDARY_EN:
+ * @BT_NOTIF_COEX_OFF:
+ * @BT_COEX_MODE_POS:
+ * @BT_COEX_MODE_MSK:
+ * @BT_COEX_DISABLE:
+ * @BT_COEX_2W:
+ * @BT_COEX_3W:
+ * @BT_COEX_NW:
+ * @BT_USE_DEFAULTS:
+ * @BT_SYNC_2_BT_DISABLE:
+ * @BT_COEX_CORUNNING_TBL_EN:
+ */
+enum iwl_bt_coex_flags {
+       BT_CH_PRIMARY_EN                = BIT(0),
+       BT_CH_SECONDARY_EN              = BIT(1),
+       BT_NOTIF_COEX_OFF               = BIT(2),
+       BT_COEX_MODE_POS                = 3,
+       BT_COEX_MODE_MSK                = BITS(3) << BT_COEX_MODE_POS,
+       BT_COEX_DISABLE                 = 0x0 << BT_COEX_MODE_POS,
+       BT_COEX_2W                      = 0x1 << BT_COEX_MODE_POS,
+       BT_COEX_3W                      = 0x2 << BT_COEX_MODE_POS,
+       BT_COEX_NW                      = 0x3 << BT_COEX_MODE_POS,
+       BT_USE_DEFAULTS                 = BIT(6),
+       BT_SYNC_2_BT_DISABLE            = BIT(7),
+       /*
+        * For future use - when the flags will be enlarged
+        * BT_COEX_CORUNNING_TBL_EN     = BIT(8),
+        */
+};
+
+/*
+ * indicates what has changed in the BT_COEX command.
+ */
+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),
+       /*
+        * For future use - when the valid flags will be enlarged
+        * BT_VALID_TXTX_DELTA_FREQ_THRS        = BIT(16),
+        * BT_VALID_TXRX_MAX_FREQ_0     = BIT(17),
+        */
+};
+
+/**
+ * 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),
+};
+
+#define BT_COEX_LUT_SIZE (12)
+
+/**
+ * struct iwl_bt_coex_cmd - bt coex configuration command
+ * @flags:&enum iwl_bt_coex_flags
+ * @lead_time:
+ * @max_kill:
+ * @bt3_time_t7_value:
+ * @kill_ack_msk:
+ * @kill_cts_msk:
+ * @bt3_prio_sample_time:
+ * @bt3_timer_t2_value:
+ * @bt4_reaction_time:
+ * @decision_lut[12]:
+ * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
+ * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
+ * @bt_prio_boost: values for PTA boost register
+ * @wifi_tx_prio_boost: SW boost of wifi tx priority
+ * @wifi_rx_prio_boost: SW boost of wifi rx priority
+ *
+ * The structure is used for the BT_COEX command.
+ */
+struct iwl_bt_coex_cmd {
+       u8 flags;
+       u8 lead_time;
+       u8 max_kill;
+       u8 bt3_time_t7_value;
+       __le32 kill_ack_msk;
+       __le32 kill_cts_msk;
+       u8 bt3_prio_sample_time;
+       u8 bt3_timer_t2_value;
+       __le16 bt4_reaction_time;
+       __le32 decision_lut[BT_COEX_LUT_SIZE];
+       u8 bt_reduced_tx_power;
+       u8 reserved;
+       __le16 valid_bit_msk;
+       __le32 bt_prio_boost;
+       u8 reserved2;
+       u8 wifi_tx_prio_boost;
+       __le16 wifi_rx_prio_boost;
+} __packed; /* BT_COEX_CMD_API_S_VER_3 */
+
+#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)
+
+/**
+ * struct iwl_bt_coex_profile_notif - notification about BT coex
+ * @mbox_msg: message from BT to WiFi
+ * @:bt_status: 0 - off, 1 - on
+ * @:bt_open_conn: number of BT connections open
+ * @:bt_traffic_load: load of BT traffic
+ * @:bt_agg_traffic_load: aggregated load of BT traffic
+ * @:bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ */
+struct iwl_bt_coex_profile_notif {
+       __le32 mbox_msg[4];
+       u8 bt_status;
+       u8 bt_open_conn;
+       u8 bt_traffic_load;
+       u8 bt_agg_traffic_load;
+       u8 bt_ci_compliance;
+       u8 reserved[3];
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */
+
+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;
+
+enum iwl_bt_coex_env_action {
+       BT_COEX_ENV_CLOSE        = 0,
+       BT_COEX_ENV_OPEN         = 1,
+}; /* BT_COEX_PROT_ENV_ACTION_API_E_VER_1 */
+
+/**
+ * struct iwl_bt_coex_prot_env_cmd - BT Protection Envelope
+ * @action: enum %iwl_bt_coex_env_action
+ * @type: enum %iwl_bt_coex_prio_table_event
+ */
+struct iwl_bt_coex_prot_env_cmd {
+       u8 action; /* 0 = closed, 1 = open */
+       u8 type; /* 0 .. 15 */
+       u8 reserved[2];
+} __packed;
+
+#endif /* __fw_api_bt_coex_h__ */
index cf6f9a02fb745393be869b4455669363cf790bc6..51e015d1dfb297cfeb6169afcc2db2ae57dc6a8d 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -258,7 +258,7 @@ enum iwl_wowlan_wakeup_reason {
        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),
+       /* BIT(11) reserved */
        IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET             = BIT(12),
 }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
 
@@ -277,6 +277,55 @@ struct iwl_wowlan_status {
        u8 wake_packet[]; /* can be truncated from _length to _bufsize */
 } __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
 
+#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__ */
index ae39b7dfda7b7c62e791b0a0633536c1bef21314..d68640ea41d42e3ea0612d1fa562de1b00ba50e6 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index be36b7604b7f83e349c84da6b27fd2d7c8030d36..127051891e9b7e19a06c56eb09c76612ea72b594 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index aa3474d0823119eee3ade37d9130e218c1ef660d..fdd33bc0a594a2d5456044b1a10df5d621ed8f8e 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 670ac8f95e26b06a97e22a34484c655d29aada66..b60d14151721da3cb141b9b74ac0e3aee2d43a92 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 0acb53dda22d44319dedc9d3fc4f9e5301360742..a30691a8a85b03a0b76815bf627480242a2458af 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 2677914bf0a681416fc06e21893de9abe5ec863f..007a93b25bd79ca96134a60ebe5ec79e69be56fe 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -537,6 +537,12 @@ struct iwl_mac_beacon_cmd {
        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;
+
 /**
  * enum iwl_dump_control - dump (flush) control flags
  * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
index 2adb61f103f4cd310ae654fdd83ff466a843dda5..1073f2682221d86dcd10d0f9557680258be53fcf 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -70,6 +70,7 @@
 #include "fw-api-mac.h"
 #include "fw-api-power.h"
 #include "fw-api-d3.h"
+#include "fw-api-bt-coex.h"
 
 /* queue and FIFO numbers by usage */
 enum {
@@ -150,8 +151,10 @@ enum {
 
        SET_CALIB_DEFAULT_CMD = 0x8e,
 
+       BEACON_NOTIFICATION = 0x90,
        BEACON_TEMPLATE_CMD = 0x91,
        TX_ANT_CONFIGURATION_CMD = 0x98,
+       BT_CONFIG = 0x9b,
        STATISTICS_NOTIFICATION = 0x9d,
 
        /* RF-KILL commands and notifications */
@@ -162,6 +165,11 @@ enum {
        REPLY_RX_MPDU_CMD = 0xc1,
        BA_NOTIF = 0xc5,
 
+       /* BT Coex */
+       BT_COEX_PRIO_TABLE = 0xcc,
+       BT_COEX_PROT_ENV = 0xcd,
+       BT_PROFILE_NOTIFICATION = 0xce,
+
        REPLY_DEBUG_CMD = 0xf0,
        DEBUG_LOG_MSG = 0xf7,
 
@@ -271,38 +279,7 @@ enum {
        NVM_ACCESS_TARGET_EEPROM = 2,
 };
 
-/**
- * struct iwl_nvm_access_cmd_ver1 - Request the device to send the NVM.
- * @op_code: 0 - read, 1 - write.
- * @target: NVM_ACCESS_TARGET_*. should be 0 for read.
- * @cache_refresh: 0 - None, 1- NVM.
- * @offset: offset in the nvm data.
- * @length: of the chunk.
- * @data: empty on read, the NVM chunk on write
- */
-struct iwl_nvm_access_cmd_ver1 {
-       u8 op_code;
-       u8 target;
-       u8 cache_refresh;
-       u8 reserved;
-       __le16 offset;
-       __le16 length;
-       u8 data[];
-} __packed; /* NVM_ACCESS_CMD_API_S_VER_1 */
-
-/**
- * struct iwl_nvm_access_resp_ver1 - response to NVM_ACCESS_CMD
- * @offset: the offset in the nvm data
- * @length: of the chunk
- * @data: the nvm chunk on when NVM_ACCESS_CMD was read, nothing on write
- */
-struct iwl_nvm_access_resp_ver1 {
-       __le16 offset;
-       __le16 length;
-       u8 data[];
-} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_1 */
-
-/* Section types for NVM_ACCESS_CMD version 2 */
+/* Section types for NVM_ACCESS_CMD */
 enum {
        NVM_SECTION_TYPE_HW = 0,
        NVM_SECTION_TYPE_SW,
@@ -323,7 +300,7 @@ enum {
  * @length: in bytes, to read/write
  * @data: if write operation, the data to write. On read its empty
  */
-struct iwl_nvm_access_cmd_ver2 {
+struct iwl_nvm_access_cmd {
        u8 op_code;
        u8 target;
        __le16 type;
@@ -340,7 +317,7 @@ struct iwl_nvm_access_cmd_ver2 {
  * @status: 0 for success, fail otherwise
  * @data: if read operation, the data returned. Empty on write.
  */
-struct iwl_nvm_access_resp_ver2 {
+struct iwl_nvm_access_resp {
        __le16 offset;
        __le16 length;
        __le16 type;
@@ -794,6 +771,7 @@ struct iwl_phy_context_cmd {
  * @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.
@@ -811,7 +789,7 @@ struct iwl_rx_phy_info {
        __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
        __le32 rate_n_flags;
        __le32 byte_count;
-       __le16 reserved2;
+       __le16 mac_active_msk;
        __le16 frame_time;
 } __packed;
 
index 500f818dba0412df8e5a76d018ddaec761529820..e18c92dd60ecdd768c86e23fe6d0f6505f396018 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -114,7 +114,7 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
                .valid = cpu_to_le32(valid_tx_ant),
        };
 
-       IWL_DEBUG_HC(mvm, "select valid tx ant: %u\n", valid_tx_ant);
+       IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant);
        return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC,
                                    sizeof(tx_ant_cmd), &tx_ant_cmd);
 }
@@ -134,9 +134,10 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
        alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
 
        alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK;
-       IWL_DEBUG_FW(mvm, "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n",
+       IWL_DEBUG_FW(mvm,
+                    "Alive ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
                     le16_to_cpu(palive->status), palive->ver_type,
-                    palive->ver_subtype);
+                    palive->ver_subtype, palive->flags);
 
        return true;
 }
@@ -309,6 +310,10 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                goto error;
        }
 
+       ret = iwl_send_bt_prio_tbl(mvm);
+       if (ret)
+               goto error;
+
        if (read_nvm) {
                /* Read nvm */
                ret = iwl_nvm_init(mvm);
@@ -322,16 +327,14 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        WARN_ON(ret);
 
        /* Send TX valid antennas before triggering calibrations */
-       ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+       ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
        if (ret)
                goto error;
 
-       /* WkP doesn't have all calibrations, need to set default values */
-       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
-               ret = iwl_set_default_calibrations(mvm);
-               if (ret)
-                       goto error;
-       }
+       /* need to set default values */
+       ret = iwl_set_default_calibrations(mvm);
+       if (ret)
+               goto error;
 
        /*
         * Send phy configurations command to init uCode
@@ -410,7 +413,15 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                goto error;
        }
 
-       ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+       ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
+       if (ret)
+               goto error;
+
+       ret = iwl_send_bt_prio_tbl(mvm);
+       if (ret)
+               goto error;
+
+       ret = iwl_send_bt_init_conf(mvm);
        if (ret)
                goto error;
 
@@ -456,7 +467,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
                goto error;
        }
 
-       ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+       ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
        if (ret)
                goto error;
 
index 011906e73a05c0e9c43f76208def2815e4d76738..2269a9e5cc6724832d384b031d5907dabcaf4597 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 341dbc0237ea7a6ff62cff843f175cc3f60dabbd..86e312a4f6291d53c1bb366ed13e7d2e1a73381d 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -196,7 +196,7 @@ u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
        u32 qmask, ac;
 
        if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
-               return BIT(IWL_OFFCHANNEL_QUEUE);
+               return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
 
        qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ?
                BIT(vif->cab_queue) : 0;
@@ -553,9 +553,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
        if (vif->bss_conf.qos)
                cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
 
+       /* Don't use cts to self as the fw doesn't support it currently. */
        if (vif->bss_conf.use_cts_prot)
-               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT |
-                                                    MAC_PROT_FLG_SELF_CTS_EN);
+               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
 
        /*
         * I think that we should enable these 2 flags regardless the HT PROT
@@ -651,6 +651,13 @@ static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm,
        /* Fill the common data for all mac context types */
        iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
 
+       /* Allow beacons to pass through as long as we are not associated,or we
+        * do not have dtim period information */
+       if (!vif->bss_conf.assoc || !vif->bss_conf.dtim_period)
+               cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
+       else
+               cmd.filter_flags &= ~cpu_to_le32(MAC_FILTER_IN_BEACON);
+
        /* Fill the data specific for station mode */
        iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta);
 
@@ -685,7 +692,12 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
        WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
 
        iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
-       /* No other data to be filled */
+
+       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);
+
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
 
@@ -714,7 +726,9 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
        iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
 
        cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
-       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROMISC);
+
+       /* 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
@@ -789,7 +803,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
                                             TX_CMD_FLG_TSF);
 
        mvm->mgmt_last_antenna_idx =
-               iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+               iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
                                     mvm->mgmt_last_antenna_idx);
 
        beacon_cmd.tx.rate_n_flags =
@@ -846,10 +860,10 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
  */
 static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
                                         struct ieee80211_vif *vif,
-                                        struct iwl_mac_data_ap *ctxt_ap)
+                                        struct iwl_mac_data_ap *ctxt_ap,
+                                        bool add)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       u32 curr_dev_time;
 
        ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
        ctxt_ap->bi_reciprocal =
@@ -861,10 +875,19 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
                                               vif->bss_conf.dtim_period));
 
        ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue);
-       curr_dev_time = iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
-       ctxt_ap->beacon_time = cpu_to_le32(curr_dev_time);
 
-       ctxt_ap->beacon_tsf = cpu_to_le64(curr_dev_time);
+       /*
+        * Only read the system 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)
+               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);
@@ -881,8 +904,12 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
        /* Fill the common data for all mac context types */
        iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
 
+       /* Also enable probe requests to pass */
+       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+
        /* Fill the data specific for ap mode */
-       iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap);
+       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);
 }
@@ -899,7 +926,8 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
        iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
 
        /* Fill the data specific for GO mode */
-       iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap);
+       iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap,
+                                    action == FW_CTXT_ACTION_ADD);
 
        cmd.go.ctwin = cpu_to_le32(vif->bss_conf.p2p_ctwindow);
        cmd.go.opp_ps_enabled = cpu_to_le32(!!vif->bss_conf.p2p_oppps);
@@ -990,3 +1018,22 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        mvmvif->uploaded = false;
        return 0;
 }
+
+int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
+                           struct iwl_rx_cmd_buffer *rxb,
+                           struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_beacon_notif *beacon = (void *)pkt->data;
+       u16 status __maybe_unused =
+               le16_to_cpu(beacon->beacon_notify_hdr.status.status);
+       u32 rate __maybe_unused =
+               le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
+
+       IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
+                    status & TX_STATUS_MSK,
+                    beacon->beacon_notify_hdr.failure_frame,
+                    le64_to_cpu(beacon->tsf),
+                    rate);
+       return 0;
+}
index 7e169b085afe05f1c4c5a17804e864d8bc15e9f2..3d193f8c33b618cadc50bad99626f973b275085a 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -65,7 +65,9 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ip.h>
 #include <net/mac80211.h>
+#include <net/tcp.h>
 
 #include "iwl-op-mode.h"
 #include "iwl-io.h"
@@ -102,10 +104,33 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
        },
 };
 
+#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
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
-       int num_mac, ret;
+       int num_mac, ret, i;
 
        /* Tell mac80211 our characteristics */
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
@@ -118,8 +143,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TIMING_BEACON_ONLY;
 
-       hw->queues = IWL_FIRST_AMPDU_QUEUE;
-       hw->offchannel_tx_hw_queue = IWL_OFFCHANNEL_QUEUE;
+       hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
+       hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
        hw->rate_control_algorithm = "iwl-mvm-rs";
 
        /*
@@ -149,18 +174,22 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(iwl_mvm_iface_combinations);
 
-       hw->wiphy->max_remain_on_channel_duration = 500;
+       hw->wiphy->max_remain_on_channel_duration = 10000;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
 
        /* 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;
-       num_mac = mvm->nvm_data->n_hw_addrs;
-       if (num_mac > 1) {
-               memcpy(mvm->addresses[1].addr, mvm->addresses[0].addr,
+
+       /* 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[1].addr[5]++;
+               mvm->addresses[i].addr[5]++;
                hw->wiphy->n_addresses++;
        }
 
@@ -206,6 +235,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
                hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
                hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+               hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
        }
 #endif
 
@@ -227,7 +257,7 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
                goto drop;
        }
 
-       if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_OFFCHANNEL_QUEUE &&
+       if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
            !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
                goto drop;
 
@@ -273,12 +303,18 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false);
                break;
        case IEEE80211_AMPDU_TX_START:
+               if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) {
+                       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_stop(mvm, vif, sta, tid);
+               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);
@@ -1051,6 +1087,13 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
 
        switch (cmd) {
        case SET_KEY:
+               if (vif->type == NL80211_IFTYPE_AP && !sta) {
+                       /* GTK on AP interface is a TX-only key, return 0 */
+                       ret = 0;
+                       key->hw_key_idx = STA_KEY_IDX_INVALID;
+                       break;
+               }
+
                IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
                ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, false);
                if (ret) {
@@ -1059,11 +1102,17 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
                         * 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;
@@ -1090,7 +1139,8 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
 static int iwl_mvm_roc(struct ieee80211_hw *hw,
                       struct ieee80211_vif *vif,
                       struct ieee80211_channel *channel,
-                      int duration)
+                      int duration,
+                      enum ieee80211_roc_type type)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct cfg80211_chan_def chandef;
@@ -1101,8 +1151,8 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
                return -EINVAL;
        }
 
-       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d)\n", channel->hw_value,
-                          duration);
+       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+                          duration, type);
 
        mutex_lock(&mvm->mutex);
 
@@ -1111,7 +1161,7 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
                                       &chandef, 1, 1);
 
        /* Schedule the time events */
-       ret = iwl_mvm_start_p2p_roc(mvm, vif, duration);
+       ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
 
        mutex_unlock(&mvm->mutex);
        IWL_DEBUG_MAC80211(mvm, "leave\n");
@@ -1215,6 +1265,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
         * will handle quota settings.
         */
        if (vif->type == NL80211_IFTYPE_MONITOR) {
+               mvmvif->monitor_active = true;
                ret = iwl_mvm_update_quotas(mvm, vif);
                if (ret)
                        goto out_remove_binding;
@@ -1245,15 +1296,16 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
        if (vif->type == NL80211_IFTYPE_AP)
                goto out_unlock;
 
-       iwl_mvm_binding_remove_vif(mvm, vif);
        switch (vif->type) {
        case NL80211_IFTYPE_MONITOR:
-               iwl_mvm_update_quotas(mvm, vif);
+               mvmvif->monitor_active = false;
+               iwl_mvm_update_quotas(mvm, NULL);
                break;
        default:
                break;
        }
 
+       iwl_mvm_binding_remove_vif(mvm, vif);
 out_unlock:
        mvmvif->phy_ctxt = NULL;
        mutex_unlock(&mvm->mutex);
index bdae700c769eb292223d8871b1789a46f6f13610..53d58968e30ace324600e2eef6112973282058ec 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -79,7 +79,7 @@
 #include "fw-api.h"
 
 #define IWL_INVALID_MAC80211_QUEUE     0xff
-#define IWL_MVM_MAX_ADDRESSES          2
+#define IWL_MVM_MAX_ADDRESSES          5
 /* RSSI offset for WkP */
 #define IWL_RSSI_OFFSET 50
 
@@ -90,10 +90,6 @@ enum iwl_mvm_tx_fifo {
        IWL_MVM_TX_FIFO_VO,
 };
 
-/* Placeholder */
-#define IWL_OFFCHANNEL_QUEUE 8
-#define IWL_FIRST_AMPDU_QUEUE 11
-
 extern struct ieee80211_ops iwl_mvm_hw_ops;
 /**
  * struct iwl_mvm_mod_params - module parameters for iwlmvm
@@ -161,6 +157,8 @@ enum iwl_power_scheme {
  * @uploaded: indicates the MAC context has been added to the device
  * @ap_active: indicates that ap context is configured, and that the interface
  *  should get quota etc.
+ * @monitor_active: indicates that monitor context is configured, and that the
+ * interface should get quota etc.
  * @queue_params: QoS params for this MAC
  * @bcast_sta: station used for broadcast packets. Used by the following
  *  vifs: P2P_DEVICE, GO and AP.
@@ -173,6 +171,9 @@ struct iwl_mvm_vif {
 
        bool uploaded;
        bool ap_active;
+       bool monitor_active;
+
+       u32 ap_beacon_time;
 
        enum iwl_tsf_id tsf_id;
 
@@ -279,10 +280,7 @@ struct iwl_mvm {
        atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
 
        struct iwl_nvm_data *nvm_data;
-       /* eeprom blob for debugfs/testmode */
-       u8 *eeprom_blob;
-       size_t eeprom_blob_size;
-       /* NVM sections for 7000 family */
+       /* NVM sections */
        struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS];
 
        /* EEPROM MAC addresses */
@@ -332,6 +330,10 @@ struct iwl_mvm {
 #ifdef CONFIG_PM_SLEEP
        int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
 #endif
+
+       /* BT-Coex */
+       u8 bt_kill_msk;
+       struct iwl_bt_coex_profile_notif last_bt_notif;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -445,6 +447,9 @@ u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
                                struct ieee80211_vif *vif);
 int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif);
+int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
+                           struct iwl_rx_cmd_buffer *rxb,
+                           struct iwl_device_cmd *cmd);
 
 /* Bindings */
 int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -502,4 +507,11 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
 void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif, int idx);
 
+/* BT Coex */
+int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
+int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
+int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
+                            struct iwl_rx_cmd_buffer *rxb,
+                            struct iwl_device_cmd *cmd);
+
 #endif /* __IWL_MVM_H__ */
index 20016bcbdeabec575e984809618015eba60f96e1..b8ec02f89acc86848a8514f0679c3369abf9ef91 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -74,26 +74,11 @@ static const int nvm_to_read[] = {
        NVM_SECTION_TYPE_PRODUCTION,
 };
 
-/* used to simplify the shared operations on NCM_ACCESS_CMD versions */
-union iwl_nvm_access_cmd {
-       struct iwl_nvm_access_cmd_ver1 ver1;
-       struct iwl_nvm_access_cmd_ver2 ver2;
-};
-union iwl_nvm_access_resp {
-       struct iwl_nvm_access_resp_ver1 ver1;
-       struct iwl_nvm_access_resp_ver2 ver2;
-};
-
-static inline void iwl_nvm_fill_read_ver1(struct iwl_nvm_access_cmd_ver1 *cmd,
-                                         u16 offset, u16 length)
-{
-       cmd->offset = cpu_to_le16(offset);
-       cmd->length = cpu_to_le16(length);
-       cmd->cache_refresh = 1;
-}
+/* Default NVM size to read */
+#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024);
 
-static inline void iwl_nvm_fill_read_ver2(struct iwl_nvm_access_cmd_ver2 *cmd,
-                                         u16 offset, u16 length, u16 section)
+static inline void iwl_nvm_fill_read(struct iwl_nvm_access_cmd *cmd,
+                                    u16 offset, u16 length, u16 section)
 {
        cmd->offset = cpu_to_le16(offset);
        cmd->length = cpu_to_le16(length);
@@ -103,8 +88,8 @@ static inline void iwl_nvm_fill_read_ver2(struct iwl_nvm_access_cmd_ver2 *cmd,
 static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
                              u16 offset, u16 length, u8 *data)
 {
-       union iwl_nvm_access_cmd nvm_access_cmd;
-       union iwl_nvm_access_resp *nvm_resp;
+       struct iwl_nvm_access_cmd nvm_access_cmd = {};
+       struct iwl_nvm_access_resp *nvm_resp;
        struct iwl_rx_packet *pkt;
        struct iwl_host_cmd cmd = {
                .id = NVM_ACCESS_CMD,
@@ -114,18 +99,8 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
        int ret, bytes_read, offset_read;
        u8 *resp_data;
 
-       memset(&nvm_access_cmd, 0, sizeof(nvm_access_cmd));
-
-       /* TODO: not sure family should be the decider, maybe FW version? */
-       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
-               iwl_nvm_fill_read_ver2(&(nvm_access_cmd.ver2),
-                                      offset, length, section);
-               cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver2);
-       } else {
-               iwl_nvm_fill_read_ver1(&(nvm_access_cmd.ver1),
-                                      offset, length);
-               cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver1);
-       }
+       iwl_nvm_fill_read(&nvm_access_cmd, offset, length, section);
+       cmd.len[0] = sizeof(struct iwl_nvm_access_cmd);
 
        ret = iwl_mvm_send_cmd(mvm, &cmd);
        if (ret)
@@ -141,17 +116,10 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
 
        /* Extract NVM response */
        nvm_resp = (void *)pkt->data;
-       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
-               ret = le16_to_cpu(nvm_resp->ver2.status);
-               bytes_read = le16_to_cpu(nvm_resp->ver2.length);
-               offset_read = le16_to_cpu(nvm_resp->ver2.offset);
-               resp_data = nvm_resp->ver2.data;
-       } else {
-               ret = le16_to_cpu(nvm_resp->ver1.length) <= 0;
-               bytes_read = le16_to_cpu(nvm_resp->ver1.length);
-               offset_read = le16_to_cpu(nvm_resp->ver1.offset);
-               resp_data = nvm_resp->ver1.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) {
                IWL_ERR(mvm,
                        "NVM access command failed with status %d (device: %s)\n",
@@ -191,17 +159,10 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
 {
        u16 length, offset = 0;
        int ret;
-       bool old_eeprom = mvm->cfg->device_family != IWL_DEVICE_FAMILY_7000;
 
-       length = (iwlwifi_mod_params.amsdu_size_8K ? (8 * 1024) : (4 * 1024))
-               - sizeof(union iwl_nvm_access_cmd)
-               - sizeof(struct iwl_rx_packet);
-       /*
-        * if length is greater than EEPROM size, truncate it because uCode
-        * doesn't check it by itself, and exit the loop when reached.
-        */
-       if (old_eeprom && length > mvm->cfg->base_params->eeprom_size)
-               length = mvm->cfg->base_params->eeprom_size;
+       /* Set nvm section read length */
+       length = IWL_NVM_DEFAULT_CHUNK_SIZE;
+
        ret = length;
 
        /* Read the NVM until exhausted (reading less than requested) */
@@ -214,8 +175,6 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
                        return ret;
                }
                offset += ret;
-               if (old_eeprom && offset == mvm->cfg->base_params->eeprom_size)
-                       break;
        }
 
        IWL_INFO(mvm, "NVM section %d read completed\n", section);
@@ -249,63 +208,31 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
        int ret, i, section;
        u8 *nvm_buffer, *temp;
 
-       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
-               /* TODO: find correct NVM max size for a section */
-               nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
-                                    GFP_KERNEL);
-               if (!nvm_buffer)
-                       return -ENOMEM;
-               for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
-                       section = nvm_to_read[i];
-                       /* we override the constness for initial read */
-                       ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
-                       if (ret < 0)
-                               break;
-                       temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
-                       if (!temp) {
-                               ret = -ENOMEM;
-                               break;
-                       }
-                       mvm->nvm_sections[section].data = temp;
-                       mvm->nvm_sections[section].length = ret;
-               }
-               kfree(nvm_buffer);
+       /* TODO: find correct NVM max size for a section */
+       nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
+                            GFP_KERNEL);
+       if (!nvm_buffer)
+               return -ENOMEM;
+       for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+               section = nvm_to_read[i];
+               /* we override the constness for initial read */
+               ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
                if (ret < 0)
-                       return ret;
-       } else {
-               /* allocate eeprom */
-               mvm->eeprom_blob_size = mvm->cfg->base_params->eeprom_size;
-               IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM size = %zd\n",
-                                mvm->eeprom_blob_size);
-               mvm->eeprom_blob = kzalloc(mvm->eeprom_blob_size, GFP_KERNEL);
-               if (!mvm->eeprom_blob)
-                       return -ENOMEM;
-
-               ret = iwl_nvm_read_section(mvm, 0, mvm->eeprom_blob);
-               if (ret != mvm->eeprom_blob_size) {
-                       IWL_ERR(mvm, "Read partial NVM %d/%zd\n",
-                               ret, mvm->eeprom_blob_size);
-                       kfree(mvm->eeprom_blob);
-                       mvm->eeprom_blob = NULL;
-                       return -EINVAL;
+                       break;
+               temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
+               if (!temp) {
+                       ret = -ENOMEM;
+                       break;
                }
+               mvm->nvm_sections[section].data = temp;
+               mvm->nvm_sections[section].length = ret;
        }
+       kfree(nvm_buffer);
+       if (ret < 0)
+               return ret;
 
        ret = 0;
-       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
-               mvm->nvm_data = iwl_parse_nvm_sections(mvm);
-       else
-               mvm->nvm_data =
-                       iwl_parse_eeprom_data(mvm->trans->dev,
-                                             mvm->cfg,
-                                             mvm->eeprom_blob,
-                                             mvm->eeprom_blob_size);
-
-       if (!mvm->nvm_data) {
-               kfree(mvm->eeprom_blob);
-               mvm->eeprom_blob = NULL;
-               ret = -ENOMEM;
-       }
+       mvm->nvm_data = iwl_parse_nvm_sections(mvm);
 
        return ret;
 }
index d0f9c1e0475e71659576b609b8f3365196435f02..fe031d304d1eddfcdf7131cb3bf172d54a6121d0 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -143,21 +143,12 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
        u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash;
        u32 reg_val = 0;
 
-       /*
-        * We can't upload the correct value to the INIT image
-        * as we don't have nvm_data by that time.
-        *
-        * TODO: Figure out what we should do here
-        */
-       if (mvm->nvm_data) {
-               radio_cfg_type = mvm->nvm_data->radio_cfg_type;
-               radio_cfg_step = mvm->nvm_data->radio_cfg_step;
-               radio_cfg_dash = mvm->nvm_data->radio_cfg_dash;
-       } else {
-               radio_cfg_type = 0;
-               radio_cfg_step = 0;
-               radio_cfg_dash = 0;
-       }
+       radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >>
+                         FW_PHY_CFG_RADIO_TYPE_POS;
+       radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >>
+                         FW_PHY_CFG_RADIO_STEP_POS;
+       radio_cfg_dash = (mvm->fw->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) <<
@@ -175,7 +166,6 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
 
        /* silicon bits */
        reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
-       reg_val |= CSR_HW_IF_CONFIG_REG_BIT_MAC_SI;
 
        iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
                                CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
@@ -230,6 +220,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
        RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
 
+       RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
+       RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
+
        RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
        RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
 
@@ -274,6 +267,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(WEP_KEY),
        CMD(REPLY_RX_PHY_CMD),
        CMD(REPLY_RX_MPDU_CMD),
+       CMD(BEACON_NOTIFICATION),
        CMD(BEACON_TEMPLATE_CMD),
        CMD(STATISTICS_NOTIFICATION),
        CMD(TX_ANT_CONFIGURATION_CMD),
@@ -293,6 +287,11 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(NET_DETECT_PROFILES_CMD),
        CMD(NET_DETECT_HOTSPOTS_CMD),
        CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
+       CMD(CARD_STATE_NOTIFICATION),
+       CMD(BT_COEX_PRIO_TABLE),
+       CMD(BT_COEX_PROT_ENV),
+       CMD(BT_PROFILE_NOTIFICATION),
+       CMD(BT_CONFIG),
 };
 #undef CMD
 
@@ -312,16 +311,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        };
        int err, scan_size;
 
-       switch (cfg->device_family) {
-       case IWL_DEVICE_FAMILY_6030:
-       case IWL_DEVICE_FAMILY_6005:
-       case IWL_DEVICE_FAMILY_7000:
-               break;
-       default:
-               IWL_ERR(trans, "Trying to load mvm on an unsupported device\n");
-               return NULL;
-       }
-
        /********************************
         * 1. Allocating and configuring HW data
         ********************************/
@@ -363,8 +352,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
        trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
 
-       /* TODO: this should really be a TLV */
-       if (cfg->device_family == IWL_DEVICE_FAMILY_7000)
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE)
                trans_cfg.bc_table_dword = true;
 
        if (!iwlwifi_mod_params.wd_disable)
@@ -438,7 +426,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
  out_free:
        iwl_phy_db_free(mvm->phy_db);
        kfree(mvm->scan_cmd);
-       kfree(mvm->eeprom_blob);
        iwl_trans_stop_hw(trans, true);
        ieee80211_free_hw(mvm->hw);
        return NULL;
@@ -460,7 +447,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        iwl_phy_db_free(mvm->phy_db);
        mvm->phy_db = NULL;
 
-       kfree(mvm->eeprom_blob);
        iwl_free_nvm_data(mvm->nvm_data);
        for (i = 0; i < NVM_NUM_OF_SECTIONS; i++)
                kfree(mvm->nvm_sections[i].data);
index b428448f8ddfb80293723cd5726912cafcf3998b..0f0b44eabd937a88ce615e3f6a5fe5ada0fe805f 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -142,7 +142,7 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
                                      struct cfg80211_chan_def *chandef,
                                      u8 chains_static, u8 chains_dynamic)
 {
-       u8 valid_rx_chains, active_cnt, idle_cnt;
+       u8 active_cnt, idle_cnt;
 
        /* Set the channel info data */
        cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
@@ -158,17 +158,16 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
         * Need to add on chain noise calibration limitations, and
         * BT coex considerations.
         */
-       valid_rx_chains = mvm->nvm_data->valid_rx_ant;
        idle_cnt = chains_static;
        active_cnt = chains_dynamic;
 
-       cmd->rxchain_info = cpu_to_le32(valid_rx_chains <<
+       cmd->rxchain_info = cpu_to_le32(iwl_fw_valid_rx_ant(mvm->fw) <<
                                        PHY_RX_CHAIN_VALID_POS);
        cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
        cmd->rxchain_info |= cpu_to_le32(active_cnt <<
                                         PHY_RX_CHAIN_MIMO_CNT_POS);
 
-       cmd->txchain_info = cpu_to_le32(mvm->nvm_data->valid_tx_ant);
+       cmd->txchain_info = cpu_to_le32(iwl_fw_valid_tx_ant(mvm->fw));
 }
 
 /*
index 5a92a4978795420acbd363ca0ca94037e47f5b28..efb9a6f3faac1448d6004fe7d3d4feecc8fcd74a 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 9256284681466fe416ffc84c10c0cc74a14d342e..a1e3e923ea3e12c1fb6a0adabab88d05b1c5530e 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -114,7 +114,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
                        data->n_interfaces[id]++;
                break;
        case NL80211_IFTYPE_MONITOR:
-               data->n_interfaces[id]++;
+               if (mvmvif->monitor_active)
+                       data->n_interfaces[id]++;
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
index 56b636d9ab304b301486761daaf39c0e6a99fc9c..a01a6612677e8f4db9deca7658b2f3a125210e35 100644 (file)
@@ -680,12 +680,14 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
  */
 static bool rs_use_green(struct ieee80211_sta *sta)
 {
-       struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv;
-
-       bool use_green = !(sta_priv->vif->bss_conf.ht_operation_mode &
-                               IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
-
-       return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && use_green;
+       /*
+        * 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;
 }
 
 /**
index b0b190d0ec23659279f9a33e7d57b21428114f30..4dfc21a3e83ed3a1ab127baf1a7864a2fdc4cc9d 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
index 9b21b92aa8d1d426cc94b91f15859e11637badd0..2157b0f8ced5cc7c314a246d758a869128e57c59 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -74,7 +74,7 @@
 static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
 {
        u16 rx_chain;
-       u8 rx_ant = mvm->nvm_data->valid_rx_ant;
+       u8 rx_ant = iwl_fw_valid_rx_ant(mvm->fw);
 
        rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
        rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
@@ -115,7 +115,7 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
        u32 tx_ant;
 
        mvm->scan_last_antenna_idx =
-               iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+               iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
                                     mvm->scan_last_antenna_idx);
        tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
 
index 274f44e2ef60e52d1fb39f66735c98563036449e..4d872d69577fe17b02a0670a7e0f36791ffd2bcc 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -101,8 +101,55 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        }
        add_sta_cmd.add_modify = update ? 1 : 0;
 
-       /* STA_FLG_FAT_EN_MSK ? */
-       /* STA_FLG_MIMO_EN_MSK ? */
+       add_sta_cmd.station_flags_msk |= cpu_to_le32(STA_FLG_FAT_EN_MSK |
+                                                    STA_FLG_MIMO_EN_MSK);
+
+       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 |=
@@ -340,6 +387,9 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
 
        if (vif->type == NL80211_IFTYPE_STATION &&
            mvmvif->ap_sta_id == mvm_sta->sta_id) {
+               /* flush its queues here since we are freeing mvm_sta */
+               ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+
                /*
                 * Put a non-NULL since the fw station isn't removed.
                 * It will be removed after the MAC will be set as
@@ -348,9 +398,6 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
                                   ERR_PTR(-EINVAL));
 
-               /* flush its queues here since we are freeing mvm_sta */
-               ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
-
                /* if we are associated - we can't remove the AP STA now */
                if (vif->bss_conf.assoc)
                        return ret;
@@ -686,7 +733,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        spin_lock_bh(&mvmsta->lock);
        tid_data = &mvmsta->tid_data[tid];
-       tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+       tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
        tid_data->txq_id = txq_id;
        *ssn = tid_data->ssn;
 
@@ -789,7 +836,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        switch (tid_data->state) {
        case IWL_AGG_ON:
-               tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+               tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
 
                IWL_DEBUG_TX_QUEUES(mvm,
                                    "ssn = %d, next_recl = %d\n",
@@ -834,6 +881,34 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        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 = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       u16 txq_id;
+
+       /*
+        * 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);
+       tid_data->state = IWL_AGG_OFF;
+       spin_unlock_bh(&mvmsta->lock);
+
+       if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
+               IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+
+       iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
+       mvm->queue_to_mac80211[tid_data->txq_id] =
+                               IWL_INVALID_MAC80211_QUEUE;
+
+       return 0;
+}
+
 static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
 {
        int i;
index 896f88ac814579a7b2912ed21fb7d7f7d3032758..b0352df981e4539565ffac6afe7213604aa39e35 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -348,6 +348,8 @@ 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);
 int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
index e437e02c714963dc050a0701df1e9183bb1983ee..4dc934bed055131c275d1cc4992f5bd7200723ca 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
 #define TU_TO_JIFFIES(_tu)     (usecs_to_jiffies((_tu) * 1024))
 #define MSEC_TO_TU(_msec)      (_msec*1000/1024)
 
-/* For ROC use a TE type which has priority high enough to be scheduled when
- * there is a concurrent BSS or GO/AP. Currently, use a TE type that has
- * priority similar to the TE priority used for action scans by the FW.
- * TODO: This needs to be changed, based on the reason for the ROC, i.e., use
- * TE_P2P_DEVICE_DISCOVERABLE for remain on channel without mgmt skb, and use
- * TE_P2P_DEVICE_ACTION_SCAN
+/*
+ * 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 TE_P2P_DEVICE_ACTION_SCAN
+#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)
@@ -116,7 +114,7 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)
         * 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, BIT(IWL_OFFCHANNEL_QUEUE), false);
+       iwl_mvm_flush_tx_path(mvm, BIT(IWL_MVM_OFFCHANNEL_QUEUE), false);
 }
 
 static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
@@ -438,7 +436,7 @@ void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
 }
 
 int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                         int duration)
+                         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;
@@ -459,21 +457,29 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *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(IWL_MVM_ROC_TE_TYPE);
+
+       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.dep_policy = cpu_to_le32(TE_INDEPENDENT);
        time_cmd.is_present = cpu_to_le32(1);
-
        time_cmd.interval = cpu_to_le32(1);
 
        /*
-        * IWL_MVM_ROC_TE_TYPE can have lower priority than other events
+        * 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 it to
-        * be fragmented.
-        * In addition, for the same reasons, allow to delay the scheduling of
-        * the time event.
+        * scheduled. To improve the chances of it being scheduled, allow them
+        * to be fragmented, and in addition allow them to be delayed.
         */
        time_cmd.max_frags = cpu_to_le32(MSEC_TO_TU(duration)/20);
        time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
index 64fb57a5ab43b03f7f7c0a9db0b7ac28764a888d..f86c51065ed3afe1e860f8fb15d50cc1f6eaef9b 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -162,6 +162,7 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
  * 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
@@ -172,7 +173,7 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
  * another notification to the driver.
  */
 int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                         int duration);
+                         int duration, enum ieee80211_roc_type type);
 
 /**
  * iwl_mvm_stop_p2p_roc - stop remain on channel for p2p device functionlity
index 6645efe5c03edfdc3c4523f0c06ab1d7bc0c7629..0acc0bff43c7933f6a67346741d6d4272f337eca 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -417,7 +417,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
        spin_unlock(&mvmsta->lock);
 
        if (mvmsta->vif->type == NL80211_IFTYPE_AP &&
-           txq_id < IWL_FIRST_AMPDU_QUEUE)
+           txq_id < IWL_MVM_FIRST_AGG_QUEUE)
                atomic_inc(&mvmsta->pending_frames);
 
        return 0;
@@ -606,7 +606,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                                             info);
 
                /* Single frame failure in an AMPDU queue => send BAR */
-               if (txq_id >= IWL_FIRST_AMPDU_QUEUE &&
+               if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE &&
                    !(info->flags & IEEE80211_TX_STAT_ACK))
                        info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
 
@@ -619,7 +619,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                ieee80211_tx_status_ni(mvm->hw, skb);
        }
 
-       if (txq_id >= IWL_FIRST_AMPDU_QUEUE) {
+       if (txq_id >= IWL_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
@@ -637,7 +637,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                next_reclaimed = ssn;
        } else {
                /* The next packet to be reclaimed is the one after this one */
-               next_reclaimed = SEQ_TO_SN(seq_ctl + 0x10);
+               next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10);
        }
 
        IWL_DEBUG_TX_REPLY(mvm,
@@ -681,7 +681,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
         * If there are no pending frames for this STA, notify mac80211 that
         * this station can go to sleep in its STA table.
         */
-       if (txq_id < IWL_FIRST_AMPDU_QUEUE && mvmsta &&
+       if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && mvmsta &&
            !WARN_ON(skb_freed > 1) &&
            mvmsta->vif->type == NL80211_IFTYPE_AP &&
            atomic_sub_and_test(skb_freed, &mvmsta->pending_frames)) {
@@ -750,7 +750,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
        u16 sequence = le16_to_cpu(pkt->hdr.sequence);
        struct ieee80211_sta *sta;
 
-       if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_FIRST_AMPDU_QUEUE))
+       if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_MVM_FIRST_AGG_QUEUE))
                return;
 
        if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
index 000e842c2eddb340e00d971859fe83c048cde3e2..e308ad93aa9e1556cf18c4ce5eef3ef7ed5aa7bb 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/pcie/1000.c b/drivers/net/wireless/iwlwifi/pcie/1000.c
deleted file mode 100644 (file)
index ff33897..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called 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"
-#include "cfg.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,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
-       .wd_timeout = IWL_WATCHDOG_DISABLED,
-       .max_event_log_size = 128,
-};
-
-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
-
-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
-
-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/pcie/2000.c b/drivers/net/wireless/iwlwifi/pcie/2000.c
deleted file mode 100644 (file)
index e7de331..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called 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 "cfg.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,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
-       .wd_timeout = IWL_DEF_WD_TIMEOUT,
-       .max_event_log_size = 512,
-       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
-       .hd_v2 = 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,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
-       .wd_timeout = IWL_LONG_WD_TIMEOUT,
-       .max_event_log_size = 512,
-       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
-       .hd_v2 = 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_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,
-};
-
-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,                \
-       .need_temp_offset_calib = true,                         \
-       .temp_offset_v2 = true,                                 \
-       .led_mode = IWL_LED_RF_STATE
-
-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,                    \
-       .bt_params = &iwl2030_bt_params,                        \
-       .eeprom_params = &iwl20x0_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .temp_offset_v2 = true,                                 \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true
-
-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,                \
-       .need_temp_offset_calib = true,                         \
-       .temp_offset_v2 = true,                                 \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true,                                         \
-       .rx_with_siso_diversity = true
-
-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,                    \
-       .bt_params = &iwl2030_bt_params,                        \
-       .eeprom_params = &iwl20x0_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .temp_offset_v2 = true,                                 \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true,                                         \
-       .rx_with_siso_diversity = true
-
-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/pcie/5000.c b/drivers/net/wireless/iwlwifi/pcie/5000.c
deleted file mode 100644 (file)
index 5096f7c..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called 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"
-#include "cfg.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,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
-       .wd_timeout = IWL_WATCHDOG_DISABLED,
-       .max_event_log_size = 512,
-       .no_idle_support = 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
-
-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,                \
-       .no_xtal_calib = true,                                  \
-       .led_mode = IWL_LED_BLINK,                              \
-       .internal_wimax_coex = true
-
-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/pcie/6000.c b/drivers/net/wireless/iwlwifi/pcie/6000.c
deleted file mode 100644 (file)
index 801ff49..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called 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 "cfg.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,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
-       .wd_timeout = IWL_DEF_WD_TIMEOUT,
-       .max_event_log_size = 512,
-       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
-};
-
-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,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1500,
-       .wd_timeout = IWL_DEF_WD_TIMEOUT,
-       .max_event_log_size = 1024,
-       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
-};
-
-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,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
-       .wd_timeout = IWL_LONG_WD_TIMEOUT,
-       .max_event_log_size = 512,
-       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
-};
-
-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_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,
-};
-
-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,                \
-       .need_temp_offset_calib = true,                         \
-       .led_mode = IWL_LED_RF_STATE
-
-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,                 \
-       .bt_params = &iwl6000_bt_params,                        \
-       .eeprom_params = &iwl6000_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true                                          \
-
-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,                 \
-       .bt_params = &iwl6000_bt_params,                        \
-       .eeprom_params = &iwl6000_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true
-
-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 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
-
-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
-
-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
-
-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/pcie/7000.c b/drivers/net/wireless/iwlwifi/pcie/7000.c
deleted file mode 100644 (file)
index 6e35b2b..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called 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 "cfg.h"
-
-/* Highest firmware API version supported */
-#define IWL7260_UCODE_API_MAX  6
-#define IWL3160_UCODE_API_MAX  6
-
-/* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK   6
-#define IWL3160_UCODE_API_OK   6
-
-/* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN  6
-#define IWL3160_UCODE_API_MIN  6
-
-/* 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 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"
-
-static const struct iwl_base_params iwl7000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
-       .num_of_queues = IWLAGN_NUM_QUEUES,
-       .pll_cfg_val = 0,
-       .shadow_ram_support = true,
-       .led_compensation = 57,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
-       .wd_timeout = IWL_LONG_WD_TIMEOUT,
-       .max_event_log_size = 512,
-       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
-};
-
-static const struct iwl_ht_params iwl7000_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),
-};
-
-#define IWL_DEVICE_7000                                                \
-       .ucode_api_max = IWL7260_UCODE_API_MAX,                 \
-       .ucode_api_ok = IWL7260_UCODE_API_OK,                   \
-       .ucode_api_min = IWL7260_UCODE_API_MIN,                 \
-       .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,                    \
-       /* TODO: .bt_params? */                                 \
-       .need_temp_offset_calib = true,                         \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true                                          \
-
-
-const struct iwl_cfg iwl7260_2ac_cfg = {
-       .name = "Intel(R) Dual Band Wireless AC7260",
-       .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,
-};
-
-const struct iwl_cfg iwl3160_ac_cfg = {
-       .name = "Intel(R) Dual Band Wireless AC3160",
-       .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,
-};
-
-MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
-MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/pcie/cfg.h b/drivers/net/wireless/iwlwifi/pcie/cfg.h
deleted file mode 100644 (file)
index c6f8e83..0000000
+++ /dev/null
@@ -1,115 +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 - 2013 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * 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 - 2013 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __iwl_pci_h__
-#define __iwl_pci_h__
-
-
-/*
- * This file declares the config structures for all devices.
- */
-
-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 iwl105_bgn_cfg;
-extern const struct iwl_cfg iwl105_bgn_d_cfg;
-extern const struct iwl_cfg iwl135_bgn_cfg;
-extern const struct iwl_cfg iwl7260_2ac_cfg;
-extern const struct iwl_cfg iwl3160_ac_cfg;
-
-#endif /* __iwl_pci_h__ */
index 7bc0fb9128ddc86f7cddfe63001a500f7430271d..46ca91f77c9c52de8654d3d95fc0bb820ca14c6a 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -69,8 +69,6 @@
 
 #include "iwl-trans.h"
 #include "iwl-drv.h"
-
-#include "cfg.h"
 #include "internal.h"
 
 #define IWL_PCI_DEVICE(dev, subdev, cfg) \
index 12c4f31ca8fbddcc95925cebf4b269b150a64ef2..50ba0a468f94baa59dede7d4c50c8a3a2f960b35 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -728,7 +728,8 @@ static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)
 
 static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg)
 {
-       iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, reg | (3 << 24));
+       iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR,
+                              ((reg & 0x000FFFFF) | (3 << 24)));
        return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT);
 }
 
@@ -736,7 +737,7 @@ 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 & 0x0000FFFF) | (3 << 24)));
+                              ((addr & 0x000FFFFF) | (3 << 24)));
        iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);
 }
 
@@ -1383,28 +1384,11 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
        return ret;
 }
 
-static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
-                                         const char __user *user_buf,
-                                         size_t count, loff_t *ppos)
-{
-       struct iwl_trans *trans = file->private_data;
-
-       if (!trans->op_mode)
-               return -EAGAIN;
-
-       local_bh_disable();
-       iwl_op_mode_nic_error(trans->op_mode);
-       local_bh_enable();
-
-       return count;
-}
-
 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);
-DEBUGFS_WRITE_FILE_OPS(fw_restart);
 
 /*
  * Create the debugfs files and directories
@@ -1418,7 +1402,6 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
        DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
        DEBUGFS_ADD_FILE(csr, dir, S_IWUSR);
        DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR);
-       DEBUGFS_ADD_FILE(fw_restart, dir, S_IWUSR);
        return 0;
 
 err:
index cb5c6792e3a8de2679022370c140263b8e1e9e57..68466ca8077082ee77fa2a95ed6507aa3a0e8bb2 100644 (file)
@@ -501,10 +501,8 @@ static int iwl_pcie_txq_alloc(struct iwl_trans *trans,
         * shared with device */
        txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz,
                                       &txq->q.dma_addr, GFP_KERNEL);
-       if (!txq->tfds) {
-               IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz);
+       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) !=
@@ -1566,8 +1564,11 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
        if (test_bit(STATUS_FW_ERROR, &trans_pcie->status))
                return -EIO;
 
-       if (test_bit(STATUS_RFKILL, &trans_pcie->status))
+       if (test_bit(STATUS_RFKILL, &trans_pcie->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);
@@ -1609,7 +1610,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
         * Check here that the packets are in the right place on the ring.
         */
 #ifdef CONFIG_IWLWIFI_DEBUG
-       wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
+       wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
        WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) &&
                  ((wifi_seq & 0xff) != q->write_ptr),
                  "Q: %d WiFi Seq %d tfdNum %d",
index cffdf4fbf161e63e615e0fc33564eb973248bc33..0064d38276bf6a714d86e7c310f49f1ef91a5d35 100644 (file)
@@ -964,6 +964,12 @@ static int mac80211_hwsim_change_interface(struct ieee80211_hw *hw,
                    newtype, vif->addr);
        hwsim_check_magic(vif);
 
+       /*
+        * interface may change from non-AP to AP in
+        * which case this needs to be set up again
+        */
+       vif->cab_queue = 0;
+
        return 0;
 }
 
@@ -1389,7 +1395,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
        return 0;
 }
 
-static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
+static void mac80211_hwsim_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        /* Not implemented, queues only on kernel side */
 }
@@ -1535,7 +1541,8 @@ static void hw_roc_done(struct work_struct *work)
 static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
                              struct ieee80211_channel *chan,
-                             int duration)
+                             int duration,
+                             enum ieee80211_roc_type type)
 {
        struct mac80211_hwsim_data *hwsim = hw->priv;
 
index cf43b3c29250add91bffeb663835c7e436fe7b4d..966a78f8e21a3ce974459c27a50326c7588cd270 100644 (file)
@@ -259,3 +259,44 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
 
        return ret_len;
 }
+
+int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv,
+                        struct host_cmd_ds_command *cmd, u16 cmd_action,
+                        struct mwifiex_11ac_vht_cfg *cfg)
+{
+       struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_11AC_CFG);
+       cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) +
+                               S_DS_GEN);
+       vhtcfg->action = cpu_to_le16(cmd_action);
+       vhtcfg->band_config = cfg->band_config;
+       vhtcfg->misc_config = cfg->misc_config;
+       vhtcfg->cap_info = cpu_to_le32(cfg->cap_info);
+       vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set);
+       vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set);
+
+       return 0;
+}
+
+/* This function initializes the BlockACK setup information for given
+ * mwifiex_private structure for 11ac enabled networks.
+ */
+void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv)
+{
+       priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
+
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+               priv->add_ba_param.tx_win_size =
+                                          MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE;
+               priv->add_ba_param.rx_win_size =
+                                          MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE;
+       } else {
+               priv->add_ba_param.tx_win_size =
+                                          MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
+               priv->add_ba_param.rx_win_size =
+                                          MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
+       }
+
+       return;
+}
index 80fd1ba462003c557672bf85ef08322fd13df137..7c2c69b5b3eb47e3af4505516a9b2b9dc7355f2f 100644 (file)
 #ifndef _MWIFIEX_11AC_H_
 #define _MWIFIEX_11AC_H_
 
+#define VHT_CFG_2GHZ BIT(0)
+#define VHT_CFG_5GHZ BIT(1)
+
+enum vht_cfg_misc_config {
+       VHT_CAP_TX_OPERATION = 1,
+       VHT_CAP_ASSOCIATION,
+       VHT_CAP_UAP_ONLY
+};
+
+#define DEFAULT_VHT_MCS_SET 0xfffa
+#define DISABLE_VHT_MCS_SET 0xffff
+
+#define VHT_BW_80_160_80P80 BIT(2)
+
 int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
                                struct mwifiex_bssdescriptor *bss_desc,
                                u8 **buffer);
+int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv,
+                        struct host_cmd_ds_command *cmd, u16 cmd_action,
+                        struct mwifiex_11ac_vht_cfg *cfg);
 #endif /* _MWIFIEX_11AC_H_ */
index 45f19716687ee2639fb80019227cddd9bd7ce64c..41e9d25a2d8e9d135a2f6b9b3b451149d36a0518 100644 (file)
@@ -679,3 +679,25 @@ void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra)
 
        return;
 }
+
+/* This function initializes the BlockACK setup information for given
+ * mwifiex_private structure.
+ */
+void mwifiex_set_ba_params(struct mwifiex_private *priv)
+{
+       priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
+
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+               priv->add_ba_param.tx_win_size =
+                                               MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE;
+               priv->add_ba_param.rx_win_size =
+                                               MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE;
+       } else {
+               priv->add_ba_param.tx_win_size =
+                                               MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;
+               priv->add_ba_param.rx_win_size =
+                                               MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
+       }
+
+       return;
+}
index 97b245cbafd8441ec76550a7ee0f441598273557..ecf28464367fc5be95a8d231b0753af349b94ec9 100644 (file)
@@ -39,6 +39,7 @@ mwifiex-y += sta_tx.o
 mwifiex-y += sta_rx.o
 mwifiex-y += uap_txrx.o
 mwifiex-y += cfg80211.o
+mwifiex-y += ethtool.o
 mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
 obj-$(CONFIG_MWIFIEX) += mwifiex.o
 
index 8aaf56ade4d9d086f36b7dee9d880f7628b50b23..47012947a447b7af836d2355ac5bc55148ba5393 100644 (file)
@@ -1374,6 +1374,18 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
        }
 
        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 (params->inactivity_timeout > 0) {
@@ -1933,66 +1945,10 @@ static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info,
                                   struct mwifiex_private *priv)
 {
        struct mwifiex_adapter *adapter = priv->adapter;
-       u32 vht_cap = 0, cap = adapter->hw_dot_11ac_dev_cap;
 
        vht_info->vht_supported = true;
 
-       switch (GET_VHTCAP_MAXMPDULEN(cap)) {
-       case 0x00:
-               vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
-               break;
-       case 0x01:
-               vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
-               break;
-       case 0x10:
-               vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
-           break;
-       default:
-           dev_err(adapter->dev, "unsupported MAX MPDU len\n");
-           break;
-       }
-
-       if (ISSUPP_11ACVHTHTCVHT(cap))
-               vht_cap |= IEEE80211_VHT_CAP_HTC_VHT;
-
-       if (ISSUPP_11ACVHTTXOPPS(cap))
-               vht_cap |= IEEE80211_VHT_CAP_VHT_TXOP_PS;
-
-       if (ISSUPP_11ACMURXBEAMFORMEE(cap))
-               vht_cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
-
-       if (ISSUPP_11ACMUTXBEAMFORMEE(cap))
-               vht_cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
-
-       if (ISSUPP_11ACSUBEAMFORMER(cap))
-               vht_cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
-
-       if (ISSUPP_11ACSUBEAMFORMEE(cap))
-               vht_cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
-
-       if (ISSUPP_11ACRXSTBC(cap))
-               vht_cap |= IEEE80211_VHT_CAP_RXSTBC_1;
-
-       if (ISSUPP_11ACTXSTBC(cap))
-               vht_cap |= IEEE80211_VHT_CAP_TXSTBC;
-
-       if (ISSUPP_11ACSGI160(cap))
-               vht_cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
-
-       if (ISSUPP_11ACSGI80(cap))
-               vht_cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
-
-       if (ISSUPP_11ACLDPC(cap))
-               vht_cap |= IEEE80211_VHT_CAP_RXLDPC;
-
-       if (ISSUPP_11ACBW8080(cap))
-               vht_cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
-
-       if (ISSUPP_11ACBW160(cap))
-               vht_cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
-
-       vht_info->cap = vht_cap;
-
+       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);
@@ -2236,6 +2192,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *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;
@@ -2294,6 +2251,152 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
 }
 EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
 
+#ifdef CONFIG_PM
+static bool
+mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
+                            s8 *byte_seq)
+{
+       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_BYTESEQ)
+                               return false;
+               }
+       }
+
+       byte_seq[MAX_BYTESEQ] = valid_byte_cnt;
+
+       return true;
+}
+
+static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
+                                   struct cfg80211_wowlan *wowlan)
+{
+       struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+       struct mwifiex_ds_mef_cfg mef_cfg;
+       struct mwifiex_mef_entry *mef_entry;
+       int i, filt_num = 0, ret;
+       bool first_pat = true;
+       u8 byte_seq[MAX_BYTESEQ + 1];
+       const u8 ipv4_mc_mac[] = {0x33, 0x33};
+       const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+       struct mwifiex_private *priv =
+                       mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
+       if (!wowlan) {
+               dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n");
+               return 0;
+       }
+
+       if (!priv->media_connected) {
+               dev_warn(adapter->dev,
+                        "Can not configure WOWLAN in disconnected state\n");
+               return 0;
+       }
+
+       mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL);
+       if (!mef_entry)
+               return -ENOMEM;
+
+       memset(&mef_cfg, 0, sizeof(mef_cfg));
+       mef_cfg.num_entries = 1;
+       mef_cfg.mef_entry = mef_entry;
+       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)) {
+                       wiphy_err(wiphy, "Pattern not supported\n");
+                       kfree(mef_entry);
+                       return -EOPNOTSUPP;
+               }
+
+               if (!wowlan->patterns[i].pkt_offset) {
+                       if (!(byte_seq[0] & 0x01) &&
+                           (byte_seq[MAX_BYTESEQ] == 1)) {
+                               mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+                               continue;
+                       } else if (is_broadcast_ether_addr(byte_seq)) {
+                               mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST;
+                               continue;
+                       } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+                                   (byte_seq[MAX_BYTESEQ] == 2)) ||
+                                  (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+                                   (byte_seq[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[MAX_BYTESEQ] = ETH_ALEN;
+               mef_entry->filter[filt_num].offset = 14;
+               mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+               if (filt_num)
+                       mef_entry->filter[filt_num].filt_action = TYPE_OR;
+       }
+
+       if (!mef_cfg.criteria)
+               mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
+                                  MWIFIEX_CRITERIA_UNICAST |
+                                  MWIFIEX_CRITERIA_MULTICAST;
+
+       ret =  mwifiex_send_cmd_sync(priv, HostCmd_CMD_MEF_CFG,
+                                    HostCmd_ACT_GEN_SET, 0,
+                                    &mef_cfg);
+
+       kfree(mef_entry);
+       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
+
 /* station cfg80211 operations */
 static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .add_virtual_intf = mwifiex_add_virtual_intf,
@@ -2322,6 +2425,11 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .change_beacon = mwifiex_cfg80211_change_beacon,
        .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
        .set_antenna = mwifiex_cfg80211_set_antenna,
+#ifdef CONFIG_PM
+       .suspend = mwifiex_cfg80211_suspend,
+       .resume = mwifiex_cfg80211_resume,
+       .set_wakeup = mwifiex_cfg80211_set_wakeup,
+#endif
 };
 
 /*
@@ -2380,6 +2488,14 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
 
        wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
 
+#ifdef CONFIG_PM
+       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT;
+       wiphy->wowlan.n_patterns = MWIFIEX_MAX_FILTERS;
+       wiphy->wowlan.pattern_min_len = 1;
+       wiphy->wowlan.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN;
+       wiphy->wowlan.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN;
+#endif
+
        wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
                                    NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
                                    NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
index b5c8b962ce12f74077fb6159b3e8558f521f10a5..da469c336aa1ad36ad682ca3d06d9242b7212145 100644 (file)
@@ -153,7 +153,7 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
                        " or cmd size is 0, not sending\n");
                if (cmd_node->wait_q_enabled)
                        adapter->cmd_wait_q.status = -1;
-               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               mwifiex_recycle_cmd_node(adapter, cmd_node);
                return -1;
        }
 
@@ -167,7 +167,7 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
                        "DNLD_CMD: FW in reset state, ignore cmd %#x\n",
                        cmd_code);
                mwifiex_complete_cmd(adapter, cmd_node);
-               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               mwifiex_recycle_cmd_node(adapter, cmd_node);
                return -1;
        }
 
@@ -228,7 +228,7 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
                        adapter->cmd_sent = false;
                if (cmd_node->wait_q_enabled)
                        adapter->cmd_wait_q.status = -1;
-               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+               mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
 
                spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
                adapter->curr_cmd = NULL;
@@ -632,6 +632,20 @@ mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
        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);
+       dev_dbg(adapter->dev, "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.
  *
@@ -673,7 +687,9 @@ mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
                list_add(&cmd_node->list, &adapter->cmd_pending_q);
        spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
 
-       dev_dbg(adapter->dev, "cmd: QUEUE_CMD: cmd=%#x is queued\n", command);
+       atomic_inc(&adapter->cmd_pending);
+       dev_dbg(adapter->dev, "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n",
+               command, atomic_read(&adapter->cmd_pending));
 }
 
 /*
@@ -783,7 +799,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
        if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) {
                dev_err(adapter->dev, "CMD_RESP: %#x been canceled\n",
                        le16_to_cpu(resp->command));
-               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+               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);
@@ -833,7 +849,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
                if (adapter->curr_cmd->wait_q_enabled)
                        adapter->cmd_wait_q.status = -1;
 
-               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+               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);
@@ -865,8 +881,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
                if (adapter->curr_cmd->wait_q_enabled)
                        adapter->cmd_wait_q.status = ret;
 
-               /* Clean up and put current command back to cmd_free_q */
-               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+               mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
 
                spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
                adapter->curr_cmd = NULL;
@@ -993,7 +1008,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
                        mwifiex_complete_cmd(adapter, cmd_node);
                        cmd_node->wait_q_enabled = false;
                }
-               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               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);
@@ -1040,7 +1055,7 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
                cmd_node = adapter->curr_cmd;
                cmd_node->wait_q_enabled = false;
                cmd_node->cmd_flag |= CMD_F_CANCELED;
-               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               mwifiex_recycle_cmd_node(adapter, cmd_node);
                mwifiex_complete_cmd(adapter, adapter->curr_cmd);
                adapter->curr_cmd = NULL;
                spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
@@ -1149,7 +1164,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
                        phs_cfg->params.hs_config.gpio,
                        phs_cfg->params.hs_config.gap);
        }
-       if (conditions != HOST_SLEEP_CFG_CANCEL) {
+       if (conditions != HS_CFG_CANCEL) {
                adapter->is_hs_configured = true;
                if (adapter->iface_type == MWIFIEX_USB ||
                    adapter->iface_type == MWIFIEX_PCIE)
index e8a569aaa2e8ca24298e8e1e25161bd5a9379943..94cc09d48444600129620a247c3e227d10865e59 100644 (file)
 #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED      2
 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED      16
 
-#define MWIFIEX_AMPDU_DEF_TXWINSIZE        32
-#define MWIFIEX_AMPDU_DEF_RXWINSIZE        16
+#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE        16
+#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE        32
+#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE        32
+#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE        16
+#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE   32
+#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE   48
+#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE   48
+#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE   32
+
 #define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT  0xffff
 
 #define MWIFIEX_RATE_BITMAP_MCS0   32
diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c
new file mode 100644 (file)
index 0000000..bfb3990
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Marvell Wireless LAN device driver: ethtool
+ *
+ * Copyright (C) 2013, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "main.h"
+
+static void mwifiex_ethtool_get_wol(struct net_device *dev,
+                                   struct ethtool_wolinfo *wol)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions);
+
+       wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY;
+
+       if (conditions == HS_CFG_COND_DEF)
+               return;
+
+       if (conditions & HS_CFG_COND_UNICAST_DATA)
+               wol->wolopts |= WAKE_UCAST;
+       if (conditions & HS_CFG_COND_MULTICAST_DATA)
+               wol->wolopts |= WAKE_MCAST;
+       if (conditions & HS_CFG_COND_BROADCAST_DATA)
+               wol->wolopts |= WAKE_BCAST;
+       if (conditions & HS_CFG_COND_MAC_EVENT)
+               wol->wolopts |= WAKE_PHY;
+}
+
+static int mwifiex_ethtool_set_wol(struct net_device *dev,
+                                  struct ethtool_wolinfo *wol)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       u32 conditions = 0;
+
+       if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY))
+               return -EOPNOTSUPP;
+
+       if (wol->wolopts & WAKE_UCAST)
+               conditions |= HS_CFG_COND_UNICAST_DATA;
+       if (wol->wolopts & WAKE_MCAST)
+               conditions |= HS_CFG_COND_MULTICAST_DATA;
+       if (wol->wolopts & WAKE_BCAST)
+               conditions |= HS_CFG_COND_BROADCAST_DATA;
+       if (wol->wolopts & WAKE_PHY)
+               conditions |= HS_CFG_COND_MAC_EVENT;
+       if (wol->wolopts == 0)
+               conditions |= HS_CFG_COND_DEF;
+       priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions);
+
+       return 0;
+}
+
+const struct ethtool_ops mwifiex_ethtool_ops = {
+       .get_wol = mwifiex_ethtool_get_wol,
+       .set_wol = mwifiex_ethtool_set_wol,
+};
index 25acb0682c560508bf6c7dd7494091da254c4e98..1f7578d553ec982e94b8c329310d08459cf18225 100644 (file)
@@ -230,40 +230,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 
 #define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & (BIT(13)|BIT(14)))
 
-#define GET_VHTCAP_MAXMPDULEN(vht_cap_info) (vht_cap_info & 0x3)
 #define GET_VHTCAP_CHWDSET(vht_cap_info)    ((vht_cap_info >> 2) & 0x3)
 #define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3)
 #define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \
                                              (2 * (nss - 1)))
 #define NO_NSS_SUPPORT         0x3
 
-/* HW_SPEC: HTC-VHT supported */
-#define ISSUPP_11ACVHTHTCVHT(Dot11acDevCap) (Dot11acDevCap & BIT(22))
-/* HW_SPEC: VHT TXOP PS support */
-#define ISSUPP_11ACVHTTXOPPS(Dot11acDevCap) (Dot11acDevCap & BIT(21))
-/* HW_SPEC: MU RX beamformee support */
-#define ISSUPP_11ACMURXBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & BIT(20))
-/* HW_SPEC: MU TX beamformee support */
-#define ISSUPP_11ACMUTXBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & BIT(19))
-/* HW_SPEC: SU Beamformee support */
-#define ISSUPP_11ACSUBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & BIT(10))
-/* HW_SPEC: SU Beamformer support */
-#define ISSUPP_11ACSUBEAMFORMER(Dot11acDevCap) (Dot11acDevCap & BIT(9))
-/* HW_SPEC: Rx STBC support */
-#define ISSUPP_11ACRXSTBC(Dot11acDevCap) (Dot11acDevCap & BIT(8))
-/* HW_SPEC: Tx STBC support */
-#define ISSUPP_11ACTXSTBC(Dot11acDevCap) (Dot11acDevCap & BIT(7))
-/* HW_SPEC: Short GI support for 160MHz BW */
-#define ISSUPP_11ACSGI160(Dot11acDevCap) (Dot11acDevCap & BIT(6))
-/* HW_SPEC: Short GI support for 80MHz BW */
-#define ISSUPP_11ACSGI80(Dot11acDevCap) (Dot11acDevCap & BIT(5))
-/* HW_SPEC: LDPC coding support */
-#define ISSUPP_11ACLDPC(Dot11acDevCap) (Dot11acDevCap & BIT(4))
-/* HW_SPEC: Channel BW 20/40/80/160/80+80 MHz support */
-#define ISSUPP_11ACBW8080(Dot11acDevCap) (Dot11acDevCap & BIT(3))
-/* HW_SPEC: Channel BW 20/40/80/160 MHz support */
-#define ISSUPP_11ACBW160(Dot11acDevCap) (Dot11acDevCap & BIT(2))
-
 #define GET_DEVTXMCSMAP(dev_mcs_map)      (dev_mcs_map >> 16)
 #define GET_DEVRXMCSMAP(dev_mcs_map)      (dev_mcs_map & 0xFFFF)
 
@@ -300,6 +272,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_802_11_TX_RATE_QUERY              0x007f
 #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS     0x0083
 #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
@@ -322,6 +295,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_PCIE_DESC_DETAILS                 0x00fa
 #define HostCmd_CMD_MGMT_FRAME_REG                    0x010c
 #define HostCmd_CMD_REMAIN_ON_CHAN                    0x010d
+#define HostCmd_CMD_11AC_CFG                         0x0112
 
 #define PROTOCOL_NO_SECURITY        0x01
 #define PROTOCOL_STATIC_WEP         0x02
@@ -376,10 +350,14 @@ enum P2P_MODES {
 #define HostCmd_SCAN_RADIO_TYPE_BG          0
 #define HostCmd_SCAN_RADIO_TYPE_A           1
 
-#define HOST_SLEEP_CFG_CANCEL          0xffffffff
-#define HOST_SLEEP_CFG_COND_DEF                0x00000000
-#define HOST_SLEEP_CFG_GPIO_DEF                0xff
-#define HOST_SLEEP_CFG_GAP_DEF         0
+#define HS_CFG_CANCEL                  0xffffffff
+#define HS_CFG_COND_DEF                        0x00000000
+#define HS_CFG_GPIO_DEF                        0xff
+#define HS_CFG_GAP_DEF                 0
+#define HS_CFG_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 MWIFIEX_TIMEOUT_FOR_AP_RESP            0xfffc
 #define MWIFIEX_STATUS_CODE_AUTH_TIMEOUT       2
@@ -469,6 +447,23 @@ enum P2P_MODES {
 #define EVENT_GET_BSS_TYPE(event_cause)         \
        (((event_cause) >> 24) & 0x00ff)
 
+#define MWIFIEX_MAX_PATTERN_LEN                20
+#define MWIFIEX_MAX_OFFSET_LEN         50
+#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 MWIFIEX_CRITERIA_BROADCAST     BIT(0)
+#define MWIFIEX_CRITERIA_UNICAST       BIT(1)
+#define MWIFIEX_CRITERIA_MULTICAST     BIT(3)
+
 struct mwifiex_ie_types_header {
        __le16 type;
        __le16 len;
@@ -1369,6 +1364,15 @@ struct host_cmd_ds_sys_config {
        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 host_cmd_tlv tlv;
        __le16 key_mgmt;
@@ -1499,6 +1503,19 @@ struct host_cmd_ds_802_11_ibss_status {
        __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
@@ -1603,6 +1620,7 @@ struct host_cmd_ds_command {
                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_mac_reg_access mac_reg;
                struct host_cmd_ds_bbp_reg_access bbp_reg;
                struct host_cmd_ds_rf_reg_access rf_reg;
@@ -1612,6 +1630,7 @@ struct host_cmd_ds_command {
                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_11ac_vht_cfg vht_cfg;
        } params;
 } __packed;
 
index 0ff4c37ab42ae6853dbe6c85f8bb33f476b531e1..42d7f0adf9bdf0f4b1ae8e0058211e44d331c34e 100644 (file)
@@ -318,9 +318,9 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
        adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
 
        adapter->is_hs_configured = false;
-       adapter->hs_cfg.conditions = cpu_to_le32(HOST_SLEEP_CFG_COND_DEF);
-       adapter->hs_cfg.gpio = HOST_SLEEP_CFG_GPIO_DEF;
-       adapter->hs_cfg.gap = HOST_SLEEP_CFG_GAP_DEF;
+       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));
@@ -533,10 +533,8 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
                if (!adapter->priv[i])
                        continue;
                priv = adapter->priv[i];
-               for (j = 0; j < MAX_NUM_TID; ++j) {
+               for (j = 0; j < MAX_NUM_TID; ++j)
                        INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
-                       spin_lock_init(&priv->wmm.tid_tbl_ptr[j].tid_tbl_lock);
-               }
                INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
                INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
                INIT_LIST_HEAD(&priv->sta_list);
@@ -713,7 +711,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
        if (adapter->curr_cmd) {
                dev_warn(adapter->dev, "curr_cmd is still in processing\n");
                del_timer(&adapter->cmd_timer);
-               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+               mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
                adapter->curr_cmd = NULL;
        }
 
index d85e6eb1f58afde9d56a60f379a52066f7d54c50..7f27e45680b5ec9427727e716a737e394b176f30 100644 (file)
@@ -272,6 +272,14 @@ struct mwifiex_ds_pm_cfg {
        } param;
 };
 
+struct mwifiex_11ac_vht_cfg {
+       u8 band_config;
+       u8 misc_config;
+       u32 cap_info;
+       u32 mcs_tx_set;
+       u32 mcs_rx_set;
+};
+
 struct mwifiex_ds_11n_tx_cfg {
        u16 tx_htcap;
        u16 tx_htinfo;
@@ -354,6 +362,29 @@ struct mwifiex_ds_misc_subsc_evt {
        struct subsc_evt_cfg bcn_h_rssi_cfg;
 };
 
+#define MAX_BYTESEQ            6       /* non-adjustable */
+#define MWIFIEX_MAX_FILTERS    10
+
+struct mwifiex_mef_filter {
+       u16 repeat;
+       u16 offset;
+       s8 byte_seq[MAX_BYTESEQ + 1];
+       u8 filt_type;
+       u8 filt_action;
+};
+
+struct mwifiex_mef_entry {
+       u8 mode;
+       u8 action;
+       struct mwifiex_mef_filter filter[MWIFIEX_MAX_FILTERS];
+};
+
+struct mwifiex_ds_mef_cfg {
+       u32 criteria;
+       u16 num_entries;
+       struct mwifiex_mef_entry *mef_entry;
+};
+
 #define MWIFIEX_MAX_VSIE_LEN       (256)
 #define MWIFIEX_MAX_VSIE_NUM       (8)
 #define MWIFIEX_VSIE_MASK_CLEAR    0x00
index 2fe0ceba4400ad017d99ca5324707aa96093d6be..6bcb66e6e97c8c1a43a139ba6970ddc0b15f701e 100644 (file)
@@ -1295,6 +1295,14 @@ int mwifiex_associate(struct mwifiex_private *priv,
            (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_GAC ||
+            priv->adapter->config_bands & BAND_AAC))
+               mwifiex_set_11ac_ba_params(priv);
+       else
+               mwifiex_set_ba_params(priv);
+
        memcpy(&current_bssid,
               &priv->curr_bss_params.bss_descriptor.mac_address,
               sizeof(current_bssid));
@@ -1323,6 +1331,13 @@ mwifiex_adhoc_start(struct mwifiex_private *priv,
        dev_dbg(priv->adapter->dev, "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_GAC ||
+            priv->adapter->config_bands & BAND_AAC))
+               mwifiex_set_11ac_ba_params(priv);
+       else
+               mwifiex_set_ba_params(priv);
+
        return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_START,
                                    HostCmd_ACT_GEN_SET, 0, adhoc_ssid);
 }
@@ -1356,6 +1371,14 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv,
                return -1;
        }
 
+       if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+           !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+           (priv->adapter->config_bands & BAND_GAC ||
+            priv->adapter->config_bands & BAND_AAC))
+               mwifiex_set_11ac_ba_params(priv);
+       else
+               mwifiex_set_ba_params(priv);
+
        dev_dbg(priv->adapter->dev, "info: curr_bss_params.channel = %d\n",
                priv->curr_bss_params.bss_descriptor.channel);
        dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %c\n",
index 9c802ede9c3b94cb56f385e7c5c40699d0c2ee9f..121443a0f2a13ed91522241cfe6fc11abad613af 100644 (file)
@@ -588,10 +588,19 @@ mwifiex_tx_timeout(struct net_device *dev)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
-       dev_err(priv->adapter->dev, "%lu : Tx timeout, bss_type-num = %d-%d\n",
-               jiffies, priv->bss_type, priv->bss_num);
-       mwifiex_set_trans_start(dev);
        priv->num_tx_timeout++;
+       priv->tx_timeout_cnt++;
+       dev_err(priv->adapter->dev,
+               "%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) {
+               dev_err(priv->adapter->dev,
+                       "tx_timeout_cnt exceeds threshold. Triggering card reset!\n");
+               priv->adapter->if_ops.card_reset(priv->adapter);
+       }
 }
 
 /*
index 7035ade9af74aeb08ceeea6b1545b28bd8e105d9..b7484efc944367761faecfe08f8bc6a7ee8bd9ae 100644 (file)
@@ -130,6 +130,9 @@ enum {
 #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
+
 struct mwifiex_dbg {
        u32 num_cmd_host_to_card_failure;
        u32 num_cmd_sleep_cfm_host_to_card_failure;
@@ -210,15 +213,12 @@ struct mwifiex_ra_list_tbl {
 
 struct mwifiex_tid_tbl {
        struct list_head ra_list;
-       /* spin lock for tid table */
-       spinlock_t tid_tbl_lock;
        struct mwifiex_ra_list_tbl *ra_list_curr;
 };
 
 #define WMM_HIGHEST_PRIORITY           7
 #define HIGH_PRIO_TID                          7
 #define LOW_PRIO_TID                           0
-#define NO_PKT_PRIO_TID                                (-1)
 
 struct mwifiex_wmm_desc {
        struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID];
@@ -394,6 +394,8 @@ struct mwifiex_private {
        u8 curr_addr[ETH_ALEN];
        u8 media_connected;
        u32 num_tx_timeout;
+       /* track consecutive timeout */
+       u8 tx_timeout_cnt;
        struct net_device *netdev;
        struct net_device_stats stats;
        u16 curr_pkt_filter;
@@ -793,6 +795,8 @@ 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,
@@ -907,12 +911,20 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv,
 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_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_set_11ac_ba_params(struct mwifiex_private *priv);
 
 /*
  * This function checks if the queuing is RA based or not.
@@ -1098,11 +1110,15 @@ 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);
 
+extern const struct ethtool_ops mwifiex_ethtool_ops;
+
 #ifdef CONFIG_DEBUG_FS
 void mwifiex_debugfs_init(void);
 void mwifiex_debugfs_remove(void);
index feb20461339767df1c8912b216b9d62a1c11f5da..856959b64bc7d01f077576f80a4d8fe8b95929ba 100644 (file)
@@ -36,8 +36,6 @@ static u8 user_rmmod;
 static struct mwifiex_if_ops pcie_ops;
 
 static struct semaphore add_remove_card_sem;
-static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter);
-static int mwifiex_pcie_resume(struct pci_dev *pdev);
 
 static int
 mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
@@ -78,6 +76,82 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
        return false;
 }
 
+#ifdef CONFIG_PM
+/*
+ * 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 pci_dev *pdev, pm_message_t state)
+{
+       struct mwifiex_adapter *adapter;
+       struct pcie_service_card *card;
+       int hs_actived;
+
+       if (pdev) {
+               card = (struct pcie_service_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;
+
+       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 pci_dev *pdev)
+{
+       struct mwifiex_adapter *adapter;
+       struct pcie_service_card *card;
+
+       if (pdev) {
+               card = (struct pcie_service_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) {
+               dev_warn(adapter->dev, "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
@@ -159,80 +233,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        kfree(card);
 }
 
-/*
- * 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 pci_dev *pdev, pm_message_t state)
-{
-       struct mwifiex_adapter *adapter;
-       struct pcie_service_card *card;
-       int hs_actived;
-
-       if (pdev) {
-               card = (struct pcie_service_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;
-
-       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 pci_dev *pdev)
-{
-       struct mwifiex_adapter *adapter;
-       struct pcie_service_card *card;
-
-       if (pdev) {
-               card = (struct pcie_service_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) {
-               dev_warn(adapter->dev, "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;
-}
-
 static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = {
        {
                PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
@@ -287,18 +287,13 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
 }
 
 /*
- * This function wakes up the card.
- *
- * A host power up command is written to the card configuration
- * register to wake up the card.
+ * This function adds delay loop to ensure FW is awake before proceeding.
  */
-static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
+static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter)
 {
        int i = 0;
-       struct pcie_service_card *card = adapter->card;
-       const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
 
-       while (reg->sleep_cookie && mwifiex_pcie_ok_to_access_hw(adapter)) {
+       while (mwifiex_pcie_ok_to_access_hw(adapter)) {
                i++;
                usleep_range(10, 20);
                /* 50ms max wait */
@@ -306,16 +301,32 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
                        break;
        }
 
+       return;
+}
+
+/* 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;
+
        dev_dbg(adapter->dev, "event: Wakeup device...\n");
 
-       /* Enable interrupts or any chip access will wakeup device */
-       if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, HOST_INTR_MASK)) {
-               dev_warn(adapter->dev, "Enable host interrupt failed\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)) {
+               dev_warn(adapter->dev, "Reading fw_status register failed\n");
                return -1;
        }
 
-       dev_dbg(adapter->dev, "PCIE wakeup: Setting PS_STATE_AWAKE\n");
-       adapter->ps_state = PS_STATE_AWAKE;
+       if (reg->sleep_cookie) {
+               mwifiex_pcie_dev_wakeup_delay(adapter);
+               dev_dbg(adapter->dev, "PCIE wakeup: Setting PS_STATE_AWAKE\n");
+               adapter->ps_state = PS_STATE_AWAKE;
+       }
 
        return 0;
 }
@@ -1030,8 +1041,8 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
        u32 wrindx, num_tx_buffs, rx_val;
        int ret;
        dma_addr_t buf_pa;
-       struct mwifiex_pcie_buf_desc *desc;
-       struct mwifiex_pfu_buf_desc *desc2;
+       struct mwifiex_pcie_buf_desc *desc = NULL;
+       struct mwifiex_pfu_buf_desc *desc2 = NULL;
        __le16 *tmp;
 
        if (!(skb->data && skb->len)) {
@@ -1984,12 +1995,13 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
                                }
                        }
                } else if (!adapter->pps_uapsd_mode &&
-                          adapter->ps_state == PS_STATE_SLEEP) {
+                          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 */
-                               if (mwifiex_pcie_ok_to_access_hw(adapter))
-                                       adapter->ps_state = PS_STATE_AWAKE;
+                               adapter->ps_state = PS_STATE_AWAKE;
+                               adapter->pm_wakeup_fw_try = false;
                }
        }
 }
@@ -2112,7 +2124,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
        }
        dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n",
                adapter->cmd_sent, adapter->data_sent);
-       mwifiex_pcie_enable_host_int(adapter);
+       if (adapter->ps_state != PS_STATE_SLEEP)
+               mwifiex_pcie_enable_host_int(adapter);
 
        return 0;
 }
index c55c5bb931349725cdb9a5394bf3d08d47027fc4..b193e25977d2a321d6a1df1923729b7039cf8825 100644 (file)
@@ -24,6 +24,7 @@
 #include "main.h"
 #include "wmm.h"
 #include "11n.h"
+#include "11ac.h"
 
 /*
  * This function prepares command to set/get RSSI information.
@@ -334,7 +335,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
        cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH);
 
        if (!hs_activate &&
-           (hscfg_param->conditions != cpu_to_le32(HOST_SLEEP_CFG_CANCEL)) &&
+           (hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL)) &&
            ((adapter->arp_filter_size > 0) &&
             (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) {
                dev_dbg(adapter->dev,
@@ -1059,6 +1060,80 @@ mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv,
        return 0;
 }
 
+static int
+mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv,
+                                 struct mwifiex_mef_entry *mef_entry,
+                                 u8 **buffer)
+{
+       struct mwifiex_mef_filter *filter = mef_entry->filter;
+       int i, byte_len;
+       u8 *stack_ptr = *buffer;
+
+       for (i = 0; i < MWIFIEX_MAX_FILTERS; i++) {
+               filter = &mef_entry->filter[i];
+               if (!filter->filt_type)
+                       break;
+               *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat);
+               stack_ptr += 4;
+               *stack_ptr = TYPE_DNUM;
+               stack_ptr += 1;
+
+               byte_len = filter->byte_seq[MAX_BYTESEQ];
+               memcpy(stack_ptr, filter->byte_seq, byte_len);
+               stack_ptr += byte_len;
+               *stack_ptr = byte_len;
+               stack_ptr += 1;
+               *stack_ptr = TYPE_BYTESEQ;
+               stack_ptr += 1;
+
+               *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset);
+               stack_ptr += 4;
+               *stack_ptr = TYPE_DNUM;
+               stack_ptr += 1;
+
+               *stack_ptr = filter->filt_type;
+               stack_ptr += 1;
+
+               if (filter->filt_action) {
+                       *stack_ptr = filter->filt_action;
+                       stack_ptr += 1;
+               }
+
+               if (stack_ptr - *buffer > STACK_NBYTES)
+                       return -1;
+       }
+
+       *buffer = stack_ptr;
+       return 0;
+}
+
+static int
+mwifiex_cmd_mef_cfg(struct mwifiex_private *priv,
+                   struct host_cmd_ds_command *cmd,
+                   struct mwifiex_ds_mef_cfg *mef)
+{
+       struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg;
+       u8 *pos = (u8 *)mef_cfg;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG);
+
+       mef_cfg->criteria = cpu_to_le32(mef->criteria);
+       mef_cfg->num_entries = cpu_to_le16(mef->num_entries);
+       pos += sizeof(*mef_cfg);
+       mef_cfg->mef_entry->mode = mef->mef_entry->mode;
+       mef_cfg->mef_entry->action = mef->mef_entry->action;
+       pos += sizeof(*(mef_cfg->mef_entry));
+
+       if (mwifiex_cmd_append_rpn_expression(priv, mef->mef_entry, &pos))
+               return -1;
+
+       mef_cfg->mef_entry->exprsize =
+                       cpu_to_le16(pos - mef_cfg->mef_entry->expr);
+       cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN);
+
+       return 0;
+}
+
 /*
  * This function prepares the commands before sending them to the firmware.
  *
@@ -1184,6 +1259,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
                      cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) +
                                  S_DS_GEN);
                break;
+       case HostCmd_CMD_11AC_CFG:
+               ret = mwifiex_cmd_11ac_cfg(priv, cmd_ptr, cmd_action, data_buf);
+               break;
        case HostCmd_CMD_P2P_MODE_CFG:
                cmd_ptr->command = cpu_to_le16(cmd_no);
                cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action);
@@ -1273,6 +1351,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
        case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
                ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf);
                break;
+       case HostCmd_CMD_MEF_CFG:
+               ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf);
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd- %#x\n", cmd_no);
index 4669f8d9389fd206fadfc8e6c2532b614880a314..9f990e14966e8354a91b6e90bd69c413c3f562e7 100644 (file)
@@ -95,7 +95,7 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
                break;
        }
        /* Handling errors here */
-       mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+       mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
 
        spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
        adapter->curr_cmd = NULL;
@@ -907,6 +907,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
        case HostCmd_CMD_REMAIN_ON_CHAN:
                ret = mwifiex_ret_remain_on_chan(priv, resp, data_buf);
                break;
+       case HostCmd_CMD_11AC_CFG:
+               break;
        case HostCmd_CMD_P2P_MODE_CFG:
                ret = mwifiex_ret_p2p_mode_cfg(priv, resp, data_buf);
                break;
@@ -976,6 +978,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
        case HostCmd_CMD_UAP_BSS_STOP:
                priv->bss_started = 0;
                break;
+       case HostCmd_CMD_MEF_CFG:
+               break;
        default:
                dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
                        resp->command);
index 13100f8de3db1729b613333f16d21438104cd2e0..e6c9b2ae22ed707fbbfd53dc98a72fad18b563f9 100644 (file)
@@ -59,9 +59,6 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
 {
        int status;
 
-       dev_dbg(adapter->dev, "cmd pending\n");
-       atomic_inc(&adapter->cmd_pending);
-
        /* Wait for completion */
        status = wait_event_interruptible(adapter->cmd_wait_q.wait,
                                          *(cmd_queued->condition));
@@ -382,7 +379,7 @@ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
                        break;
                }
                if (hs_cfg->is_invoke_hostcmd) {
-                       if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) {
+                       if (hs_cfg->conditions == HS_CFG_CANCEL) {
                                if (!adapter->is_hs_configured)
                                        /* Already cancelled */
                                        break;
@@ -397,8 +394,8 @@ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
                                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(HOST_SLEEP_CFG_CANCEL)) {
+                       } else if (adapter->hs_cfg.conditions ==
+                                  cpu_to_le32(HS_CFG_CANCEL)) {
                                /* Return failure if no parameters for HS
                                   enable */
                                status = -1;
@@ -414,7 +411,7 @@ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
                                                HostCmd_CMD_802_11_HS_CFG_ENH,
                                                HostCmd_ACT_GEN_SET, 0,
                                                &adapter->hs_cfg);
-                       if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL)
+                       if (hs_cfg->conditions == HS_CFG_CANCEL)
                                /* Restore previous condition */
                                adapter->hs_cfg.conditions =
                                                cpu_to_le32(prev_cond);
@@ -448,7 +445,7 @@ int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type)
 {
        struct mwifiex_ds_hs_cfg hscfg;
 
-       hscfg.conditions = HOST_SLEEP_CFG_CANCEL;
+       hscfg.conditions = HS_CFG_CANCEL;
        hscfg.is_invoke_hostcmd = true;
 
        return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
index 296faec143657bde52c6304f429f4b0b231221a3..8f923d0d2ba657241c7634b26dc6f04697045e40 100644 (file)
@@ -169,6 +169,8 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
        if (!status) {
                priv->stats.tx_packets++;
                priv->stats.tx_bytes += skb->len;
+               if (priv->tx_timeout_cnt)
+                       priv->tx_timeout_cnt = 0;
        } else {
                priv->stats.tx_errors++;
        }
index 6e76a15a89501a7aa6cacde72aee43cd10612f1d..b04b1db291002b8174cb0cf013a69c97e0bf4e40 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include "main.h"
+#include "11ac.h"
 
 /* This function parses security related parameters from cfg80211_ap_settings
  * and sets into FW understandable bss_config structure.
@@ -177,6 +178,60 @@ mwifiex_set_ht_params(struct mwifiex_private *priv,
        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;
+}
+
+/* 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_sync(priv, HostCmd_CMD_11AC_CFG,
+                             HostCmd_ACT_GEN_SET, 0, &vht_cfg);
+
+       return;
+}
+
 /* This function finds supported rates IE from beacon parameter and sets
  * these rates into bss_config structure.
  */
index 21553976b550ff8c3ab925e58a4f508fa0845033..e57ac0dd3ab55177512aeac08482e54e926696cd 100644 (file)
@@ -195,7 +195,7 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb)
        skb->protocol = eth_type_trans(skb, priv->netdev);
        skb->ip_summed = CHECKSUM_NONE;
 
-       /* This is required only in case of 11n and USB as we alloc
+       /* This is required only in case of 11n and USB/PCIE as we alloc
         * a buffer of 4K only if its 11N (to be able to receive 4K
         * AMSDU packets). In case of SD we allocate buffers based
         * on the size of packet and hence this is not needed.
@@ -212,7 +212,8 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb)
         * fragments. Currently we fail the Filesndl-ht.scr script
         * for UDP, hence this fix
         */
-       if ((priv->adapter->iface_type == MWIFIEX_USB) &&
+       if ((priv->adapter->iface_type == MWIFIEX_USB ||
+            priv->adapter->iface_type == MWIFIEX_PCIE) &&
            (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE))
                skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE);
 
@@ -238,7 +239,6 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb)
 int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
                         struct cmd_ctrl_node *cmd_node)
 {
-       atomic_dec(&adapter->cmd_pending);
        dev_dbg(adapter->dev, "cmd completed: status=%d\n",
                adapter->cmd_wait_q.status);
 
index 32adc878041d252300c6fde9bd9d53ca049e902a..2cc81ba590e3bdf09c4143415a1e488e248c4f7b 100644 (file)
@@ -436,10 +436,7 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter)
                                        = priv->aggr_prio_tbl[7].ampdu_user
                                        = BA_STREAM_NOT_ALLOWED;
 
-               priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
-               priv->add_ba_param.tx_win_size = MWIFIEX_AMPDU_DEF_TXWINSIZE;
-               priv->add_ba_param.rx_win_size = MWIFIEX_AMPDU_DEF_RXWINSIZE;
-
+               mwifiex_set_ba_params(priv);
                mwifiex_reset_11n_rx_seq_num(priv);
 
                atomic_set(&priv->wmm.tx_pkts_queued, 0);
@@ -688,13 +685,13 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
        ra_list->total_pkts_size += skb->len;
        ra_list->pkt_count++;
 
-       atomic_inc(&priv->wmm.tx_pkts_queued);
-
        if (atomic_read(&priv->wmm.highest_queued_prio) <
                                                tos_to_tid_inv[tid_down])
                atomic_set(&priv->wmm.highest_queued_prio,
                           tos_to_tid_inv[tid_down]);
 
+       atomic_inc(&priv->wmm.tx_pkts_queued);
+
        spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
 }
 
@@ -890,19 +887,15 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
        struct mwifiex_bss_prio_node *bssprio_node, *bssprio_head;
        struct mwifiex_tid_tbl *tid_ptr;
        atomic_t *hqp;
-       int is_list_empty;
-       unsigned long flags;
+       unsigned long flags_bss, flags_ra;
        int i, j;
 
        for (j = adapter->priv_num - 1; j >= 0; --j) {
                spin_lock_irqsave(&adapter->bss_prio_tbl[j].bss_prio_lock,
-                                 flags);
-               is_list_empty = list_empty(&adapter->bss_prio_tbl[j]
-                                          .bss_prio_head);
-               spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
-                                      flags);
-               if (is_list_empty)
-                       continue;
+                                 flags_bss);
+
+               if (list_empty(&adapter->bss_prio_tbl[j].bss_prio_head))
+                       goto skip_prio_tbl;
 
                if (adapter->bss_prio_tbl[j].bss_prio_cur ==
                    (struct mwifiex_bss_prio_node *)
@@ -919,26 +912,26 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
 
                do {
                        priv_tmp = bssprio_node->priv;
-                       hqp = &priv_tmp->wmm.highest_queued_prio;
 
+                       if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)
+                               goto skip_bss;
+
+                       /* iterate over the WMM queues of the BSS */
+                       hqp = &priv_tmp->wmm.highest_queued_prio;
                        for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) {
 
+                               spin_lock_irqsave(&priv_tmp->wmm.
+                                                 ra_list_spinlock, flags_ra);
+
                                tid_ptr = &(priv_tmp)->wmm.
                                        tid_tbl_ptr[tos_to_tid[i]];
 
                                /* For non-STA ra_list_curr may be NULL */
                                if (!tid_ptr->ra_list_curr)
-                                       continue;
-
-                               spin_lock_irqsave(&tid_ptr->tid_tbl_lock,
-                                                 flags);
-                               is_list_empty =
-                                       list_empty(&adapter->bss_prio_tbl[j]
-                                                  .bss_prio_head);
-                               spin_unlock_irqrestore(&tid_ptr->tid_tbl_lock,
-                                                      flags);
-                               if (is_list_empty)
-                                       continue;
+                                       goto skip_wmm_queue;
+
+                               if (list_empty(&tid_ptr->ra_list))
+                                       goto skip_wmm_queue;
 
                                /*
                                 * Always choose the next ra we transmitted
@@ -960,10 +953,8 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
                                }
 
                                do {
-                                       is_list_empty =
-                                               skb_queue_empty(&ptr->skb_head);
-
-                                       if (!is_list_empty)
+                                       if (!skb_queue_empty(&ptr->skb_head))
+                                               /* holds both locks */
                                                goto found;
 
                                        /* Get next ra */
@@ -978,14 +969,14 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
                                                    struct mwifiex_ra_list_tbl,
                                                    list);
                                } while (ptr != head);
-                       }
 
-                       /* No packet at any TID for this priv. Mark as such
-                        * to skip checking TIDs for this priv (until pkt is
-                        * added).
-                        */
-                       atomic_set(hqp, NO_PKT_PRIO_TID);
+skip_wmm_queue:
+                               spin_unlock_irqrestore(&priv_tmp->wmm.
+                                                      ra_list_spinlock,
+                                                      flags_ra);
+                       }
 
+skip_bss:
                        /* Get next bss priority node */
                        bssprio_node = list_first_entry(&bssprio_node->list,
                                                struct mwifiex_bss_prio_node,
@@ -1000,14 +991,21 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
                                                struct mwifiex_bss_prio_node,
                                                list);
                } while (bssprio_node != bssprio_head);
+
+skip_prio_tbl:
+               spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
+                                      flags_bss);
        }
+
        return NULL;
 
 found:
-       spin_lock_irqsave(&priv_tmp->wmm.ra_list_spinlock, flags);
+       /* holds bss_prio_lock / ra_list_spinlock */
        if (atomic_read(hqp) > i)
                atomic_set(hqp, i);
-       spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags);
+       spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra);
+       spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
+                              flags_bss);
 
        *priv = priv_tmp;
        *tid = tos_to_tid[i];
index 091d9a64080aa520879261a276d1e08e529f111f..956c1084ebf13ade0959fff0ad894a4910b60108 100644 (file)
@@ -232,6 +232,7 @@ struct mwl8k_priv {
        u16 num_mcaddrs;
        u8 hw_rev;
        u32 fw_rev;
+       u32 caps;
 
        /*
         * Running count of TX packets in flight, to avoid
@@ -284,6 +285,7 @@ struct mwl8k_priv {
        unsigned fw_state;
        char *fw_pref;
        char *fw_alt;
+       bool is_8764;
        struct completion firmware_loading_complete;
 
        /* bitmap of running BSSes */
@@ -600,13 +602,18 @@ mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length)
        loops = 1000;
        do {
                u32 int_code;
-
-               int_code = ioread32(regs + MWL8K_HIU_INT_CODE);
-               if (int_code == MWL8K_INT_CODE_CMD_FINISHED) {
-                       iowrite32(0, regs + MWL8K_HIU_INT_CODE);
-                       break;
+               if (priv->is_8764) {
+                       int_code = ioread32(regs +
+                                           MWL8K_HIU_H2A_INTERRUPT_STATUS);
+                       if (int_code == 0)
+                               break;
+               } else {
+                       int_code = ioread32(regs + MWL8K_HIU_INT_CODE);
+                       if (int_code == MWL8K_INT_CODE_CMD_FINISHED) {
+                               iowrite32(0, regs + MWL8K_HIU_INT_CODE);
+                               break;
+                       }
                }
-
                cond_resched();
                udelay(1);
        } while (--loops);
@@ -724,7 +731,7 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw)
        int rc;
        int loops;
 
-       if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) {
+       if (!memcmp(fw->data, "\x01\x00\x00\x00", 4) && !priv->is_8764) {
                const struct firmware *helper = priv->fw_helper;
 
                if (helper == NULL) {
@@ -743,7 +750,10 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw)
 
                rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
        } else {
-               rc = mwl8k_load_fw_image(priv, fw->data, fw->size);
+               if (priv->is_8764)
+                       rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
+               else
+                       rc = mwl8k_load_fw_image(priv, fw->data, fw->size);
        }
 
        if (rc) {
@@ -908,9 +918,9 @@ static void mwl8k_encapsulate_tx_frame(struct mwl8k_priv *priv,
 }
 
 /*
- * Packet reception for 88w8366 AP firmware.
+ * Packet reception for 88w8366/88w8764 AP firmware.
  */
-struct mwl8k_rxd_8366_ap {
+struct mwl8k_rxd_ap {
        __le16 pkt_len;
        __u8 sq2;
        __u8 rate;
@@ -928,30 +938,30 @@ struct mwl8k_rxd_8366_ap {
        __u8 rx_ctrl;
 } __packed;
 
-#define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT     0x80
-#define MWL8K_8366_AP_RATE_INFO_40MHZ          0x40
-#define MWL8K_8366_AP_RATE_INFO_RATEID(x)      ((x) & 0x3f)
+#define MWL8K_AP_RATE_INFO_MCS_FORMAT          0x80
+#define MWL8K_AP_RATE_INFO_40MHZ               0x40
+#define MWL8K_AP_RATE_INFO_RATEID(x)           ((x) & 0x3f)
 
-#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST    0x80
+#define MWL8K_AP_RX_CTRL_OWNED_BY_HOST         0x80
 
-/* 8366 AP rx_status bits */
-#define MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK          0x80
-#define MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR       0xFF
-#define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR      0x02
-#define MWL8K_8366_AP_RXSTAT_WEP_DECRYPT_ICV_ERR       0x04
-#define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR      0x08
+/* 8366/8764 AP rx_status bits */
+#define MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK               0x80
+#define MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR            0xFF
+#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR           0x02
+#define MWL8K_AP_RXSTAT_WEP_DECRYPT_ICV_ERR            0x04
+#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR           0x08
 
-static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr)
+static void mwl8k_rxd_ap_init(void *_rxd, dma_addr_t next_dma_addr)
 {
-       struct mwl8k_rxd_8366_ap *rxd = _rxd;
+       struct mwl8k_rxd_ap *rxd = _rxd;
 
        rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
-       rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST;
+       rxd->rx_ctrl = MWL8K_AP_RX_CTRL_OWNED_BY_HOST;
 }
 
-static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len)
+static void mwl8k_rxd_ap_refill(void *_rxd, dma_addr_t addr, int len)
 {
-       struct mwl8k_rxd_8366_ap *rxd = _rxd;
+       struct mwl8k_rxd_ap *rxd = _rxd;
 
        rxd->pkt_len = cpu_to_le16(len);
        rxd->pkt_phys_addr = cpu_to_le32(addr);
@@ -960,12 +970,12 @@ static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len)
 }
 
 static int
-mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,
-                         __le16 *qos, s8 *noise)
+mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status,
+                    __le16 *qos, s8 *noise)
 {
-       struct mwl8k_rxd_8366_ap *rxd = _rxd;
+       struct mwl8k_rxd_ap *rxd = _rxd;
 
-       if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST))
+       if (!(rxd->rx_ctrl & MWL8K_AP_RX_CTRL_OWNED_BY_HOST))
                return -1;
        rmb();
 
@@ -974,11 +984,11 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,
        status->signal = -rxd->rssi;
        *noise = -rxd->noise_floor;
 
-       if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) {
+       if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) {
                status->flag |= RX_FLAG_HT;
-               if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ)
+               if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ)
                        status->flag |= RX_FLAG_40MHZ;
-               status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate);
+               status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate);
        } else {
                int i;
 
@@ -1002,19 +1012,19 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,
 
        *qos = rxd->qos_control;
 
-       if ((rxd->rx_status != MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR) &&
-           (rxd->rx_status & MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK) &&
-           (rxd->rx_status & MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR))
+       if ((rxd->rx_status != MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR) &&
+           (rxd->rx_status & MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK) &&
+           (rxd->rx_status & MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR))
                status->flag |= RX_FLAG_MMIC_ERROR;
 
        return le16_to_cpu(rxd->pkt_len);
 }
 
-static struct rxd_ops rxd_8366_ap_ops = {
-       .rxd_size       = sizeof(struct mwl8k_rxd_8366_ap),
-       .rxd_init       = mwl8k_rxd_8366_ap_init,
-       .rxd_refill     = mwl8k_rxd_8366_ap_refill,
-       .rxd_process    = mwl8k_rxd_8366_ap_process,
+static struct rxd_ops rxd_ap_ops = {
+       .rxd_size       = sizeof(struct mwl8k_rxd_ap),
+       .rxd_init       = mwl8k_rxd_ap_init,
+       .rxd_refill     = mwl8k_rxd_ap_refill,
+       .rxd_process    = mwl8k_rxd_ap_process,
 };
 
 /*
@@ -2401,6 +2411,9 @@ mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)
 {
        struct mwl8k_priv *priv = hw->priv;
 
+       if (priv->caps)
+               return;
+
        if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) {
                mwl8k_setup_2ghz_band(hw);
                if (caps & MWL8K_CAP_MIMO)
@@ -2412,6 +2425,8 @@ mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)
                if (caps & MWL8K_CAP_MIMO)
                        mwl8k_set_ht_caps(hw, &priv->band_50, caps);
        }
+
+       priv->caps = caps;
 }
 
 static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
@@ -4792,16 +4807,14 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
        struct mwl8k_priv *priv = hw->priv;
        int rc;
 
-       if (conf->flags & IEEE80211_CONF_IDLE) {
-               mwl8k_cmd_radio_disable(hw);
-               return 0;
-       }
-
        rc = mwl8k_fw_lock(hw);
        if (rc)
                return rc;
 
-       rc = mwl8k_cmd_radio_enable(hw);
+       if (conf->flags & IEEE80211_CONF_IDLE)
+               rc = mwl8k_cmd_radio_disable(hw);
+       else
+               rc = mwl8k_cmd_radio_enable(hw);
        if (rc)
                goto out;
 
@@ -5429,12 +5442,17 @@ enum {
        MWL8363 = 0,
        MWL8687,
        MWL8366,
+       MWL8764,
 };
 
 #define MWL8K_8366_AP_FW_API 3
 #define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw"
 #define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api)
 
+#define MWL8K_8764_AP_FW_API 1
+#define _MWL8K_8764_AP_FW(api) "mwl8k/fmimage_8764_ap-" #api ".fw"
+#define MWL8K_8764_AP_FW(api) _MWL8K_8764_AP_FW(api)
+
 static struct mwl8k_device_info mwl8k_info_tbl[] = {
        [MWL8363] = {
                .part_name      = "88w8363",
@@ -5452,7 +5470,13 @@ static struct mwl8k_device_info mwl8k_info_tbl[] = {
                .fw_image_sta   = "mwl8k/fmimage_8366.fw",
                .fw_image_ap    = MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API),
                .fw_api_ap      = MWL8K_8366_AP_FW_API,
-               .ap_rxd_ops     = &rxd_8366_ap_ops,
+               .ap_rxd_ops     = &rxd_ap_ops,
+       },
+       [MWL8764] = {
+               .part_name      = "88w8764",
+               .fw_image_ap    = MWL8K_8764_AP_FW(MWL8K_8764_AP_FW_API),
+               .fw_api_ap      = MWL8K_8764_AP_FW_API,
+               .ap_rxd_ops     = &rxd_ap_ops,
        },
 };
 
@@ -5474,6 +5498,7 @@ static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
        { PCI_VDEVICE(MARVELL, 0x2a41), .driver_data = MWL8366, },
        { PCI_VDEVICE(MARVELL, 0x2a42), .driver_data = MWL8366, },
        { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, },
+       { PCI_VDEVICE(MARVELL, 0x2b36), .driver_data = MWL8764, },
        { },
 };
 MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
@@ -5995,6 +6020,8 @@ static int mwl8k_probe(struct pci_dev *pdev,
        priv->pdev = pdev;
        priv->device_info = &mwl8k_info_tbl[id->driver_data];
 
+       if (id->driver_data == MWL8764)
+               priv->is_8764 = true;
 
        priv->sram = pci_iomap(pdev, 0, 0x10000);
        if (priv->sram == NULL) {
index 7744f42de1eaa7c95e1f4da84b39cb81d3af099d..1f9cb55c33608c1a5daeab253ebd9372da8827e7 100644 (file)
@@ -1584,7 +1584,7 @@ static int ezusb_probe(struct usb_interface *interface,
        struct ezusb_priv *upriv = NULL;
        struct usb_interface_descriptor *iface_desc;
        struct usb_endpoint_descriptor *ep;
-       const struct firmware *fw_entry;
+       const struct firmware *fw_entry = NULL;
        int retval = 0;
        int i;
 
index aadda99989c007838faf7eb5ca816d18904ea91c..ee654a691f388660c9f33ab6b0ea5538e532f694 100644 (file)
@@ -670,7 +670,7 @@ static unsigned int p54_flush_count(struct p54_common *priv)
        return total;
 }
 
-static void p54_flush(struct ieee80211_hw *dev, bool drop)
+static void p54_flush(struct ieee80211_hw *dev, u32 queues, bool drop)
 {
        struct p54_common *priv = dev->priv;
        unsigned int total, i;
index 4fd49a007b513874da70a6e7477f9035552e2b3b..978e7eb26567fc14cfe2a86faa294670f90e13ad 100644 (file)
@@ -396,7 +396,7 @@ static int p54spi_rx(struct p54s_priv *priv)
 static irqreturn_t p54spi_interrupt(int irq, void *config)
 {
        struct spi_device *spi = config;
-       struct p54s_priv *priv = dev_get_drvdata(&spi->dev);
+       struct p54s_priv *priv = spi_get_drvdata(spi);
 
        ieee80211_queue_work(priv->hw, &priv->work);
 
@@ -609,7 +609,7 @@ static int p54spi_probe(struct spi_device *spi)
 
        priv = hw->priv;
        priv->hw = hw;
-       dev_set_drvdata(&spi->dev, priv);
+       spi_set_drvdata(spi, priv);
        priv->spi = spi;
 
        spi->bits_per_word = 16;
@@ -685,7 +685,7 @@ err_free:
 
 static int p54spi_remove(struct spi_device *spi)
 {
-       struct p54s_priv *priv = dev_get_drvdata(&spi->dev);
+       struct p54s_priv *priv = spi_get_drvdata(spi);
 
        p54_unregister_common(priv->hw);
 
index 3109c0db66e1167e8dffa94582a0c594a7c608da..ebada812b3a5695a10574155cc4f1304bea20862 100644 (file)
@@ -144,7 +144,7 @@ static int psm;
 static char *essid;
 
 /* Default to encapsulation unless translation requested */
-static int translate = 1;
+static bool translate = 1;
 
 static int country = USA;
 
@@ -178,7 +178,7 @@ module_param(hop_dwell, int, 0);
 module_param(beacon_period, int, 0);
 module_param(psm, int, 0);
 module_param(essid, charp, 0);
-module_param(translate, int, 0);
+module_param(translate, bool, 0);
 module_param(country, int, 0);
 module_param(sniffer, int, 0);
 module_param(bc, int, 0);
@@ -953,7 +953,7 @@ static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx,
                           unsigned char *data, int len)
 {
        __be16 proto = ((struct ethhdr *)data)->h_proto;
-       if (ntohs(proto) >= 1536) { /* DIX II ethernet frame */
+       if (ntohs(proto) >= ETH_P_802_3_MIN) { /* DIX II ethernet frame */
                pr_debug("ray_cs translate_frame DIX II\n");
                /* Copy LLC header to card buffer */
                memcpy_toio(&ptx->var, eth2_llc, sizeof(eth2_llc));
@@ -1353,7 +1353,7 @@ static int ray_get_range(struct net_device *dev, struct iw_request_info *info,
 static int ray_set_framing(struct net_device *dev, struct iw_request_info *info,
                           union iwreq_data *wrqu, char *extra)
 {
-       translate = *(extra);   /* Set framing mode */
+       translate = !!*(extra); /* Set framing mode */
 
        return 0;
 }
index 525fd7521dffa0300e660f4600967be5780c8313..8169a85c44986034a958ee01b6dce7052402691c 100644 (file)
@@ -2,7 +2,7 @@
  * Driver for RNDIS based wireless USB devices.
  *
  * Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net>
- * Copyright (C) 2008-2009 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
+ * Copyright (C) 2008-2009 by Jussi Kivilinna <jussi.kivilinna@iki.fi>
  *
  * 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
@@ -2839,8 +2839,7 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
        } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
                cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL);
 
-       if (info != NULL)
-               kfree(info);
+       kfree(info);
 
        priv->connected = true;
        memcpy(priv->bssid, bssid, ETH_ALEN);
index 76cd47eb901e089bbf58669c8eb5fc7da6631a7a..9b915d3a44bee4c7d585ca79d8209415c40d7c01 100644 (file)
@@ -173,6 +173,13 @@ config RT2800USB_RT53XX
          rt2800usb driver.
          Supported chips: RT5370
 
+config RT2800USB_RT55XX
+       bool "rt2800usb - Include support for rt55xx devices (EXPERIMENTAL)"
+       ---help---
+         This adds support for rt55xx wireless chipset family to the
+         rt2800usb driver.
+         Supported chips: RT5572
+
 config RT2800USB_UNKNOWN
        bool "rt2800usb - Include support for unknown (USB) devices"
        default n
index 4db1088a847fc3a347507e5fece8d600f0cc47ce..a7630d5ec8921cb9c3024e9fcf07e0bbf1a5071c 100644 (file)
@@ -51,6 +51,7 @@
  * RF3320 2.4G 1T1R(RT3350/RT3370/RT3390)
  * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392)
  * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662)
+ * RF5592 2.4G/5G 2T2R
  * RF5360 2.4G 1T1R
  * RF5370 2.4G 1T1R
  * RF5390 2.4G 1T1R
@@ -68,6 +69,7 @@
 #define RF3320                         0x000b
 #define RF3322                         0x000c
 #define RF3053                         0x000d
+#define RF5592                         0x000f
 #define RF3290                         0x3290
 #define RF5360                         0x5360
 #define RF5370                         0x5370
 #define REV_RT3390E                    0x0211
 #define REV_RT5390F                    0x0502
 #define REV_RT5390R                    0x1502
+#define REV_RT5592C                    0x0221
 
-/*
- * Signal information.
- * Default offset is required for RSSI <-> dBm conversion.
- */
 #define DEFAULT_RSSI_OFFSET            120
 
 /*
 #define GPIO_SWITCH_6                  FIELD32(0x00000040)
 #define GPIO_SWITCH_7                  FIELD32(0x00000080)
 
+/*
+ * FIXME: where the DEBUG_INDEX name come from?
+ */
+#define MAC_DEBUG_INDEX                        0x05e8
+#define MAC_DEBUG_INDEX_XTAL           FIELD32(0x80000000)
+
 /*
  * MAC Control/Status Registers(CSR).
  * Some values are set in TU, whereas 1 TU == 1024 us.
@@ -1934,6 +1939,9 @@ struct mac_iveiv_entry {
 #define BBP4_BANDWIDTH                 FIELD8(0x18)
 #define BBP4_MAC_IF_CTRL               FIELD8(0x40)
 
+/* BBP27 */
+#define BBP27_RX_CHAIN_SEL             FIELD8(0x60)
+
 /*
  * BBP 47: Bandwidth
  */
@@ -1947,6 +1955,20 @@ struct mac_iveiv_entry {
  */
 #define BBP49_UPDATE_FLAG              FIELD8(0x01)
 
+/*
+ * BBP 105:
+ * - bit0: detect SIG on primary channel only (on 40MHz bandwidth)
+ * - bit1: FEQ (Feed Forward Compensation) for independend streams
+ * - bit2: MLD (Maximum Likehood Detection) for 2 streams (reserved on single
+ *        stream)
+ * - bit4: channel estimation updates based on remodulation of
+ *        L-SIG and HT-SIG symbols
+ */
+#define BBP105_DETECT_SIG_ON_PRIMARY   FIELD8(0x01)
+#define BBP105_FEQ                     FIELD8(0x02)
+#define BBP105_MLD                     FIELD8(0x04)
+#define BBP105_SIG_REMODULATION                FIELD8(0x08)
+
 /*
  * BBP 109
  */
@@ -1966,6 +1988,11 @@ struct mac_iveiv_entry {
  */
 #define BBP152_RX_DEFAULT_ANT          FIELD8(0x80)
 
+/*
+ * BBP 254: unknown
+ */
+#define BBP254_BIT7                    FIELD8(0x80)
+
 /*
  * RFCSR registers
  * The wordsize of the RFCSR is 8 bits.
@@ -2021,10 +2048,19 @@ struct mac_iveiv_entry {
 #define RFCSR7_BIT5                    FIELD8(0x20)
 #define RFCSR7_BITS67                  FIELD8(0xc0)
 
+/*
+ * RFCSR 9:
+ */
+#define RFCSR9_K                       FIELD8(0x0f)
+#define RFCSR9_N                       FIELD8(0x10)
+#define RFCSR9_UNKNOWN                 FIELD8(0x60)
+#define RFCSR9_MOD                     FIELD8(0x80)
+
 /*
  * RFCSR 11:
  */
 #define RFCSR11_R                      FIELD8(0x03)
+#define RFCSR11_MOD                    FIELD8(0xc0)
 
 /*
  * RFCSR 12:
@@ -2130,11 +2166,13 @@ struct mac_iveiv_entry {
  * RFCSR 49:
  */
 #define RFCSR49_TX                     FIELD8(0x3f)
+#define RFCSR49_EP                     FIELD8(0xc0)
 
 /*
  * RFCSR 50:
  */
 #define RFCSR50_TX                     FIELD8(0x3f)
+#define RFCSR50_EP                     FIELD8(0xc0)
 
 /*
  * RF registers
@@ -2496,6 +2534,61 @@ struct mac_iveiv_entry {
 #define EEPROM_BBP_VALUE               FIELD16(0x00ff)
 #define EEPROM_BBP_REG_ID              FIELD16(0xff00)
 
+/*
+ * EEPROM IQ Calibration, unlike other entries those are byte addresses.
+ */
+
+#define EEPROM_IQ_GAIN_CAL_TX0_2G                      0x130
+#define EEPROM_IQ_PHASE_CAL_TX0_2G                     0x131
+#define EEPROM_IQ_GROUPDELAY_CAL_TX0_2G                        0x132
+#define EEPROM_IQ_GAIN_CAL_TX1_2G                      0x133
+#define EEPROM_IQ_PHASE_CAL_TX1_2G                     0x134
+#define EEPROM_IQ_GROUPDELAY_CAL_TX1_2G                        0x135
+#define EEPROM_IQ_GAIN_CAL_RX0_2G                      0x136
+#define EEPROM_IQ_PHASE_CAL_RX0_2G                     0x137
+#define EEPROM_IQ_GROUPDELAY_CAL_RX0_2G                        0x138
+#define EEPROM_IQ_GAIN_CAL_RX1_2G                      0x139
+#define EEPROM_IQ_PHASE_CAL_RX1_2G                     0x13A
+#define EEPROM_IQ_GROUPDELAY_CAL_RX1_2G                        0x13B
+#define EEPROM_RF_IQ_COMPENSATION_CONTROL              0x13C
+#define EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CONTROL    0x13D
+#define EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5G         0x144
+#define EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5G                0x145
+#define EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5G       0X146
+#define EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5G      0x147
+#define EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5G       0x148
+#define EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5G      0x149
+#define EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5G         0x14A
+#define EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5G                0x14B
+#define EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5G       0X14C
+#define EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5G      0x14D
+#define EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5G       0x14E
+#define EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5G      0x14F
+#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH36_TO_CH64_5G   0x150
+#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH36_TO_CH64_5G   0x151
+#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH100_TO_CH138_5G 0x152
+#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH100_TO_CH138_5G 0x153
+#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH140_TO_CH165_5G 0x154
+#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH140_TO_CH165_5G 0x155
+#define EEPROM_IQ_GAIN_CAL_RX0_CH36_TO_CH64_5G         0x156
+#define EEPROM_IQ_PHASE_CAL_RX0_CH36_TO_CH64_5G                0x157
+#define EEPROM_IQ_GAIN_CAL_RX0_CH100_TO_CH138_5G       0X158
+#define EEPROM_IQ_PHASE_CAL_RX0_CH100_TO_CH138_5G      0x159
+#define EEPROM_IQ_GAIN_CAL_RX0_CH140_TO_CH165_5G       0x15A
+#define EEPROM_IQ_PHASE_CAL_RX0_CH140_TO_CH165_5G      0x15B
+#define EEPROM_IQ_GAIN_CAL_RX1_CH36_TO_CH64_5G         0x15C
+#define EEPROM_IQ_PHASE_CAL_RX1_CH36_TO_CH64_5G                0x15D
+#define EEPROM_IQ_GAIN_CAL_RX1_CH100_TO_CH138_5G       0X15E
+#define EEPROM_IQ_PHASE_CAL_RX1_CH100_TO_CH138_5G      0x15F
+#define EEPROM_IQ_GAIN_CAL_RX1_CH140_TO_CH165_5G       0x160
+#define EEPROM_IQ_PHASE_CAL_RX1_CH140_TO_CH165_5G      0x161
+#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH36_TO_CH64_5G   0x162
+#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH36_TO_CH64_5G   0x163
+#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH100_TO_CH138_5G 0x164
+#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH100_TO_CH138_5G 0x165
+#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH140_TO_CH165_5G 0x166
+#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH140_TO_CH165_5G 0x167
+
 /*
  * MCU mailbox commands.
  * MCU_SLEEP - go to power-save mode.
@@ -2535,6 +2628,8 @@ struct mac_iveiv_entry {
 #define TXWI_DESC_SIZE                 (4 * sizeof(__le32))
 #define RXWI_DESC_SIZE                 (4 * sizeof(__le32))
 
+#define TXWI_DESC_SIZE_5592            (5 * sizeof(__le32))
+#define RXWI_DESC_SIZE_5592            (6 * sizeof(__le32))
 /*
  * TX WI structure
  */
index a658b4bc7da2fea78f2160b97907f20d547abde5..7deac4d2459f9b754dab33ed7564cb850fe20bbe 100644 (file)
@@ -527,8 +527,10 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
         */
        rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
        rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
-       if (rt2x00_is_usb(rt2x00dev))
+       if (rt2x00_is_usb(rt2x00dev)) {
                rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0);
+               rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
+       }
        msleep(1);
 
        return 0;
@@ -674,11 +676,6 @@ void rt2800_process_rxwi(struct queue_entry *entry,
         * Convert descriptor AGC value to RSSI value.
         */
        rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word);
-
-       /*
-        * Remove RXWI descriptor from start of buffer.
-        */
-       skb_pull(entry->skb, RXWI_DESC_SIZE);
 }
 EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
 
@@ -1988,8 +1985,21 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
 }
 
 #define POWER_BOUND            0x27
+#define POWER_BOUND_5G         0x2b
 #define FREQ_OFFSET_BOUND      0x5f
 
+static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev)
+{
+       u8 rfcsr;
+
+       rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr);
+       if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND)
+               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset);
+       rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
+}
+
 static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev,
                                         struct ieee80211_conf *conf,
                                         struct rf_channel *rf,
@@ -2010,12 +2020,7 @@ static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev,
                rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1);
        rt2800_rfcsr_write(rt2x00dev, 49, rfcsr);
 
-       rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr);
-       if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND)
-               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND);
-       else
-               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset);
-       rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
+       rt2800_adjust_freq_offset(rt2x00dev);
 
        if (rf->channel <= 14) {
                if (rf->channel == 6)
@@ -2056,13 +2061,7 @@ static void rt2800_config_channel_rf3322(struct rt2x00_dev *rt2x00dev,
        else
                rt2800_rfcsr_write(rt2x00dev, 48, info->default_power2);
 
-       rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr);
-       if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND)
-               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND);
-       else
-               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset);
-
-       rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
+       rt2800_adjust_freq_offset(rt2x00dev);
 
        rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
        rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
@@ -2127,12 +2126,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
        rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
 
-       rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr);
-       if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND)
-               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND);
-       else
-               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset);
-       rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
+       rt2800_adjust_freq_offset(rt2x00dev);
 
        if (rf->channel <= 14) {
                int idx = rf->channel-1;
@@ -2184,6 +2178,382 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
        }
 }
 
+static void rt2800_config_channel_rf55xx(struct rt2x00_dev *rt2x00dev,
+                                        struct ieee80211_conf *conf,
+                                        struct rf_channel *rf,
+                                        struct channel_info *info)
+{
+       u8 rfcsr, ep_reg;
+       u32 reg;
+       int power_bound;
+
+       /* TODO */
+       const bool is_11b = false;
+       const bool is_type_ep = false;
+
+       rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+       rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL,
+                          (rf->channel > 14 || conf_is_ht40(conf)) ? 5 : 0);
+       rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
+
+       /* Order of values on rf_channel entry: N, K, mod, R */
+       rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1 & 0xff);
+
+       rt2800_rfcsr_read(rt2x00dev,  9, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR9_K, rf->rf2 & 0xf);
+       rt2x00_set_field8(&rfcsr, RFCSR9_N, (rf->rf1 & 0x100) >> 8);
+       rt2x00_set_field8(&rfcsr, RFCSR9_MOD, ((rf->rf3 - 8) & 0x4) >> 2);
+       rt2800_rfcsr_write(rt2x00dev, 9, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf4 - 1);
+       rt2x00_set_field8(&rfcsr, RFCSR11_MOD, (rf->rf3 - 8) & 0x3);
+       rt2800_rfcsr_write(rt2x00dev, 11, rfcsr);
+
+       if (rf->channel <= 14) {
+               rt2800_rfcsr_write(rt2x00dev, 10, 0x90);
+               /* FIXME: RF11 owerwrite ? */
+               rt2800_rfcsr_write(rt2x00dev, 11, 0x4A);
+               rt2800_rfcsr_write(rt2x00dev, 12, 0x52);
+               rt2800_rfcsr_write(rt2x00dev, 13, 0x42);
+               rt2800_rfcsr_write(rt2x00dev, 22, 0x40);
+               rt2800_rfcsr_write(rt2x00dev, 24, 0x4A);
+               rt2800_rfcsr_write(rt2x00dev, 25, 0x80);
+               rt2800_rfcsr_write(rt2x00dev, 27, 0x42);
+               rt2800_rfcsr_write(rt2x00dev, 36, 0x80);
+               rt2800_rfcsr_write(rt2x00dev, 37, 0x08);
+               rt2800_rfcsr_write(rt2x00dev, 38, 0x89);
+               rt2800_rfcsr_write(rt2x00dev, 39, 0x1B);
+               rt2800_rfcsr_write(rt2x00dev, 40, 0x0D);
+               rt2800_rfcsr_write(rt2x00dev, 41, 0x9B);
+               rt2800_rfcsr_write(rt2x00dev, 42, 0xD5);
+               rt2800_rfcsr_write(rt2x00dev, 43, 0x72);
+               rt2800_rfcsr_write(rt2x00dev, 44, 0x0E);
+               rt2800_rfcsr_write(rt2x00dev, 45, 0xA2);
+               rt2800_rfcsr_write(rt2x00dev, 46, 0x6B);
+               rt2800_rfcsr_write(rt2x00dev, 48, 0x10);
+               rt2800_rfcsr_write(rt2x00dev, 51, 0x3E);
+               rt2800_rfcsr_write(rt2x00dev, 52, 0x48);
+               rt2800_rfcsr_write(rt2x00dev, 54, 0x38);
+               rt2800_rfcsr_write(rt2x00dev, 56, 0xA1);
+               rt2800_rfcsr_write(rt2x00dev, 57, 0x00);
+               rt2800_rfcsr_write(rt2x00dev, 58, 0x39);
+               rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
+               rt2800_rfcsr_write(rt2x00dev, 61, 0x91);
+               rt2800_rfcsr_write(rt2x00dev, 62, 0x39);
+
+               /* TODO RF27 <- tssi */
+
+               rfcsr = rf->channel <= 10 ? 0x07 : 0x06;
+               rt2800_rfcsr_write(rt2x00dev, 23, rfcsr);
+               rt2800_rfcsr_write(rt2x00dev, 59, rfcsr);
+
+               if (is_11b) {
+                       /* CCK */
+                       rt2800_rfcsr_write(rt2x00dev, 31, 0xF8);
+                       rt2800_rfcsr_write(rt2x00dev, 32, 0xC0);
+                       if (is_type_ep)
+                               rt2800_rfcsr_write(rt2x00dev, 55, 0x06);
+                       else
+                               rt2800_rfcsr_write(rt2x00dev, 55, 0x47);
+               } else {
+                       /* OFDM */
+                       if (is_type_ep)
+                               rt2800_rfcsr_write(rt2x00dev, 55, 0x03);
+                       else
+                               rt2800_rfcsr_write(rt2x00dev, 55, 0x43);
+               }
+
+               power_bound = POWER_BOUND;
+               ep_reg = 0x2;
+       } else {
+               rt2800_rfcsr_write(rt2x00dev, 10, 0x97);
+               /* FIMXE: RF11 overwrite */
+               rt2800_rfcsr_write(rt2x00dev, 11, 0x40);
+               rt2800_rfcsr_write(rt2x00dev, 25, 0xBF);
+               rt2800_rfcsr_write(rt2x00dev, 27, 0x42);
+               rt2800_rfcsr_write(rt2x00dev, 36, 0x00);
+               rt2800_rfcsr_write(rt2x00dev, 37, 0x04);
+               rt2800_rfcsr_write(rt2x00dev, 38, 0x85);
+               rt2800_rfcsr_write(rt2x00dev, 40, 0x42);
+               rt2800_rfcsr_write(rt2x00dev, 41, 0xBB);
+               rt2800_rfcsr_write(rt2x00dev, 42, 0xD7);
+               rt2800_rfcsr_write(rt2x00dev, 45, 0x41);
+               rt2800_rfcsr_write(rt2x00dev, 48, 0x00);
+               rt2800_rfcsr_write(rt2x00dev, 57, 0x77);
+               rt2800_rfcsr_write(rt2x00dev, 60, 0x05);
+               rt2800_rfcsr_write(rt2x00dev, 61, 0x01);
+
+               /* TODO RF27 <- tssi */
+
+               if (rf->channel >= 36 && rf->channel <= 64) {
+
+                       rt2800_rfcsr_write(rt2x00dev, 12, 0x2E);
+                       rt2800_rfcsr_write(rt2x00dev, 13, 0x22);
+                       rt2800_rfcsr_write(rt2x00dev, 22, 0x60);
+                       rt2800_rfcsr_write(rt2x00dev, 23, 0x7F);
+                       if (rf->channel <= 50)
+                               rt2800_rfcsr_write(rt2x00dev, 24, 0x09);
+                       else if (rf->channel >= 52)
+                               rt2800_rfcsr_write(rt2x00dev, 24, 0x07);
+                       rt2800_rfcsr_write(rt2x00dev, 39, 0x1C);
+                       rt2800_rfcsr_write(rt2x00dev, 43, 0x5B);
+                       rt2800_rfcsr_write(rt2x00dev, 44, 0X40);
+                       rt2800_rfcsr_write(rt2x00dev, 46, 0X00);
+                       rt2800_rfcsr_write(rt2x00dev, 51, 0xFE);
+                       rt2800_rfcsr_write(rt2x00dev, 52, 0x0C);
+                       rt2800_rfcsr_write(rt2x00dev, 54, 0xF8);
+                       if (rf->channel <= 50) {
+                               rt2800_rfcsr_write(rt2x00dev, 55, 0x06),
+                               rt2800_rfcsr_write(rt2x00dev, 56, 0xD3);
+                       } else if (rf->channel >= 52) {
+                               rt2800_rfcsr_write(rt2x00dev, 55, 0x04);
+                               rt2800_rfcsr_write(rt2x00dev, 56, 0xBB);
+                       }
+
+                       rt2800_rfcsr_write(rt2x00dev, 58, 0x15);
+                       rt2800_rfcsr_write(rt2x00dev, 59, 0x7F);
+                       rt2800_rfcsr_write(rt2x00dev, 62, 0x15);
+
+               } else if (rf->channel >= 100 && rf->channel <= 165) {
+
+                       rt2800_rfcsr_write(rt2x00dev, 12, 0x0E);
+                       rt2800_rfcsr_write(rt2x00dev, 13, 0x42);
+                       rt2800_rfcsr_write(rt2x00dev, 22, 0x40);
+                       if (rf->channel <= 153) {
+                               rt2800_rfcsr_write(rt2x00dev, 23, 0x3C);
+                               rt2800_rfcsr_write(rt2x00dev, 24, 0x06);
+                       } else if (rf->channel >= 155) {
+                               rt2800_rfcsr_write(rt2x00dev, 23, 0x38);
+                               rt2800_rfcsr_write(rt2x00dev, 24, 0x05);
+                       }
+                       if (rf->channel <= 138) {
+                               rt2800_rfcsr_write(rt2x00dev, 39, 0x1A);
+                               rt2800_rfcsr_write(rt2x00dev, 43, 0x3B);
+                               rt2800_rfcsr_write(rt2x00dev, 44, 0x20);
+                               rt2800_rfcsr_write(rt2x00dev, 46, 0x18);
+                       } else if (rf->channel >= 140) {
+                               rt2800_rfcsr_write(rt2x00dev, 39, 0x18);
+                               rt2800_rfcsr_write(rt2x00dev, 43, 0x1B);
+                               rt2800_rfcsr_write(rt2x00dev, 44, 0x10);
+                               rt2800_rfcsr_write(rt2x00dev, 46, 0X08);
+                       }
+                       if (rf->channel <= 124)
+                               rt2800_rfcsr_write(rt2x00dev, 51, 0xFC);
+                       else if (rf->channel >= 126)
+                               rt2800_rfcsr_write(rt2x00dev, 51, 0xEC);
+                       if (rf->channel <= 138)
+                               rt2800_rfcsr_write(rt2x00dev, 52, 0x06);
+                       else if (rf->channel >= 140)
+                               rt2800_rfcsr_write(rt2x00dev, 52, 0x06);
+                       rt2800_rfcsr_write(rt2x00dev, 54, 0xEB);
+                       if (rf->channel <= 138)
+                               rt2800_rfcsr_write(rt2x00dev, 55, 0x01);
+                       else if (rf->channel >= 140)
+                               rt2800_rfcsr_write(rt2x00dev, 55, 0x00);
+                       if (rf->channel <= 128)
+                               rt2800_rfcsr_write(rt2x00dev, 56, 0xBB);
+                       else if (rf->channel >= 130)
+                               rt2800_rfcsr_write(rt2x00dev, 56, 0xAB);
+                       if (rf->channel <= 116)
+                               rt2800_rfcsr_write(rt2x00dev, 58, 0x1D);
+                       else if (rf->channel >= 118)
+                               rt2800_rfcsr_write(rt2x00dev, 58, 0x15);
+                       if (rf->channel <= 138)
+                               rt2800_rfcsr_write(rt2x00dev, 59, 0x3F);
+                       else if (rf->channel >= 140)
+                               rt2800_rfcsr_write(rt2x00dev, 59, 0x7C);
+                       if (rf->channel <= 116)
+                               rt2800_rfcsr_write(rt2x00dev, 62, 0x1D);
+                       else if (rf->channel >= 118)
+                               rt2800_rfcsr_write(rt2x00dev, 62, 0x15);
+               }
+
+               power_bound = POWER_BOUND_5G;
+               ep_reg = 0x3;
+       }
+
+       rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr);
+       if (info->default_power1 > power_bound)
+               rt2x00_set_field8(&rfcsr, RFCSR49_TX, power_bound);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1);
+       if (is_type_ep)
+               rt2x00_set_field8(&rfcsr, RFCSR49_EP, ep_reg);
+       rt2800_rfcsr_write(rt2x00dev, 49, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+       if (info->default_power1 > power_bound)
+               rt2x00_set_field8(&rfcsr, RFCSR50_TX, power_bound);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR50_TX, info->default_power2);
+       if (is_type_ep)
+               rt2x00_set_field8(&rfcsr, RFCSR50_EP, ep_reg);
+       rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
+       rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
+
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD,
+                         rt2x00dev->default_ant.tx_chain_num >= 1);
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD,
+                         rt2x00dev->default_ant.tx_chain_num == 2);
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
+
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD,
+                         rt2x00dev->default_ant.rx_chain_num >= 1);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD,
+                         rt2x00dev->default_ant.rx_chain_num == 2);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
+
+       rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0xe4);
+
+       if (conf_is_ht40(conf))
+               rt2800_rfcsr_write(rt2x00dev, 30, 0x16);
+       else
+               rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
+
+       if (!is_11b) {
+               rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
+               rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
+       }
+
+       /* TODO proper frequency adjustment */
+       rt2800_adjust_freq_offset(rt2x00dev);
+
+       /* TODO merge with others */
+       rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1);
+       rt2800_rfcsr_write(rt2x00dev, 3, rfcsr);
+
+       /* BBP settings */
+       rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
+       rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
+       rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain);
+
+       rt2800_bbp_write(rt2x00dev, 79, (rf->channel <= 14) ? 0x1C : 0x18);
+       rt2800_bbp_write(rt2x00dev, 80, (rf->channel <= 14) ? 0x0E : 0x08);
+       rt2800_bbp_write(rt2x00dev, 81, (rf->channel <= 14) ? 0x3A : 0x38);
+       rt2800_bbp_write(rt2x00dev, 82, (rf->channel <= 14) ? 0x62 : 0x92);
+
+       /* GLRT band configuration */
+       rt2800_bbp_write(rt2x00dev, 195, 128);
+       rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0xE0 : 0xF0);
+       rt2800_bbp_write(rt2x00dev, 195, 129);
+       rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x1F : 0x1E);
+       rt2800_bbp_write(rt2x00dev, 195, 130);
+       rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x38 : 0x28);
+       rt2800_bbp_write(rt2x00dev, 195, 131);
+       rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x32 : 0x20);
+       rt2800_bbp_write(rt2x00dev, 195, 133);
+       rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x28 : 0x7F);
+       rt2800_bbp_write(rt2x00dev, 195, 124);
+       rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x19 : 0x7F);
+}
+
+static void rt2800_bbp_write_with_rx_chain(struct rt2x00_dev *rt2x00dev,
+                                          const unsigned int word,
+                                          const u8 value)
+{
+       u8 chain, reg;
+
+       for (chain = 0; chain < rt2x00dev->default_ant.rx_chain_num; chain++) {
+               rt2800_bbp_read(rt2x00dev, 27, &reg);
+               rt2x00_set_field8(&reg,  BBP27_RX_CHAIN_SEL, chain);
+               rt2800_bbp_write(rt2x00dev, 27, reg);
+
+               rt2800_bbp_write(rt2x00dev, word, value);
+       }
+}
+
+static void rt2800_iq_calibrate(struct rt2x00_dev *rt2x00dev, int channel)
+{
+       u8 cal;
+
+       /* TX0 IQ Gain */
+       rt2800_bbp_write(rt2x00dev, 158, 0x2c);
+       if (channel <= 14)
+               cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_GAIN_CAL_TX0_2G);
+       else if (channel >= 36 && channel <= 64)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5G);
+       else if (channel >= 100 && channel <= 138)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5G);
+       else if (channel >= 140 && channel <= 165)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5G);
+       else
+               cal = 0;
+       rt2800_bbp_write(rt2x00dev, 159, cal);
+
+       /* TX0 IQ Phase */
+       rt2800_bbp_write(rt2x00dev, 158, 0x2d);
+       if (channel <= 14)
+               cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_PHASE_CAL_TX0_2G);
+       else if (channel >= 36 && channel <= 64)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5G);
+       else if (channel >= 100 && channel <= 138)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5G);
+       else if (channel >= 140 && channel <= 165)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5G);
+       else
+               cal = 0;
+       rt2800_bbp_write(rt2x00dev, 159, cal);
+
+       /* TX1 IQ Gain */
+       rt2800_bbp_write(rt2x00dev, 158, 0x4a);
+       if (channel <= 14)
+               cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_GAIN_CAL_TX1_2G);
+       else if (channel >= 36 && channel <= 64)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5G);
+       else if (channel >= 100 && channel <= 138)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5G);
+       else if (channel >= 140 && channel <= 165)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5G);
+       else
+               cal = 0;
+       rt2800_bbp_write(rt2x00dev, 159, cal);
+
+       /* TX1 IQ Phase */
+       rt2800_bbp_write(rt2x00dev, 158, 0x4b);
+       if (channel <= 14)
+               cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_PHASE_CAL_TX1_2G);
+       else if (channel >= 36 && channel <= 64)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5G);
+       else if (channel >= 100 && channel <= 138)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5G);
+       else if (channel >= 140 && channel <= 165)
+               cal = rt2x00_eeprom_byte(rt2x00dev,
+                                        EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5G);
+       else
+               cal = 0;
+       rt2800_bbp_write(rt2x00dev, 159, cal);
+
+       /* FIXME: possible RX0, RX1 callibration ? */
+
+       /* RF IQ compensation control */
+       rt2800_bbp_write(rt2x00dev, 158, 0x04);
+       cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_RF_IQ_COMPENSATION_CONTROL);
+       rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0);
+
+       /* RF IQ imbalance compensation control */
+       rt2800_bbp_write(rt2x00dev, 158, 0x03);
+       cal = rt2x00_eeprom_byte(rt2x00dev,
+                                EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CONTROL);
+       rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0);
+}
+
 static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                                  struct ieee80211_conf *conf,
                                  struct rf_channel *rf,
@@ -2225,6 +2595,9 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        case RF5392:
                rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info);
                break;
+       case RF5592:
+               rt2800_config_channel_rf55xx(rt2x00dev, conf, rf, info);
+               break;
        default:
                rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info);
        }
@@ -2326,6 +2699,17 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        if (rt2x00_rt(rt2x00dev, RT3572))
                rt2800_rfcsr_write(rt2x00dev, 8, 0x80);
 
+       if (rt2x00_rt(rt2x00dev, RT5592)) {
+               rt2800_bbp_write(rt2x00dev, 195, 141);
+               rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a);
+
+               /* AGC init */
+               reg = (rf->channel <= 14 ? 0x1c : 0x24) + 2 * rt2x00dev->lna_gain;
+               rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg);
+
+               rt2800_iq_calibrate(rt2x00dev, rf->channel);
+       }
+
        rt2800_bbp_read(rt2x00dev, 4, &bbp);
        rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf));
        rt2800_bbp_write(rt2x00dev, 4, bbp);
@@ -2938,13 +3322,16 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
                    rt2x00_rt(rt2x00dev, RT3390) ||
                    rt2x00_rt(rt2x00dev, RT3572) ||
                    rt2x00_rt(rt2x00dev, RT5390) ||
-                   rt2x00_rt(rt2x00dev, RT5392))
+                   rt2x00_rt(rt2x00dev, RT5392) ||
+                   rt2x00_rt(rt2x00dev, RT5592))
                        vgc = 0x1c + (2 * rt2x00dev->lna_gain);
                else
                        vgc = 0x2e + rt2x00dev->lna_gain;
        } else { /* 5GHZ band */
                if (rt2x00_rt(rt2x00dev, RT3572))
                        vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3;
+               else if (rt2x00_rt(rt2x00dev, RT5592))
+                       vgc = 0x24 + (2 * rt2x00dev->lna_gain);
                else {
                        if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags))
                                vgc = 0x32 + (rt2x00dev->lna_gain * 5) / 3;
@@ -2960,7 +3347,11 @@ static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev,
                                  struct link_qual *qual, u8 vgc_level)
 {
        if (qual->vgc_level != vgc_level) {
-               rt2800_bbp_write(rt2x00dev, 66, vgc_level);
+               if (rt2x00_rt(rt2x00dev, RT5592)) {
+                       rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a);
+                       rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level);
+               } else
+                       rt2800_bbp_write(rt2x00dev, 66, vgc_level);
                qual->vgc_level = vgc_level;
                qual->vgc_level_reg = vgc_level;
        }
@@ -2975,15 +3366,23 @@ EXPORT_SYMBOL_GPL(rt2800_reset_tuner);
 void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
                       const u32 count)
 {
+       u8 vgc;
+
        if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C))
                return;
-
        /*
-        * When RSSI is better then -80 increase VGC level with 0x10
+        * When RSSI is better then -80 increase VGC level with 0x10, except
+        * for rt5592 chip.
         */
-       rt2800_set_vgc(rt2x00dev, qual,
-                      rt2800_get_default_vgc(rt2x00dev) +
-                      ((qual->rssi > -80) * 0x10));
+
+       vgc = rt2800_get_default_vgc(rt2x00dev);
+
+       if (rt2x00_rt(rt2x00dev, RT5592) && qual->rssi > -65)
+               vgc += 0x20;
+       else if (qual->rssi > -80)
+               vgc += 0x10;
+
+       rt2800_set_vgc(rt2x00dev, qual, vgc);
 }
 EXPORT_SYMBOL_GPL(rt2800_link_tuner);
 
@@ -3122,7 +3521,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
                rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
                rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
        } else if (rt2x00_rt(rt2x00dev, RT5390) ||
-                  rt2x00_rt(rt2x00dev, RT5392)) {
+                  rt2x00_rt(rt2x00dev, RT5392) ||
+                  rt2x00_rt(rt2x00dev, RT5592)) {
                rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404);
                rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
                rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
@@ -3302,7 +3702,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_field32(&reg, TXOP_CTRL_CFG_EXT_CWMIN, 0);
        rt2800_register_write(rt2x00dev, TXOP_CTRL_CFG, reg);
 
-       rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, 0x00000002);
+       reg = rt2x00_rt(rt2x00dev, RT5592) ? 0x00000082 : 0x00000002;
+       rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, reg);
 
        rt2800_register_read(rt2x00dev, TX_RTS_CFG, &reg);
        rt2x00_set_field32(&reg, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32);
@@ -3487,6 +3888,136 @@ static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
        return -EACCES;
 }
 
+static void rt2800_bbp4_mac_if_ctrl(struct rt2x00_dev *rt2x00dev)
+{
+       u8 value;
+
+       rt2800_bbp_read(rt2x00dev, 4, &value);
+       rt2x00_set_field8(&value, BBP4_MAC_IF_CTRL, 1);
+       rt2800_bbp_write(rt2x00dev, 4, value);
+}
+
+static void rt2800_init_freq_calibration(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_bbp_write(rt2x00dev, 142, 1);
+       rt2800_bbp_write(rt2x00dev, 143, 57);
+}
+
+static void rt2800_init_bbp_5592_glrt(struct rt2x00_dev *rt2x00dev)
+{
+       const u8 glrt_table[] = {
+               0xE0, 0x1F, 0X38, 0x32, 0x08, 0x28, 0x19, 0x0A, 0xFF, 0x00, /* 128 ~ 137 */
+               0x16, 0x10, 0x10, 0x0B, 0x36, 0x2C, 0x26, 0x24, 0x42, 0x36, /* 138 ~ 147 */
+               0x30, 0x2D, 0x4C, 0x46, 0x3D, 0x40, 0x3E, 0x42, 0x3D, 0x40, /* 148 ~ 157 */
+               0X3C, 0x34, 0x2C, 0x2F, 0x3C, 0x35, 0x2E, 0x2A, 0x49, 0x41, /* 158 ~ 167 */
+               0x36, 0x31, 0x30, 0x30, 0x0E, 0x0D, 0x28, 0x21, 0x1C, 0x16, /* 168 ~ 177 */
+               0x50, 0x4A, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, /* 178 ~ 187 */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 188 ~ 197 */
+               0x00, 0x00, 0x7D, 0x14, 0x32, 0x2C, 0x36, 0x4C, 0x43, 0x2C, /* 198 ~ 207 */
+               0x2E, 0x36, 0x30, 0x6E,                                     /* 208 ~ 211 */
+       };
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(glrt_table); i++) {
+               rt2800_bbp_write(rt2x00dev, 195, 128 + i);
+               rt2800_bbp_write(rt2x00dev, 196, glrt_table[i]);
+       }
+};
+
+static void rt2800_init_bbb_early(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_bbp_write(rt2x00dev, 65, 0x2C);
+       rt2800_bbp_write(rt2x00dev, 66, 0x38);
+       rt2800_bbp_write(rt2x00dev, 68, 0x0B);
+       rt2800_bbp_write(rt2x00dev, 69, 0x12);
+       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+       rt2800_bbp_write(rt2x00dev, 73, 0x10);
+       rt2800_bbp_write(rt2x00dev, 81, 0x37);
+       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+       rt2800_bbp_write(rt2x00dev, 83, 0x6A);
+       rt2800_bbp_write(rt2x00dev, 84, 0x99);
+       rt2800_bbp_write(rt2x00dev, 86, 0x00);
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+       rt2800_bbp_write(rt2x00dev, 92, 0x00);
+       rt2800_bbp_write(rt2x00dev, 103, 0x00);
+       rt2800_bbp_write(rt2x00dev, 105, 0x05);
+       rt2800_bbp_write(rt2x00dev, 106, 0x35);
+}
+
+static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
+{
+       int ant, div_mode;
+       u16 eeprom;
+       u8 value;
+
+       rt2800_init_bbb_early(rt2x00dev);
+
+       rt2800_bbp_read(rt2x00dev, 105, &value);
+       rt2x00_set_field8(&value, BBP105_MLD,
+                         rt2x00dev->default_ant.rx_chain_num == 2);
+       rt2800_bbp_write(rt2x00dev, 105, value);
+
+       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+       rt2800_bbp_write(rt2x00dev, 20, 0x06);
+       rt2800_bbp_write(rt2x00dev, 31, 0x08);
+       rt2800_bbp_write(rt2x00dev, 65, 0x2C);
+       rt2800_bbp_write(rt2x00dev, 68, 0xDD);
+       rt2800_bbp_write(rt2x00dev, 69, 0x1A);
+       rt2800_bbp_write(rt2x00dev, 70, 0x05);
+       rt2800_bbp_write(rt2x00dev, 73, 0x13);
+       rt2800_bbp_write(rt2x00dev, 74, 0x0F);
+       rt2800_bbp_write(rt2x00dev, 75, 0x4F);
+       rt2800_bbp_write(rt2x00dev, 76, 0x28);
+       rt2800_bbp_write(rt2x00dev, 77, 0x59);
+       rt2800_bbp_write(rt2x00dev, 84, 0x9A);
+       rt2800_bbp_write(rt2x00dev, 86, 0x38);
+       rt2800_bbp_write(rt2x00dev, 88, 0x90);
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+       rt2800_bbp_write(rt2x00dev, 92, 0x02);
+       rt2800_bbp_write(rt2x00dev, 95, 0x9a);
+       rt2800_bbp_write(rt2x00dev, 98, 0x12);
+       rt2800_bbp_write(rt2x00dev, 103, 0xC0);
+       rt2800_bbp_write(rt2x00dev, 104, 0x92);
+       /* FIXME BBP105 owerwrite */
+       rt2800_bbp_write(rt2x00dev, 105, 0x3C);
+       rt2800_bbp_write(rt2x00dev, 106, 0x35);
+       rt2800_bbp_write(rt2x00dev, 128, 0x12);
+       rt2800_bbp_write(rt2x00dev, 134, 0xD0);
+       rt2800_bbp_write(rt2x00dev, 135, 0xF6);
+       rt2800_bbp_write(rt2x00dev, 137, 0x0F);
+
+       /* Initialize GLRT (Generalized Likehood Radio Test) */
+       rt2800_init_bbp_5592_glrt(rt2x00dev);
+
+       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
+       ant = (div_mode == 3) ? 1 : 0;
+       rt2800_bbp_read(rt2x00dev, 152, &value);
+       if (ant == 0) {
+               /* Main antenna */
+               rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
+       } else {
+               /* Auxiliary antenna */
+               rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
+       }
+       rt2800_bbp_write(rt2x00dev, 152, value);
+
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) {
+               rt2800_bbp_read(rt2x00dev, 254, &value);
+               rt2x00_set_field8(&value, BBP254_BIT7, 1);
+               rt2800_bbp_write(rt2x00dev, 254, value);
+       }
+
+       rt2800_init_freq_calibration(rt2x00dev);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x19);
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C))
+               rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+}
+
 static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
 {
        unsigned int i;
@@ -3498,6 +4029,11 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
                     rt2800_wait_bbp_ready(rt2x00dev)))
                return -EACCES;
 
+       if (rt2x00_rt(rt2x00dev, RT5592)) {
+               rt2800_init_bbp_5592(rt2x00dev);
+               return 0;
+       }
+
        if (rt2x00_rt(rt2x00dev, RT3352)) {
                rt2800_bbp_write(rt2x00dev, 3, 0x00);
                rt2800_bbp_write(rt2x00dev, 4, 0x50);
@@ -3505,11 +4041,8 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
 
        if (rt2x00_rt(rt2x00dev, RT3290) ||
            rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392)) {
-               rt2800_bbp_read(rt2x00dev, 4, &value);
-               rt2x00_set_field8(&value, BBP4_MAC_IF_CTRL, 1);
-               rt2800_bbp_write(rt2x00dev, 4, value);
-       }
+           rt2x00_rt(rt2x00dev, RT5392))
+               rt2800_bbp4_mac_if_ctrl(rt2x00dev);
 
        if (rt2800_is_305x_soc(rt2x00dev) ||
            rt2x00_rt(rt2x00dev, RT3290) ||
@@ -3783,9 +4316,7 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
                        rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
                rt2800_bbp_write(rt2x00dev, 152, value);
 
-               /* Init frequency calibration */
-               rt2800_bbp_write(rt2x00dev, 142, 1);
-               rt2800_bbp_write(rt2x00dev, 143, 57);
+               rt2800_init_freq_calibration(rt2x00dev);
        }
 
        for (i = 0; i < EEPROM_BBP_SIZE; i++) {
@@ -4259,6 +4790,69 @@ static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev)
        rt2800_rfcsr_write(rt2x00dev, 63, 0x07);
 }
 
+static void rt2800_init_rfcsr_5592(struct rt2x00_dev *rt2x00dev)
+{
+       u8 reg;
+       u16 eeprom;
+
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x3F);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x08);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x08);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0xE4);
+       rt2800_rfcsr_write(rt2x00dev, 7, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 14, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 15, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 16, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 19, 0x4D);
+       rt2800_rfcsr_write(rt2x00dev, 20, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 21, 0x8D);
+       rt2800_rfcsr_write(rt2x00dev, 26, 0x82);
+       rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 33, 0xC0);
+       rt2800_rfcsr_write(rt2x00dev, 34, 0x07);
+       rt2800_rfcsr_write(rt2x00dev, 35, 0x12);
+       rt2800_rfcsr_write(rt2x00dev, 47, 0x0C);
+       rt2800_rfcsr_write(rt2x00dev, 53, 0x22);
+       rt2800_rfcsr_write(rt2x00dev, 63, 0x07);
+
+       rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
+       msleep(1);
+
+       rt2800_adjust_freq_offset(rt2x00dev);
+
+       rt2800_bbp_read(rt2x00dev, 138, &reg);
+
+       /*  Turn off unused DAC1 and ADC1 to reduce power consumption */
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
+               rt2x00_set_field8(&reg, BBP138_RX_ADC1, 0);
+       if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
+               rt2x00_set_field8(&reg, BBP138_TX_DAC1, 1);
+
+       rt2800_bbp_write(rt2x00dev, 138, reg);
+
+       /* Enable DC filter */
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C))
+               rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+       rt2800_rfcsr_read(rt2x00dev, 38, &reg);
+       rt2x00_set_field8(&reg, RFCSR38_RX_LO1_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 38, reg);
+
+       rt2800_rfcsr_read(rt2x00dev, 39, &reg);
+       rt2x00_set_field8(&reg, RFCSR39_RX_LO2_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 39, reg);
+
+       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+       rt2800_rfcsr_read(rt2x00dev, 30, &reg);
+       rt2x00_set_field8(&reg, RFCSR30_RX_VCM, 2);
+       rt2800_rfcsr_write(rt2x00dev, 30, reg);
+}
+
 static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
 {
        struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
@@ -4276,6 +4870,8 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
            !rt2x00_rt(rt2x00dev, RT3572) &&
            !rt2x00_rt(rt2x00dev, RT5390) &&
            !rt2x00_rt(rt2x00dev, RT5392) &&
+           !rt2x00_rt(rt2x00dev, RT5392) &&
+           !rt2x00_rt(rt2x00dev, RT5592) &&
            !rt2800_is_305x_soc(rt2x00dev))
                return 0;
 
@@ -4330,6 +4926,9 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
        case RT5392:
                rt2800_init_rfcsr_5392(rt2x00dev);
                break;
+       case RT5592:
+               rt2800_init_rfcsr_5592(rt2x00dev);
+               return 0;
        }
 
        if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) {
@@ -4427,7 +5026,8 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
        if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F) ||
            rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
            rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) ||
-           rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E))
+           rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E) ||
+           rt2x00_rt_rev_lt(rt2x00dev, RT5592, REV_RT5592C))
                rt2800_rfcsr_write(rt2x00dev, 27, 0x03);
 
        rt2800_register_read(rt2x00dev, OPT_14_CSR, &reg);
@@ -4451,7 +5051,8 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
                rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
        }
 
-       if (rt2x00_rt(rt2x00dev, RT3090)) {
+       if (rt2x00_rt(rt2x00dev, RT3090) ||
+           rt2x00_rt(rt2x00dev, RT5592)) {
                rt2800_bbp_read(rt2x00dev, 138, &bbp);
 
                /*  Turn off unused DAC1 and ADC1 to reduce power consumption */
@@ -4507,7 +5108,8 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
        }
 
        if (rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392)) {
+           rt2x00_rt(rt2x00dev, RT5392) ||
+           rt2x00_rt(rt2x00dev, RT5592)) {
                rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr);
                rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0);
                rt2800_rfcsr_write(rt2x00dev, 38, rfcsr);
@@ -4533,15 +5135,23 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
         * Initialize all registers.
         */
        if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) ||
-                    rt2800_init_registers(rt2x00dev) ||
-                    rt2800_init_bbp(rt2x00dev) ||
-                    rt2800_init_rfcsr(rt2x00dev)))
+                    rt2800_init_registers(rt2x00dev)))
                return -EIO;
 
        /*
         * Send signal to firmware during boot time.
         */
-       rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
+       rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
+       rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
+       if (rt2x00_is_usb(rt2x00dev)) {
+               rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0);
+               rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
+       }
+       msleep(1);
+
+       if (unlikely(rt2800_init_bbp(rt2x00dev) ||
+                    rt2800_init_rfcsr(rt2x00dev)))
+               return -EIO;
 
        if (rt2x00_is_usb(rt2x00dev) &&
            (rt2x00_rt(rt2x00dev, RT3070) ||
@@ -4821,9 +5431,9 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
 
 static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
 {
-       u32 reg;
        u16 value;
        u16 eeprom;
+       u16 rf;
 
        /*
         * Read EEPROM word for configuration.
@@ -4835,41 +5445,14 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
         * RT28xx/RT30xx: defined in "EEPROM_NIC_CONF0_RF_TYPE" field
         * RT53xx: defined in "EEPROM_CHIP_ID" field
         */
-       if (rt2x00_rt(rt2x00dev, RT3290))
-               rt2800_register_read(rt2x00dev, MAC_CSR0_3290, &reg);
-       else
-               rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
-
-       if (rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT3290 ||
-           rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT5390 ||
-           rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT5392)
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &value);
+       if (rt2x00_rt(rt2x00dev, RT3290) ||
+           rt2x00_rt(rt2x00dev, RT5390) ||
+           rt2x00_rt(rt2x00dev, RT5392))
+               rt2x00_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf);
        else
-               value = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE);
-
-       rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET),
-                       value, rt2x00_get_field32(reg, MAC_CSR0_REVISION));
-
-       switch (rt2x00dev->chip.rt) {
-       case RT2860:
-       case RT2872:
-       case RT2883:
-       case RT3070:
-       case RT3071:
-       case RT3090:
-       case RT3290:
-       case RT3352:
-       case RT3390:
-       case RT3572:
-       case RT5390:
-       case RT5392:
-               break;
-       default:
-               ERROR(rt2x00dev, "Invalid RT chipset 0x%04x detected.\n", rt2x00dev->chip.rt);
-               return -ENODEV;
-       }
+               rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE);
 
-       switch (rt2x00dev->chip.rf) {
+       switch (rf) {
        case RF2820:
        case RF2850:
        case RF2720:
@@ -4887,13 +5470,15 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        case RF5372:
        case RF5390:
        case RF5392:
+       case RF5592:
                break;
        default:
-               ERROR(rt2x00dev, "Invalid RF chipset 0x%04x detected.\n",
-                     rt2x00dev->chip.rf);
+               ERROR(rt2x00dev, "Invalid RF chipset 0x%04x detected.\n", rf);
                return -ENODEV;
        }
 
+       rt2x00_set_rf(rt2x00dev, rf);
+
        /*
         * Identify default antenna configuration.
         */
@@ -5122,6 +5707,138 @@ static const struct rf_channel rf_vals_3x[] = {
        {173, 0x61, 0, 9},
 };
 
+static const struct rf_channel rf_vals_5592_xtal20[] = {
+       /* Channel, N, K, mod, R */
+       {1, 482, 4, 10, 3},
+       {2, 483, 4, 10, 3},
+       {3, 484, 4, 10, 3},
+       {4, 485, 4, 10, 3},
+       {5, 486, 4, 10, 3},
+       {6, 487, 4, 10, 3},
+       {7, 488, 4, 10, 3},
+       {8, 489, 4, 10, 3},
+       {9, 490, 4, 10, 3},
+       {10, 491, 4, 10, 3},
+       {11, 492, 4, 10, 3},
+       {12, 493, 4, 10, 3},
+       {13, 494, 4, 10, 3},
+       {14, 496, 8, 10, 3},
+       {36, 172, 8, 12, 1},
+       {38, 173, 0, 12, 1},
+       {40, 173, 4, 12, 1},
+       {42, 173, 8, 12, 1},
+       {44, 174, 0, 12, 1},
+       {46, 174, 4, 12, 1},
+       {48, 174, 8, 12, 1},
+       {50, 175, 0, 12, 1},
+       {52, 175, 4, 12, 1},
+       {54, 175, 8, 12, 1},
+       {56, 176, 0, 12, 1},
+       {58, 176, 4, 12, 1},
+       {60, 176, 8, 12, 1},
+       {62, 177, 0, 12, 1},
+       {64, 177, 4, 12, 1},
+       {100, 183, 4, 12, 1},
+       {102, 183, 8, 12, 1},
+       {104, 184, 0, 12, 1},
+       {106, 184, 4, 12, 1},
+       {108, 184, 8, 12, 1},
+       {110, 185, 0, 12, 1},
+       {112, 185, 4, 12, 1},
+       {114, 185, 8, 12, 1},
+       {116, 186, 0, 12, 1},
+       {118, 186, 4, 12, 1},
+       {120, 186, 8, 12, 1},
+       {122, 187, 0, 12, 1},
+       {124, 187, 4, 12, 1},
+       {126, 187, 8, 12, 1},
+       {128, 188, 0, 12, 1},
+       {130, 188, 4, 12, 1},
+       {132, 188, 8, 12, 1},
+       {134, 189, 0, 12, 1},
+       {136, 189, 4, 12, 1},
+       {138, 189, 8, 12, 1},
+       {140, 190, 0, 12, 1},
+       {149, 191, 6, 12, 1},
+       {151, 191, 10, 12, 1},
+       {153, 192, 2, 12, 1},
+       {155, 192, 6, 12, 1},
+       {157, 192, 10, 12, 1},
+       {159, 193, 2, 12, 1},
+       {161, 193, 6, 12, 1},
+       {165, 194, 2, 12, 1},
+       {184, 164, 0, 12, 1},
+       {188, 164, 4, 12, 1},
+       {192, 165, 8, 12, 1},
+       {196, 166, 0, 12, 1},
+};
+
+static const struct rf_channel rf_vals_5592_xtal40[] = {
+       /* Channel, N, K, mod, R */
+       {1, 241, 2, 10, 3},
+       {2, 241, 7, 10, 3},
+       {3, 242, 2, 10, 3},
+       {4, 242, 7, 10, 3},
+       {5, 243, 2, 10, 3},
+       {6, 243, 7, 10, 3},
+       {7, 244, 2, 10, 3},
+       {8, 244, 7, 10, 3},
+       {9, 245, 2, 10, 3},
+       {10, 245, 7, 10, 3},
+       {11, 246, 2, 10, 3},
+       {12, 246, 7, 10, 3},
+       {13, 247, 2, 10, 3},
+       {14, 248, 4, 10, 3},
+       {36, 86, 4, 12, 1},
+       {38, 86, 6, 12, 1},
+       {40, 86, 8, 12, 1},
+       {42, 86, 10, 12, 1},
+       {44, 87, 0, 12, 1},
+       {46, 87, 2, 12, 1},
+       {48, 87, 4, 12, 1},
+       {50, 87, 6, 12, 1},
+       {52, 87, 8, 12, 1},
+       {54, 87, 10, 12, 1},
+       {56, 88, 0, 12, 1},
+       {58, 88, 2, 12, 1},
+       {60, 88, 4, 12, 1},
+       {62, 88, 6, 12, 1},
+       {64, 88, 8, 12, 1},
+       {100, 91, 8, 12, 1},
+       {102, 91, 10, 12, 1},
+       {104, 92, 0, 12, 1},
+       {106, 92, 2, 12, 1},
+       {108, 92, 4, 12, 1},
+       {110, 92, 6, 12, 1},
+       {112, 92, 8, 12, 1},
+       {114, 92, 10, 12, 1},
+       {116, 93, 0, 12, 1},
+       {118, 93, 2, 12, 1},
+       {120, 93, 4, 12, 1},
+       {122, 93, 6, 12, 1},
+       {124, 93, 8, 12, 1},
+       {126, 93, 10, 12, 1},
+       {128, 94, 0, 12, 1},
+       {130, 94, 2, 12, 1},
+       {132, 94, 4, 12, 1},
+       {134, 94, 6, 12, 1},
+       {136, 94, 8, 12, 1},
+       {138, 94, 10, 12, 1},
+       {140, 95, 0, 12, 1},
+       {149, 95, 9, 12, 1},
+       {151, 95, 11, 12, 1},
+       {153, 96, 1, 12, 1},
+       {155, 96, 3, 12, 1},
+       {157, 96, 5, 12, 1},
+       {159, 96, 7, 12, 1},
+       {161, 96, 9, 12, 1},
+       {165, 97, 1, 12, 1},
+       {184, 82, 0, 12, 1},
+       {188, 82, 4, 12, 1},
+       {192, 82, 8, 12, 1},
+       {196, 83, 0, 12, 1},
+};
+
 static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 {
        struct hw_mode_spec *spec = &rt2x00dev->spec;
@@ -5130,6 +5847,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        char *default_power2;
        unsigned int i;
        u16 eeprom;
+       u32 reg;
 
        /*
         * Disable powersaving as default on PCI devices.
@@ -5211,8 +5929,22 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals_3x);
                spec->channels = rf_vals_3x;
+       } else if (rt2x00_rf(rt2x00dev, RF5592)) {
+               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+
+               rt2800_register_read(rt2x00dev, MAC_DEBUG_INDEX, &reg);
+               if (rt2x00_get_field32(reg, MAC_DEBUG_INDEX_XTAL)) {
+                       spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal40);
+                       spec->channels = rf_vals_5592_xtal40;
+               } else {
+                       spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal20);
+                       spec->channels = rf_vals_5592_xtal20;
+               }
        }
 
+       if (WARN_ON_ONCE(!spec->channels))
+               return -ENODEV;
+
        /*
         * Initialize HT information.
         */
@@ -5300,11 +6032,56 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        return 0;
 }
 
+static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev)
+{
+       u32 reg;
+       u32 rt;
+       u32 rev;
+
+       if (rt2x00_rt(rt2x00dev, RT3290))
+               rt2800_register_read(rt2x00dev, MAC_CSR0_3290, &reg);
+       else
+               rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
+
+       rt = rt2x00_get_field32(reg, MAC_CSR0_CHIPSET);
+       rev = rt2x00_get_field32(reg, MAC_CSR0_REVISION);
+
+       switch (rt) {
+       case RT2860:
+       case RT2872:
+       case RT2883:
+       case RT3070:
+       case RT3071:
+       case RT3090:
+       case RT3290:
+       case RT3352:
+       case RT3390:
+       case RT3572:
+       case RT5390:
+       case RT5392:
+       case RT5592:
+               break;
+       default:
+               ERROR(rt2x00dev,
+                     "Invalid RT chipset 0x%04x, rev %04x detected.\n",
+                     rt, rev);
+               return -ENODEV;
+       }
+
+       rt2x00_set_rt(rt2x00dev, rt, rev);
+
+       return 0;
+}
+
 int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
        u32 reg;
 
+       retval = rt2800_probe_rt(rt2x00dev);
+       if (retval)
+               return retval;
+
        /*
         * Allocate eeprom data.
         */
index ba5a05625aaa70b5dcba84081a4bbea5a5970a9d..565a80d0e56412270631e50381607c98f56c2e03 100644 (file)
@@ -730,6 +730,11 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
         * Process the RXWI structure that is at the start of the buffer.
         */
        rt2800_process_rxwi(entry, rxdesc);
+
+       /*
+        * Remove RXWI descriptor from start of buffer.
+        */
+       skb_pull(entry->skb, RXWI_DESC_SIZE);
 }
 
 /*
@@ -743,10 +748,90 @@ static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
        rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
 }
 
+static bool rt2800pci_txdone_entry_check(struct queue_entry *entry, u32 status)
+{
+       __le32 *txwi;
+       u32 word;
+       int wcid, tx_wcid;
+
+       wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID);
+
+       txwi = rt2800_drv_get_txwi(entry);
+       rt2x00_desc_read(txwi, 1, &word);
+       tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
+
+       return (tx_wcid == wcid);
+}
+
+static bool rt2800pci_txdone_find_entry(struct queue_entry *entry, void *data)
+{
+       u32 status = *(u32 *)data;
+
+       /*
+        * rt2800pci hardware might reorder frames when exchanging traffic
+        * with multiple BA enabled STAs.
+        *
+        * For example, a tx queue
+        *    [ STA1 | STA2 | STA1 | STA2 ]
+        * can result in tx status reports
+        *    [ STA1 | STA1 | STA2 | STA2 ]
+        * when the hw decides to aggregate the frames for STA1 into one AMPDU.
+        *
+        * To mitigate this effect, associate the tx status to the first frame
+        * in the tx queue with a matching wcid.
+        */
+       if (rt2800pci_txdone_entry_check(entry, status) &&
+           !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               /*
+                * Got a matching frame, associate the tx status with
+                * the frame
+                */
+               entry->status = status;
+               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
+               return true;
+       }
+
+       /* Check the next frame */
+       return false;
+}
+
+static bool rt2800pci_txdone_match_first(struct queue_entry *entry, void *data)
+{
+       u32 status = *(u32 *)data;
+
+       /*
+        * Find the first frame without tx status and assign this status to it
+        * regardless if it matches or not.
+        */
+       if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               /*
+                * Got a matching frame, associate the tx status with
+                * the frame
+                */
+               entry->status = status;
+               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
+               return true;
+       }
+
+       /* Check the next frame */
+       return false;
+}
+static bool rt2800pci_txdone_release_entries(struct queue_entry *entry,
+                                            void *data)
+{
+       if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               rt2800_txdone_entry(entry, entry->status,
+                                   rt2800pci_get_txwi(entry));
+               return false;
+       }
+
+       /* No more frames to release */
+       return true;
+}
+
 static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
 {
        struct data_queue *queue;
-       struct queue_entry *entry;
        u32 status;
        u8 qid;
        int max_tx_done = 16;
@@ -784,8 +869,33 @@ static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
                        break;
                }
 
-               entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
-               rt2800_txdone_entry(entry, status, rt2800pci_get_txwi(entry));
+               /*
+                * Let's associate this tx status with the first
+                * matching frame.
+                */
+               if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                               Q_INDEX, &status,
+                                               rt2800pci_txdone_find_entry)) {
+                       /*
+                        * We cannot match the tx status to any frame, so just
+                        * use the first one.
+                        */
+                       if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                                       Q_INDEX, &status,
+                                                       rt2800pci_txdone_match_first)) {
+                               WARNING(rt2x00dev, "No frame found for TX "
+                                                  "status on queue %u, dropping\n",
+                                                  qid);
+                               break;
+                       }
+               }
+
+               /*
+                * Release all frames with a valid tx status.
+                */
+               rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                          Q_INDEX, NULL,
+                                          rt2800pci_txdone_release_entries);
 
                if (--max_tx_done == 0)
                        break;
index 098613ed93fbda017b22a60b968a57e2a51d9999..f32282009146ec238e0e4dc4c1fef93eb5a55095 100644 (file)
@@ -485,7 +485,7 @@ static void rt2800usb_write_tx_desc(struct queue_entry *entry,
         */
        skbdesc->flags |= SKBDESC_DESC_IN_SKB;
        skbdesc->desc = txi;
-       skbdesc->desc_len = TXINFO_DESC_SIZE + TXWI_DESC_SIZE;
+       skbdesc->desc_len = entry->queue->desc_size;
 }
 
 /*
@@ -730,6 +730,11 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
         * Process the RXWI structure.
         */
        rt2800_process_rxwi(entry, rxdesc);
+
+       /*
+        * Remove RXWI descriptor from start of buffer.
+        */
+       skb_pull(entry->skb, entry->queue->desc_size - RXINFO_DESC_SIZE);
 }
 
 /*
@@ -890,6 +895,47 @@ static const struct rt2x00_ops rt2800usb_ops = {
 #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
 };
 
+static const struct data_queue_desc rt2800usb_queue_rx_5592 = {
+       .entry_num              = 128,
+       .data_size              = AGGREGATION_SIZE,
+       .desc_size              = RXINFO_DESC_SIZE + RXWI_DESC_SIZE_5592,
+       .priv_size              = sizeof(struct queue_entry_priv_usb),
+};
+
+static const struct data_queue_desc rt2800usb_queue_tx_5592 = {
+       .entry_num              = 16,
+       .data_size              = AGGREGATION_SIZE,
+       .desc_size              = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592,
+       .priv_size              = sizeof(struct queue_entry_priv_usb),
+};
+
+static const struct data_queue_desc rt2800usb_queue_bcn_5592 = {
+       .entry_num              = 8,
+       .data_size              = MGMT_FRAME_SIZE,
+       .desc_size              = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592,
+       .priv_size              = sizeof(struct queue_entry_priv_usb),
+};
+
+
+static const struct rt2x00_ops rt2800usb_ops_5592 = {
+       .name                   = KBUILD_MODNAME,
+       .drv_data_size          = sizeof(struct rt2800_drv_data),
+       .max_ap_intf            = 8,
+       .eeprom_size            = EEPROM_SIZE,
+       .rf_size                = RF_SIZE,
+       .tx_queues              = NUM_TX_QUEUES,
+       .extra_tx_headroom      = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592,
+       .rx                     = &rt2800usb_queue_rx_5592,
+       .tx                     = &rt2800usb_queue_tx_5592,
+       .bcn                    = &rt2800usb_queue_bcn_5592,
+       .lib                    = &rt2800usb_rt2x00_ops,
+       .drv                    = &rt2800usb_rt2800_ops,
+       .hw                     = &rt2800usb_mac80211_ops,
+#ifdef CONFIG_RT2X00_LIB_DEBUGFS
+       .debugfs                = &rt2800_rt2x00debug,
+#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
+};
+
 /*
  * rt2800usb module information.
  */
@@ -1200,6 +1246,18 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x148f, 0x5370) },
        { USB_DEVICE(0x148f, 0x5372) },
 #endif
+#ifdef CONFIG_RT2800USB_RT55XX
+       /* Arcadyan */
+       { USB_DEVICE(0x043e, 0x7a32), .driver_info = 5592 },
+       /* AVM GmbH */
+       { USB_DEVICE(0x057c, 0x8501), .driver_info = 5592 },
+       /* D-Link DWA-160-B2 */
+       { USB_DEVICE(0x2001, 0x3c1a), .driver_info = 5592 },
+       /* Proware */
+       { USB_DEVICE(0x043e, 0x7a13), .driver_info = 5592 },
+       /* Ralink */
+       { USB_DEVICE(0x148f, 0x5572), .driver_info = 5592 },
+#endif
 #ifdef CONFIG_RT2800USB_UNKNOWN
        /*
         * Unclear what kind of devices these are (they aren't supported by the
@@ -1303,6 +1361,9 @@ MODULE_LICENSE("GPL");
 static int rt2800usb_probe(struct usb_interface *usb_intf,
                           const struct usb_device_id *id)
 {
+       if (id->driver_info == 5592)
+               return rt2x00usb_probe(usb_intf, &rt2800usb_ops_5592);
+
        return rt2x00usb_probe(usb_intf, &rt2800usb_ops);
 }
 
index 086abb403a4f2085463da5a571505dbc040389cc..0d02d16ca1662e96d6b57fb75a738888ee4b209f 100644 (file)
@@ -193,6 +193,7 @@ struct rt2x00_chip {
 #define RT3883         0x3883  /* WSOC */
 #define RT5390         0x5390  /* 2.4GHz */
 #define RT5392         0x5392  /* 2.4GHz */
+#define RT5592         0x5592
 
        u16 rf;
        u16 rev;
@@ -1064,8 +1065,7 @@ static inline void rt2x00_rf_write(struct rt2x00_dev *rt2x00dev,
 }
 
 /*
- *  Generic EEPROM access.
- * The EEPROM is being accessed by word index.
+ * Generic EEPROM access. The EEPROM is being accessed by word or byte index.
  */
 static inline void *rt2x00_eeprom_addr(struct rt2x00_dev *rt2x00dev,
                                       const unsigned int word)
@@ -1085,6 +1085,12 @@ static inline void rt2x00_eeprom_write(struct rt2x00_dev *rt2x00dev,
        rt2x00dev->eeprom[word] = cpu_to_le16(data);
 }
 
+static inline u8 rt2x00_eeprom_byte(struct rt2x00_dev *rt2x00dev,
+                                   const unsigned int byte)
+{
+       return *(((u8 *)rt2x00dev->eeprom) + byte);
+}
+
 /*
  * Chipset handlers
  */
@@ -1100,6 +1106,23 @@ static inline void rt2x00_set_chip(struct rt2x00_dev *rt2x00dev,
             rt2x00dev->chip.rt, rt2x00dev->chip.rf, rt2x00dev->chip.rev);
 }
 
+static inline void rt2x00_set_rt(struct rt2x00_dev *rt2x00dev,
+                                const u16 rt, const u16 rev)
+{
+       rt2x00dev->chip.rt = rt;
+       rt2x00dev->chip.rev = rev;
+
+       INFO(rt2x00dev, "RT chipset %04x, rev %04x detected\n",
+            rt2x00dev->chip.rt, rt2x00dev->chip.rev);
+}
+
+static inline void rt2x00_set_rf(struct rt2x00_dev *rt2x00dev, const u16 rf)
+{
+       rt2x00dev->chip.rf = rf;
+
+       INFO(rt2x00dev, "RF chipset %04x detected\n", rt2x00dev->chip.rf);
+}
+
 static inline bool rt2x00_rt(struct rt2x00_dev *rt2x00dev, const u16 rt)
 {
        return (rt2x00dev->chip.rt == rt);
@@ -1360,7 +1383,7 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw,
                      struct ieee80211_vif *vif, u16 queue,
                      const struct ieee80211_tx_queue_params *params);
 void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw);
-void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop);
+void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
 int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
 int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
 void rt2x00mac_get_ringparam(struct ieee80211_hw *hw,
index 20c6eccce5aa32004f5b8b5f2aacf5703903fc6c..9161c02d8ff9719f9a6702efec55fdc86b6401f2 100644 (file)
@@ -748,7 +748,7 @@ void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll);
 
-void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop)
+void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct rt2x00_dev *rt2x00dev = hw->priv;
        struct data_queue *queue;
index 4d91795dc6a22afd411f809e35f218cef8893d01..952a0490eb1706573061d4ac056a12f6a17032b7 100644 (file)
@@ -832,7 +832,9 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
 bool rt2x00queue_for_each_entry(struct data_queue *queue,
                                enum queue_index start,
                                enum queue_index end,
-                               bool (*fn)(struct queue_entry *entry))
+                               void *data,
+                               bool (*fn)(struct queue_entry *entry,
+                                          void *data))
 {
        unsigned long irqflags;
        unsigned int index_start;
@@ -863,17 +865,17 @@ bool rt2x00queue_for_each_entry(struct data_queue *queue,
         */
        if (index_start < index_end) {
                for (i = index_start; i < index_end; i++) {
-                       if (fn(&queue->entries[i]))
+                       if (fn(&queue->entries[i], data))
                                return true;
                }
        } else {
                for (i = index_start; i < queue->limit; i++) {
-                       if (fn(&queue->entries[i]))
+                       if (fn(&queue->entries[i], data))
                                return true;
                }
 
                for (i = 0; i < index_end; i++) {
-                       if (fn(&queue->entries[i]))
+                       if (fn(&queue->entries[i], data))
                                return true;
                }
        }
index 9b8c10a86dee9731b5941842d941be21ee0cae24..3d0137193da00631623d073f19003b985d4505e9 100644 (file)
@@ -359,6 +359,7 @@ enum queue_entry_flags {
        ENTRY_DATA_PENDING,
        ENTRY_DATA_IO_FAILED,
        ENTRY_DATA_STATUS_PENDING,
+       ENTRY_DATA_STATUS_SET,
 };
 
 /**
@@ -372,6 +373,7 @@ enum queue_entry_flags {
  * @entry_idx: The entry index number.
  * @priv_data: Private data belonging to this queue entry. The pointer
  *     points to data specific to a particular driver and queue type.
+ * @status: Device specific status
  */
 struct queue_entry {
        unsigned long flags;
@@ -383,6 +385,8 @@ struct queue_entry {
 
        unsigned int entry_idx;
 
+       u32 status;
+
        void *priv_data;
 };
 
@@ -584,6 +588,7 @@ struct data_queue_desc {
  * @queue: Pointer to @data_queue
  * @start: &enum queue_index Pointer to start index
  * @end: &enum queue_index Pointer to end index
+ * @data: Data to pass to the callback function
  * @fn: The function to call for each &struct queue_entry
  *
  * This will walk through all entries in the queue, in chronological
@@ -596,7 +601,9 @@ struct data_queue_desc {
 bool rt2x00queue_for_each_entry(struct data_queue *queue,
                                enum queue_index start,
                                enum queue_index end,
-                               bool (*fn)(struct queue_entry *entry));
+                               void *data,
+                               bool (*fn)(struct queue_entry *entry,
+                                          void *data));
 
 /**
  * rt2x00queue_empty - Check if the queue is empty.
index 40ea80725a96bde2e3faa5c393a6793cb9c726c2..5e50d4ff9d210b4d22c59d4c624b0a473badee89 100644 (file)
@@ -285,7 +285,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
                queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
 }
 
-static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry)
+static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void *data)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
@@ -390,7 +390,7 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
        queue_work(rt2x00dev->workqueue, &rt2x00dev->rxdone_work);
 }
 
-static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry)
+static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void *data)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
@@ -427,12 +427,18 @@ void rt2x00usb_kick_queue(struct data_queue *queue)
        case QID_AC_BE:
        case QID_AC_BK:
                if (!rt2x00queue_empty(queue))
-                       rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX,
+                       rt2x00queue_for_each_entry(queue,
+                                                  Q_INDEX_DONE,
+                                                  Q_INDEX,
+                                                  NULL,
                                                   rt2x00usb_kick_tx_entry);
                break;
        case QID_RX:
                if (!rt2x00queue_full(queue))
-                       rt2x00queue_for_each_entry(queue, Q_INDEX, Q_INDEX_DONE,
+                       rt2x00queue_for_each_entry(queue,
+                                                  Q_INDEX,
+                                                  Q_INDEX_DONE,
+                                                  NULL,
                                                   rt2x00usb_kick_rx_entry);
                break;
        default:
@@ -441,7 +447,7 @@ void rt2x00usb_kick_queue(struct data_queue *queue)
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue);
 
-static bool rt2x00usb_flush_entry(struct queue_entry *entry)
+static bool rt2x00usb_flush_entry(struct queue_entry *entry, void *data)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct queue_entry_priv_usb *entry_priv = entry->priv_data;
@@ -468,7 +474,7 @@ void rt2x00usb_flush_queue(struct data_queue *queue, bool drop)
        unsigned int i;
 
        if (drop)
-               rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX,
+               rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, NULL,
                                           rt2x00usb_flush_entry);
 
        /*
@@ -559,7 +565,7 @@ void rt2x00usb_clear_entry(struct queue_entry *entry)
        entry->flags = 0;
 
        if (entry->queue->qid == QID_RX)
-               rt2x00usb_kick_rx_entry(entry);
+               rt2x00usb_kick_rx_entry(entry, NULL);
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry);
 
index b6aa0c40658f67fccaf5879062d02a9b9377fe88..7253de3d8c6671c78e732b001e7270731465d039 100644 (file)
@@ -55,6 +55,15 @@ config RTL8723AE
 
        If you choose to build it as a module, it will be called rtl8723ae
 
+config RTL8188EE
+       tristate "Realtek RTL8188EE Wireless Network Adapter"
+       depends on RTLWIFI && PCI
+       ---help---
+       This is the driver for Realtek RTL8188EE 802.11n PCIe
+       wireless network adapters.
+
+       If you choose to build it as a module, it will be called rtl8188ee
+
 config RTL8192CU
        tristate "Realtek RTL8192CU/RTL8188CU USB Wireless Network Adapter"
        depends on RTLWIFI && USB
index 3b1cbac741e3f4b91c41dd5c0360cb5fe521b88a..ff02b874f8d87dfd15ff0205a6036e65c0b9ad5c 100644 (file)
@@ -26,5 +26,6 @@ obj-$(CONFIG_RTL8192CU)               += rtl8192cu/
 obj-$(CONFIG_RTL8192SE)                += rtl8192se/
 obj-$(CONFIG_RTL8192DE)                += rtl8192de/
 obj-$(CONFIG_RTL8723AE)                += rtl8723ae/
+obj-$(CONFIG_RTL8188EE)                += rtl8188ee/
 
 ccflags-y += -D__CHECK_ENDIAN__
index 99c5cea3fe21e50cf575b822073266a827814558..cac1fa912e8c70049f48fb0f3d5ab76a001108f3 100644 (file)
@@ -54,7 +54,8 @@
  *5) frame process functions
  *6) IOT functions
  *7) sysfs functions
- *8) ...
+ *8) vif functions
+ *9) ...
  */
 
 /*********************************************************
@@ -198,34 +199,46 @@ static void _rtl_init_hw_ht_capab(struct ieee80211_hw *hw,
 
        ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
 
-       /*
-        *hw->wiphy->bands[IEEE80211_BAND_2GHZ]
+       /*hw->wiphy->bands[IEEE80211_BAND_2GHZ]
         *base on ant_num
         *rx_mask: RX mask
-        *if rx_ant =1 rx_mask[0]=0xff;==>MCS0-MCS7
-        *if rx_ant =2 rx_mask[1]=0xff;==>MCS8-MCS15
-        *if rx_ant >=3 rx_mask[2]=0xff;
-        *if BW_40 rx_mask[4]=0x01;
+        *if rx_ant = 1 rx_mask[0]= 0xff;==>MCS0-MCS7
+        *if rx_ant = 2 rx_mask[1]= 0xff;==>MCS8-MCS15
+        *if rx_ant >= 3 rx_mask[2]= 0xff;
+        *if BW_40 rx_mask[4]= 0x01;
         *highest supported RX rate
         */
-       if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_2T2R) {
+       if (rtlpriv->dm.supp_phymode_switch) {
 
-               RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "1T2R or 2T2R\n");
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG,
+                        "Support phy mode switch\n");
 
                ht_cap->mcs.rx_mask[0] = 0xFF;
                ht_cap->mcs.rx_mask[1] = 0xFF;
                ht_cap->mcs.rx_mask[4] = 0x01;
 
                ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS15);
-       } else if (get_rf_type(rtlphy) == RF_1T1R) {
-
-               RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "1T1R\n");
-
-               ht_cap->mcs.rx_mask[0] = 0xFF;
-               ht_cap->mcs.rx_mask[1] = 0x00;
-               ht_cap->mcs.rx_mask[4] = 0x01;
-
-               ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS7);
+       } else {
+               if (get_rf_type(rtlphy) == RF_1T2R ||
+                   get_rf_type(rtlphy) == RF_2T2R) {
+                       RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+                                "1T2R or 2T2R\n");
+                       ht_cap->mcs.rx_mask[0] = 0xFF;
+                       ht_cap->mcs.rx_mask[1] = 0xFF;
+                       ht_cap->mcs.rx_mask[4] = 0x01;
+
+                       ht_cap->mcs.rx_highest =
+                                cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS15);
+               } else if (get_rf_type(rtlphy) == RF_1T1R) {
+                       RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "1T1R\n");
+
+                       ht_cap->mcs.rx_mask[0] = 0xFF;
+                       ht_cap->mcs.rx_mask[1] = 0x00;
+                       ht_cap->mcs.rx_mask[4] = 0x01;
+
+                       ht_cap->mcs.rx_highest =
+                                cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS7);
+               }
        }
 }
 
@@ -311,6 +324,8 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw)
            IEEE80211_HW_AMPDU_AGGREGATION |
            IEEE80211_HW_CONNECTION_MONITOR |
            /* IEEE80211_HW_SUPPORTS_CQM_RSSI | */
+           IEEE80211_HW_CONNECTION_MONITOR |
+           IEEE80211_HW_MFP_CAPABLE |
            IEEE80211_HW_REPORTS_TX_ACK_STATUS | 0;
 
        /* swlps or hwlps has been set in diff chip in init_sw_vars */
@@ -323,8 +338,12 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw)
        hw->wiphy->interface_modes =
            BIT(NL80211_IFTYPE_AP) |
            BIT(NL80211_IFTYPE_STATION) |
-           BIT(NL80211_IFTYPE_ADHOC);
+           BIT(NL80211_IFTYPE_ADHOC) |
+           BIT(NL80211_IFTYPE_MESH_POINT) |
+           BIT(NL80211_IFTYPE_P2P_CLIENT) |
+           BIT(NL80211_IFTYPE_P2P_GO);
 
+       hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        hw->wiphy->rts_threshold = 2347;
 
        hw->queues = AC_MAX;
@@ -354,9 +373,10 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
        /* <1> timer */
-       init_timer(&rtlpriv->works.watchdog_timer);
        setup_timer(&rtlpriv->works.watchdog_timer,
                    rtl_watch_dog_timer_callback, (unsigned long)hw);
+       setup_timer(&rtlpriv->works.dualmac_easyconcurrent_retrytimer,
+                   rtl_easy_concurrent_retrytimer_callback, (unsigned long)hw);
 
        /* <2> work queue */
        rtlpriv->works.hw = hw;
@@ -369,6 +389,8 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw)
                          (void *)rtl_swlps_wq_callback);
        INIT_DELAYED_WORK(&rtlpriv->works.ps_rfon_wq,
                          (void *)rtl_swlps_rfon_wq_callback);
+       INIT_DELAYED_WORK(&rtlpriv->works.fwevt_wq,
+                         (void *)rtl_fwevt_wq_callback);
 
 }
 
@@ -382,6 +404,7 @@ void rtl_deinit_deferred_work(struct ieee80211_hw *hw)
        cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq);
        cancel_delayed_work(&rtlpriv->works.ps_work);
        cancel_delayed_work(&rtlpriv->works.ps_rfon_wq);
+       cancel_delayed_work(&rtlpriv->works.fwevt_wq);
 }
 
 void rtl_init_rfkill(struct ieee80211_hw *hw)
@@ -436,12 +459,6 @@ int rtl_init_core(struct ieee80211_hw *hw)
        if (rtl_regd_init(hw, rtl_reg_notifier)) {
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "REGD init failed\n");
                return 1;
-       } else {
-               /* CRDA regd hint must after init CRDA */
-               if (regulatory_hint(hw->wiphy, rtlpriv->regd.alpha2)) {
-                       RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
-                                "regulatory_hint fail\n");
-               }
        }
 
        /* <4> locks */
@@ -449,15 +466,25 @@ int rtl_init_core(struct ieee80211_hw *hw)
        mutex_init(&rtlpriv->locks.ps_mutex);
        spin_lock_init(&rtlpriv->locks.ips_lock);
        spin_lock_init(&rtlpriv->locks.irq_th_lock);
+       spin_lock_init(&rtlpriv->locks.irq_pci_lock);
+       spin_lock_init(&rtlpriv->locks.tx_lock);
        spin_lock_init(&rtlpriv->locks.h2c_lock);
        spin_lock_init(&rtlpriv->locks.rf_ps_lock);
        spin_lock_init(&rtlpriv->locks.rf_lock);
        spin_lock_init(&rtlpriv->locks.waitq_lock);
+       spin_lock_init(&rtlpriv->locks.entry_list_lock);
+       spin_lock_init(&rtlpriv->locks.fw_ps_lock);
        spin_lock_init(&rtlpriv->locks.cck_and_rw_pagea_lock);
+       spin_lock_init(&rtlpriv->locks.check_sendpkt_lock);
+       spin_lock_init(&rtlpriv->locks.fw_ps_lock);
+       spin_lock_init(&rtlpriv->locks.lps_lock);
+
+       /* <5> init list */
+       INIT_LIST_HEAD(&rtlpriv->entry_list);
 
        rtlmac->link_state = MAC80211_NOLINK;
 
-       /* <5> init deferred work */
+       /* <6> init deferred work */
        _rtl_init_deferred_work(hw);
 
        return 0;
@@ -523,7 +550,8 @@ static void _rtl_query_shortgi(struct ieee80211_hw *hw,
        if (mac->opmode == NL80211_IFTYPE_STATION)
                bw_40 = mac->bw_40;
        else if (mac->opmode == NL80211_IFTYPE_AP ||
-                mac->opmode == NL80211_IFTYPE_ADHOC)
+                mac->opmode == NL80211_IFTYPE_ADHOC ||
+                mac->opmode == NL80211_IFTYPE_MESH_POINT)
                bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40;
 
        if (bw_40 && sgi_40)
@@ -578,23 +606,26 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw,
        if (!tcb_desc->disable_ratefallback || !tcb_desc->use_driver_rate) {
                if (mac->opmode == NL80211_IFTYPE_STATION) {
                        tcb_desc->ratr_index = 0;
-               } else if (mac->opmode == NL80211_IFTYPE_ADHOC) {
+               } else if (mac->opmode == NL80211_IFTYPE_ADHOC ||
+                          mac->opmode == NL80211_IFTYPE_MESH_POINT) {
                        if (tcb_desc->multicast || tcb_desc->broadcast) {
                                tcb_desc->hw_rate =
                                    rtlpriv->cfg->maps[RTL_RC_CCK_RATE2M];
                                tcb_desc->use_driver_rate = 1;
+                               tcb_desc->ratr_index = RATR_INX_WIRELESS_MC;
                        } else {
-                               /* TODO */
+                               tcb_desc->ratr_index = ratr_index;
                        }
-                       tcb_desc->ratr_index = ratr_index;
                } else if (mac->opmode == NL80211_IFTYPE_AP) {
                        tcb_desc->ratr_index = ratr_index;
                }
        }
 
        if (rtlpriv->dm.useramask) {
-               /* TODO we will differentiate adhoc and station futrue  */
-               if (mac->opmode == NL80211_IFTYPE_STATION) {
+               tcb_desc->ratr_index = ratr_index;
+               /* TODO we will differentiate adhoc and station future  */
+               if (mac->opmode == NL80211_IFTYPE_STATION ||
+                   mac->opmode == NL80211_IFTYPE_MESH_POINT) {
                        tcb_desc->mac_id = 0;
 
                        if (mac->mode == WIRELESS_MODE_N_24G)
@@ -608,7 +639,7 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw,
                        else if (mac->mode & WIRELESS_MODE_A)
                                tcb_desc->ratr_index = RATR_INX_WIRELESS_G;
                } else if (mac->opmode == NL80211_IFTYPE_AP ||
-                       mac->opmode == NL80211_IFTYPE_ADHOC) {
+                          mac->opmode == NL80211_IFTYPE_ADHOC) {
                        if (NULL != sta) {
                                if (sta->aid > 0)
                                        tcb_desc->mac_id = sta->aid + 1;
@@ -619,7 +650,6 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw,
                        }
                }
        }
-
 }
 
 static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw,
@@ -633,7 +663,8 @@ static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw,
        if (!sta)
                return;
        if (mac->opmode == NL80211_IFTYPE_AP ||
-           mac->opmode == NL80211_IFTYPE_ADHOC) {
+           mac->opmode == NL80211_IFTYPE_ADHOC ||
+           mac->opmode == NL80211_IFTYPE_MESH_POINT) {
                if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
                        return;
        } else if (mac->opmode == NL80211_IFTYPE_STATION) {
@@ -834,8 +865,8 @@ bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb)
        if (rtlpriv->dm.supp_phymode_switch &&
            mac->link_state < MAC80211_LINKED &&
            (ieee80211_is_auth(fc) || ieee80211_is_probe_req(fc))) {
-               if (rtlpriv->cfg->ops->check_switch_to_dmdp)
-                       rtlpriv->cfg->ops->check_switch_to_dmdp(hw);
+               if (rtlpriv->cfg->ops->chk_switch_dmdp)
+                       rtlpriv->cfg->ops->chk_switch_dmdp(hw);
        }
        if (ieee80211_is_auth(fc)) {
                RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "MAC80211_LINKING\n");
@@ -924,6 +955,56 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(rtl_get_tcb_desc);
 
+static bool addbareq_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct ieee80211_sta *sta = NULL;
+       struct ieee80211_hdr *hdr = rtl_get_hdr(skb);
+       struct rtl_sta_info *sta_entry = NULL;
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
+       u16 capab = 0, tid = 0;
+       struct rtl_tid_data *tid_data;
+       struct sk_buff *skb_delba = NULL;
+       struct ieee80211_rx_status rx_status = { 0 };
+
+       rcu_read_lock();
+       sta = rtl_find_sta(hw, hdr->addr3);
+       if (sta == NULL) {
+               RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_EMERG,
+                        "sta is NULL\n");
+               rcu_read_unlock();
+               return true;
+       }
+
+       sta_entry = (struct rtl_sta_info *)sta->drv_priv;
+       if (!sta_entry) {
+               rcu_read_unlock();
+               return true;
+       }
+       capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+       tid_data = &sta_entry->tids[tid];
+       if (tid_data->agg.rx_agg_state == RTL_RX_AGG_START) {
+               skb_delba = rtl_make_del_ba(hw, hdr->addr2, hdr->addr3, tid);
+               if (skb_delba) {
+                       rx_status.freq = hw->conf.channel->center_freq;
+                       rx_status.band = hw->conf.channel->band;
+                       rx_status.flag |= RX_FLAG_DECRYPTED;
+                       rx_status.flag |= RX_FLAG_MACTIME_END;
+                       rx_status.rate_idx = 0;
+                       rx_status.signal = 50 + 10;
+                       memcpy(IEEE80211_SKB_RXCB(skb_delba), &rx_status,
+                              sizeof(rx_status));
+                       RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG,
+                                     "fake del\n", skb_delba->data,
+                                     skb_delba->len);
+                       ieee80211_rx_irqsafe(hw, skb_delba);
+               }
+       }
+       rcu_read_unlock();
+       return false;
+}
+
 bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
 {
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
@@ -948,6 +1029,11 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
                        RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
                                 "%s ACT_ADDBAREQ From :%pM\n",
                                 is_tx ? "Tx" : "Rx", hdr->addr2);
+                       RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, "req\n",
+                                     skb->data, skb->len);
+                       if (!is_tx)
+                               if (addbareq_rx(hw, skb))
+                                       return true;
                        break;
                case ACT_ADDBARSP:
                        RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
@@ -1003,8 +1089,9 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
                                         is_tx ? "Tx" : "Rx");
 
                                if (is_tx) {
+                                       rtlpriv->enter_ps = false;
                                        schedule_work(&rtlpriv->
-                                                     works.lps_leave_work);
+                                                     works.lps_change_work);
                                        ppsc->last_delaylps_stamp_jiffies =
                                            jiffies;
                                }
@@ -1014,7 +1101,8 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
                }
        } else if (ETH_P_ARP == ether_type) {
                if (is_tx) {
-                       schedule_work(&rtlpriv->works.lps_leave_work);
+                       rtlpriv->enter_ps = false;
+                       schedule_work(&rtlpriv->works.lps_change_work);
                        ppsc->last_delaylps_stamp_jiffies = jiffies;
                }
 
@@ -1024,7 +1112,8 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
                         "802.1X %s EAPOL pkt!!\n", is_tx ? "Tx" : "Rx");
 
                if (is_tx) {
-                       schedule_work(&rtlpriv->works.lps_leave_work);
+                       rtlpriv->enter_ps = false;
+                       schedule_work(&rtlpriv->works.lps_change_work);
                        ppsc->last_delaylps_stamp_jiffies = jiffies;
                }
 
@@ -1101,6 +1190,58 @@ int rtl_tx_agg_stop(struct ieee80211_hw *hw,
        return 0;
 }
 
+int rtl_rx_agg_start(struct ieee80211_hw *hw,
+                    struct ieee80211_sta *sta, u16 tid)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_tid_data *tid_data;
+       struct rtl_sta_info *sta_entry = NULL;
+
+       if (sta == NULL)
+               return -EINVAL;
+
+       if (unlikely(tid >= MAX_TID_COUNT))
+               return -EINVAL;
+
+       sta_entry = (struct rtl_sta_info *)sta->drv_priv;
+       if (!sta_entry)
+               return -ENXIO;
+       tid_data = &sta_entry->tids[tid];
+
+       RT_TRACE(rtlpriv, COMP_RECV, DBG_DMESG,
+                "on ra = %pM tid = %d seq:%d\n", sta->addr, tid,
+                tid_data->seq_number);
+
+       tid_data->agg.rx_agg_state = RTL_RX_AGG_START;
+       return 0;
+}
+
+int rtl_rx_agg_stop(struct ieee80211_hw *hw,
+                   struct ieee80211_sta *sta, u16 tid)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_sta_info *sta_entry = NULL;
+
+       if (sta == NULL)
+               return -EINVAL;
+
+       if (!sta->addr) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "ra = NULL\n");
+               return -EINVAL;
+       }
+
+       RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG,
+                "on ra = %pM tid = %d\n", sta->addr, tid);
+
+       if (unlikely(tid >= MAX_TID_COUNT))
+               return -EINVAL;
+
+       sta_entry = (struct rtl_sta_info *)sta->drv_priv;
+       sta_entry->tids[tid].agg.rx_agg_state = RTL_RX_AGG_STOP;
+
+       return 0;
+}
+
 int rtl_tx_agg_oper(struct ieee80211_hw *hw,
                struct ieee80211_sta *sta, u16 tid)
 {
@@ -1132,6 +1273,34 @@ int rtl_tx_agg_oper(struct ieee80211_hw *hw,
  * wq & timer callback functions
  *
  *********************************************************/
+/* this function is used for roaming */
+void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+       if (rtlpriv->mac80211.opmode != NL80211_IFTYPE_STATION)
+               return;
+
+       if (rtlpriv->mac80211.link_state < MAC80211_LINKED)
+               return;
+
+       /* check if this really is a beacon */
+       if (!ieee80211_is_beacon(hdr->frame_control) &&
+           !ieee80211_is_probe_resp(hdr->frame_control))
+               return;
+
+       /* min. beacon length + FCS_LEN */
+       if (skb->len <= 40 + FCS_LEN)
+               return;
+
+       /* and only beacons from the associated BSSID, please */
+       if (compare_ether_addr(hdr->addr3, rtlpriv->mac80211.bssid))
+               return;
+
+       rtlpriv->link_info.bcn_rx_inperiod++;
+}
+
 void rtl_watchdog_wq_callback(void *data)
 {
        struct rtl_works *rtlworks = container_of_dwork_rtl(data,
@@ -1142,6 +1311,8 @@ void rtl_watchdog_wq_callback(void *data)
        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
        bool busytraffic = false;
+       bool tx_busy_traffic = false;
+       bool rx_busy_traffic = false;
        bool higher_busytraffic = false;
        bool higher_busyrxtraffic = false;
        u8 idx, tid;
@@ -1151,7 +1322,6 @@ void rtl_watchdog_wq_callback(void *data)
        u32 aver_tx_cnt_inperiod = 0;
        u32 aver_tidtx_inperiod[MAX_TID_COUNT] = {0};
        u32 tidtx_inp4eriod[MAX_TID_COUNT] = {0};
-       bool enter_ps = false;
 
        if (is_hal_stop(rtlhal))
                return;
@@ -1191,8 +1361,13 @@ void rtl_watchdog_wq_callback(void *data)
                aver_tx_cnt_inperiod = tx_cnt_inp4eriod / 4;
 
                /* (2) check traffic busy */
-               if (aver_rx_cnt_inperiod > 100 || aver_tx_cnt_inperiod > 100)
+               if (aver_rx_cnt_inperiod > 100 || aver_tx_cnt_inperiod > 100) {
                        busytraffic = true;
+                       if (aver_rx_cnt_inperiod > aver_tx_cnt_inperiod)
+                               rx_busy_traffic = true;
+                       else
+                               tx_busy_traffic = false;
+               }
 
                /* Higher Tx/Rx data. */
                if (aver_rx_cnt_inperiod > 4000 ||
@@ -1228,15 +1403,12 @@ void rtl_watchdog_wq_callback(void *data)
                if (((rtlpriv->link_info.num_rx_inperiod +
                      rtlpriv->link_info.num_tx_inperiod) > 8) ||
                    (rtlpriv->link_info.num_rx_inperiod > 2))
-                       enter_ps = false;
+                       rtlpriv->enter_ps = true;
                else
-                       enter_ps = true;
+                       rtlpriv->enter_ps = false;
 
                /* LeisurePS only work in infra mode. */
-               if (enter_ps)
-                       rtl_lps_enter(hw);
-               else
-                       rtl_lps_leave(hw);
+               schedule_work(&rtlpriv->works.lps_change_work);
        }
 
        rtlpriv->link_info.num_rx_inperiod = 0;
@@ -1246,10 +1418,37 @@ void rtl_watchdog_wq_callback(void *data)
 
        rtlpriv->link_info.busytraffic = busytraffic;
        rtlpriv->link_info.higher_busytraffic = higher_busytraffic;
+       rtlpriv->link_info.rx_busy_traffic = rx_busy_traffic;
+       rtlpriv->link_info.tx_busy_traffic = tx_busy_traffic;
        rtlpriv->link_info.higher_busyrxtraffic = higher_busyrxtraffic;
 
        /* <3> DM */
        rtlpriv->cfg->ops->dm_watchdog(hw);
+
+       /* <4> roaming */
+       if (mac->link_state == MAC80211_LINKED &&
+           mac->opmode == NL80211_IFTYPE_STATION) {
+               if ((rtlpriv->link_info.bcn_rx_inperiod +
+                    rtlpriv->link_info.num_rx_inperiod) == 0) {
+                       rtlpriv->link_info.roam_times++;
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
+                                "AP off for %d s\n",
+                                (rtlpriv->link_info.roam_times * 2));
+
+                       /* if we can't recv beacon for 6s, we should
+                        * reconnect this AP
+                        */
+                       if (rtlpriv->link_info.roam_times >= 3) {
+                               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                        "AP off, try to reconnect now\n");
+                               rtlpriv->link_info.roam_times = 0;
+                               ieee80211_connection_loss(rtlpriv->mac80211.vif);
+                       }
+               } else {
+                       rtlpriv->link_info.roam_times = 0;
+               }
+       }
+       rtlpriv->link_info.bcn_rx_inperiod = 0;
 }
 
 void rtl_watch_dog_timer_callback(unsigned long data)
@@ -1264,6 +1463,28 @@ void rtl_watch_dog_timer_callback(unsigned long data)
                  jiffies + MSECS(RTL_WATCH_DOG_TIME));
 }
 
+void rtl_fwevt_wq_callback(void *data)
+{
+       struct rtl_works *rtlworks =
+               container_of_dwork_rtl(data, struct rtl_works, fwevt_wq);
+       struct ieee80211_hw *hw = rtlworks->hw;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       rtlpriv->cfg->ops->c2h_command_handle(hw);
+}
+
+void rtl_easy_concurrent_retrytimer_callback(unsigned long data)
+{
+       struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_priv *buddy_priv = rtlpriv->buddy_priv;
+
+       if (buddy_priv == NULL)
+               return;
+
+       rtlpriv->cfg->ops->dualmac_easy_concurrent(hw);
+}
+
 /*********************************************************
  *
  * frame process functions
@@ -1334,14 +1555,16 @@ static struct sk_buff *rtl_make_smps_action(struct ieee80211_hw *hw,
 }
 
 int rtl_send_smps_action(struct ieee80211_hw *hw,
-               struct ieee80211_sta *sta, u8 *da, u8 *bssid,
+               struct ieee80211_sta *sta,
                enum ieee80211_smps_mode smps)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
        struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
-       struct sk_buff *skb = rtl_make_smps_action(hw, smps, da, bssid);
+       struct sk_buff *skb = NULL;
        struct rtl_tcb_desc tcb_desc;
+       u8 bssid[ETH_ALEN] = {0};
+
        memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc));
 
        if (rtlpriv->mac80211.act_scanning)
@@ -1356,21 +1579,67 @@ int rtl_send_smps_action(struct ieee80211_hw *hw,
        if (!test_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status))
                goto err_free;
 
+       if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_AP)
+               memcpy(bssid, rtlpriv->efuse.dev_addr, ETH_ALEN);
+       else
+               memcpy(bssid, rtlpriv->mac80211.bssid, ETH_ALEN);
+
+       skb = rtl_make_smps_action(hw, smps, sta->addr, bssid);
        /* this is a type = mgmt * stype = action frame */
        if (skb) {
                struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
                struct rtl_sta_info *sta_entry =
                        (struct rtl_sta_info *) sta->drv_priv;
                sta_entry->mimo_ps = smps;
-               rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0);
 
                info->control.rates[0].idx = 0;
                info->band = hw->conf.channel->band;
                rtlpriv->intf_ops->adapter_tx(hw, sta, skb, &tcb_desc);
        }
+       return 1;
+
 err_free:
        return 0;
 }
+EXPORT_SYMBOL(rtl_send_smps_action);
+
+/* There seem to be issues in mac80211 regarding when del ba frames can be
+ * received. As a work around, we make a fake del_ba if we receive a ba_req;
+ * however, rx_agg was opened to let mac80211 release some ba related
+ * resources. This del_ba is for tx only.
+ */
+struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw,
+                               u8 *sa, u8 *bssid, u16 tid)
+{
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *action_frame;
+       u16 params;
+
+       /* 27 = header + category + action + smps mode */
+       skb = dev_alloc_skb(34 + hw->extra_tx_headroom);
+       if (!skb)
+               return NULL;
+
+       skb_reserve(skb, hw->extra_tx_headroom);
+       action_frame = (void *)skb_put(skb, 34);
+       memset(action_frame, 0, 34);
+       memcpy(action_frame->sa, sa, ETH_ALEN);
+       memcpy(action_frame->da, rtlefuse->dev_addr, ETH_ALEN);
+       memcpy(action_frame->bssid, bssid, ETH_ALEN);
+       action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_ACTION);
+       action_frame->u.action.category = WLAN_CATEGORY_BACK;
+       action_frame->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
+       params = (u16)(1 << 11);        /* bit 11 initiator */
+       params |= (u16)(tid << 12);             /* bit 15:12 TID number */
+
+       action_frame->u.action.u.delba.params = cpu_to_le16(params);
+       action_frame->u.action.u.delba.reason_code =
+               cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT);
+
+       return skb;
+}
 
 /*********************************************************
  *
@@ -1587,11 +1856,17 @@ MODULE_AUTHOR("Larry Finger     <Larry.FInger@lwfinger.net>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core");
 
+struct rtl_global_var global_var = {};
+
 static int __init rtl_core_module_init(void)
 {
        if (rtl_rate_control_register())
                pr_err("Unable to register rtl_rc, use default RC !!\n");
 
+       /* init some global vars */
+       INIT_LIST_HEAD(&global_var.glb_priv_list);
+       spin_lock_init(&global_var.glb_list_lock);
+
        return 0;
 }
 
index 5a8c80e259f7f36070f05b096e4187064f829729..8576bc34b03289ef077db41f5700e3a2eea5bf21 100644 (file)
@@ -113,6 +113,7 @@ void rtl_init_rx_config(struct ieee80211_hw *hw);
 void rtl_init_rfkill(struct ieee80211_hw *hw);
 void rtl_deinit_rfkill(struct ieee80211_hw *hw);
 
+void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb);
 void rtl_watch_dog_timer_callback(unsigned long data);
 void rtl_deinit_deferred_work(struct ieee80211_hw *hw);
 
@@ -126,7 +127,12 @@ int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
                    u16 tid);
 int rtl_tx_agg_oper(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
                    u16 tid);
+int rtl_rx_agg_start(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+                    u16 tid);
+int rtl_rx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+                   u16 tid);
 void rtl_watchdog_wq_callback(void *data);
+void rtl_fwevt_wq_callback(void *data);
 
 void rtl_get_tcb_desc(struct ieee80211_hw *hw,
                      struct ieee80211_tx_info *info,
@@ -134,14 +140,18 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw,
                      struct sk_buff *skb, struct rtl_tcb_desc *tcb_desc);
 
 int rtl_send_smps_action(struct ieee80211_hw *hw,
-               struct ieee80211_sta *sta, u8 *da, u8 *bssid,
-               enum ieee80211_smps_mode smps);
+                        struct ieee80211_sta *sta,
+                        enum ieee80211_smps_mode smps);
 u8 *rtl_find_ie(u8 *data, unsigned int len, u8 ie);
 void rtl_recognize_peer(struct ieee80211_hw *hw, u8 *data, unsigned int len);
 u8 rtl_tid_to_ac(u8 tid);
 extern struct attribute_group rtl_attribute_group;
+void rtl_easy_concurrent_retrytimer_callback(unsigned long data);
+extern struct rtl_global_var global_var;
 int rtlwifi_rate_mapping(struct ieee80211_hw *hw,
                         bool isht, u8 desc_rate, bool first_ampdu);
 bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb);
+struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw,
+                               u8 *sa, u8 *bssid, u16 tid);
 
 #endif
index d3ce9fbef00ee0b8d3e88eefcfd7111d9d0080ae..2201b5cee08f5187b167a77c287f80e6ec0455c0 100644 (file)
@@ -104,9 +104,12 @@ static void rtl_op_stop(struct ieee80211_hw *hw)
        if (is_hal_stop(rtlhal))
                return;
 
+       /* here is must, because adhoc do stop and start,
+        * but stop with RFOFF may cause something wrong,
+        * like adhoc TP
+        */
        if (unlikely(ppsc->rfpwr_state == ERFOFF)) {
                rtl_ips_nic_on(hw);
-               mdelay(1);
        }
 
        mutex_lock(&rtlpriv->locks.conf_mutex);
@@ -167,7 +170,11 @@ static int rtl_op_add_interface(struct ieee80211_hw *hw,
        rtl_ips_nic_on(hw);
 
        mutex_lock(&rtlpriv->locks.conf_mutex);
-       switch (vif->type) {
+
+       switch (ieee80211_vif_type_p2p(vif)) {
+       case NL80211_IFTYPE_P2P_CLIENT:
+               mac->p2p = P2P_ROLE_CLIENT;
+               /*fall through*/
        case NL80211_IFTYPE_STATION:
                if (mac->beacon_enabled == 1) {
                        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
@@ -192,6 +199,9 @@ static int rtl_op_add_interface(struct ieee80211_hw *hw,
                                (u8 *) (&mac->basic_rates));
 
                break;
+       case NL80211_IFTYPE_P2P_GO:
+               mac->p2p = P2P_ROLE_GO;
+               /*fall through*/
        case NL80211_IFTYPE_AP:
                RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
                         "NL80211_IFTYPE_AP\n");
@@ -205,6 +215,19 @@ static int rtl_op_add_interface(struct ieee80211_hw *hw,
                rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE,
                                (u8 *) (&mac->basic_rates));
                break;
+       case NL80211_IFTYPE_MESH_POINT:
+               RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
+                        "NL80211_IFTYPE_MESH_POINT\n");
+
+               mac->link_state = MAC80211_LINKED;
+               rtlpriv->cfg->ops->set_bcn_reg(hw);
+               if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G)
+                       mac->basic_rates = 0xfff;
+               else
+                       mac->basic_rates = 0xff0;
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE,
+                               (u8 *)(&mac->basic_rates));
+               break;
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         "operation mode %d is not supported!\n", vif->type);
@@ -212,6 +235,13 @@ static int rtl_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
+       if (mac->p2p) {
+               RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
+                        "p2p role %x\n", vif->type);
+               mac->basic_rates = 0xff0;/*disable cck rate for p2p*/
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE,
+                               (u8 *)(&mac->basic_rates));
+       }
        mac->vif = vif;
        mac->opmode = vif->type;
        rtlpriv->cfg->ops->set_network_type(hw, vif->type);
@@ -232,9 +262,9 @@ static void rtl_op_remove_interface(struct ieee80211_hw *hw,
        mutex_lock(&rtlpriv->locks.conf_mutex);
 
        /* Free beacon resources */
-       if ((mac->opmode == NL80211_IFTYPE_AP) ||
-           (mac->opmode == NL80211_IFTYPE_ADHOC) ||
-           (mac->opmode == NL80211_IFTYPE_MESH_POINT)) {
+       if ((vif->type == NL80211_IFTYPE_AP) ||
+           (vif->type == NL80211_IFTYPE_ADHOC) ||
+           (vif->type == NL80211_IFTYPE_MESH_POINT)) {
                if (mac->beacon_enabled == 1) {
                        mac->beacon_enabled = 0;
                        rtlpriv->cfg->ops->update_interrupt_mask(hw, 0,
@@ -247,6 +277,7 @@ static void rtl_op_remove_interface(struct ieee80211_hw *hw,
         *Note: We assume NL80211_IFTYPE_UNSPECIFIED as
         *NO LINK for our hardware.
         */
+       mac->p2p = 0;
        mac->vif = NULL;
        mac->link_state = MAC80211_NOLINK;
        memset(mac->bssid, 0, 6);
@@ -256,6 +287,22 @@ static void rtl_op_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&rtlpriv->locks.conf_mutex);
 }
 
+static int rtl_op_change_interface(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     enum nl80211_iftype new_type, bool p2p)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       int ret;
+       rtl_op_remove_interface(hw, vif);
+
+       vif->type = new_type;
+       vif->p2p = p2p;
+       ret = rtl_op_add_interface(hw, vif);
+       RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
+                "p2p %x\n", p2p);
+       return ret;
+}
+
 static int rtl_op_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -264,6 +311,9 @@ static int rtl_op_config(struct ieee80211_hw *hw, u32 changed)
        struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
        struct ieee80211_conf *conf = &hw->conf;
 
+       if (mac->skip_scan)
+               return 1;
+
        mutex_lock(&rtlpriv->locks.conf_mutex);
        if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {  /*BIT(2)*/
                RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
@@ -323,6 +373,16 @@ static int rtl_op_config(struct ieee80211_hw *hw, u32 changed)
                struct ieee80211_channel *channel = hw->conf.channel;
                u8 wide_chan = (u8) channel->hw_value;
 
+               if (mac->act_scanning)
+                       mac->n_channels++;
+
+               if (rtlpriv->dm.supp_phymode_switch &&
+                   mac->link_state < MAC80211_LINKED &&
+                   !mac->act_scanning) {
+                       if (rtlpriv->cfg->ops->chk_switch_dmdp)
+                               rtlpriv->cfg->ops->chk_switch_dmdp(hw);
+               }
+
                /*
                 *because we should back channel to
                 *current_network.chan in in scanning,
@@ -373,13 +433,13 @@ static int rtl_op_config(struct ieee80211_hw *hw, u32 changed)
                if (wide_chan <= 0)
                        wide_chan = 1;
 
-               /* In scanning, before we go offchannel we may send a ps=1 null
-                * to AP, and then we may send a ps = 0 null to AP quickly, but
-                * first null may have caused AP to put lots of packet to hw tx
-                * buffer. These packets must be tx'd before we go off channel
-                * so we must delay more time to let AP flush these packets
-                * before going offchannel, or dis-association or delete BA will
-                * happen by AP
+               /* In scanning, before we go offchannel we may send a ps = 1
+                * null to AP, and then we may send a ps = 0 null to AP quickly,
+                * but first null may have caused AP to put lots of packet to
+                * hw tx buffer. These packets must be tx'd before we go off
+                * channel so we must delay more time to let AP flush these
+                * packets before going offchannel, or dis-association or
+                * delete BA will be caused by AP
                 */
                if (rtlpriv->mac80211.offchan_delay) {
                        rtlpriv->mac80211.offchan_delay = false;
@@ -441,7 +501,8 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
         * and nolink check bssid is set in set network_type */
        if ((changed_flags & FIF_BCN_PRBRESP_PROMISC) &&
                (mac->link_state >= MAC80211_LINKED)) {
-               if (mac->opmode != NL80211_IFTYPE_AP) {
+               if (mac->opmode != NL80211_IFTYPE_AP &&
+                   mac->opmode != NL80211_IFTYPE_MESH_POINT) {
                        if (*new_flags & FIF_BCN_PRBRESP_PROMISC) {
                                rtlpriv->cfg->ops->set_chk_bssid(hw, false);
                        } else {
@@ -481,32 +542,43 @@ static int rtl_op_sta_add(struct ieee80211_hw *hw,
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
        struct rtl_sta_info *sta_entry;
 
        if (sta) {
                sta_entry = (struct rtl_sta_info *) sta->drv_priv;
+               spin_lock_bh(&rtlpriv->locks.entry_list_lock);
+               list_add_tail(&sta_entry->list, &rtlpriv->entry_list);
+               spin_unlock_bh(&rtlpriv->locks.entry_list_lock);
                if (rtlhal->current_bandtype == BAND_ON_2_4G) {
                        sta_entry->wireless_mode = WIRELESS_MODE_G;
                        if (sta->supp_rates[0] <= 0xf)
                                sta_entry->wireless_mode = WIRELESS_MODE_B;
-                       if (sta->ht_cap.ht_supported)
+                       if (sta->ht_cap.ht_supported == true)
                                sta_entry->wireless_mode = WIRELESS_MODE_N_24G;
+
+                       if (vif->type == NL80211_IFTYPE_ADHOC)
+                               sta_entry->wireless_mode = WIRELESS_MODE_G;
                } else if (rtlhal->current_bandtype == BAND_ON_5G) {
                        sta_entry->wireless_mode = WIRELESS_MODE_A;
-                       if (sta->ht_cap.ht_supported)
+                       if (sta->ht_cap.ht_supported == true)
                                sta_entry->wireless_mode = WIRELESS_MODE_N_24G;
-               }
 
-               /* I found some times mac80211 give wrong supp_rates for adhoc*/
-               if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_ADHOC)
-                       sta_entry->wireless_mode = WIRELESS_MODE_G;
+                       if (vif->type == NL80211_IFTYPE_ADHOC)
+                               sta_entry->wireless_mode = WIRELESS_MODE_A;
+               }
+               /*disable cck rate for p2p*/
+               if (mac->p2p)
+                       sta->supp_rates[0] &= 0xfffffff0;
 
+               memcpy(sta_entry->mac_addr, sta->addr, ETH_ALEN);
                RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG,
                         "Add sta addr is %pM\n", sta->addr);
                rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0);
        }
        return 0;
 }
+
 static int rtl_op_sta_remove(struct ieee80211_hw *hw,
                                struct ieee80211_vif *vif,
                                struct ieee80211_sta *sta)
@@ -519,9 +591,14 @@ static int rtl_op_sta_remove(struct ieee80211_hw *hw,
                sta_entry = (struct rtl_sta_info *) sta->drv_priv;
                sta_entry->wireless_mode = 0;
                sta_entry->ratr_index = 0;
+
+               spin_lock_bh(&rtlpriv->locks.entry_list_lock);
+               list_del(&sta_entry->list);
+               spin_unlock_bh(&rtlpriv->locks.entry_list_lock);
        }
        return 0;
 }
+
 static int _rtl_get_hal_qnum(u16 queue)
 {
        int qnum;
@@ -547,8 +624,8 @@ static int _rtl_get_hal_qnum(u16 queue)
 }
 
 /*
- *for mac80211 VO=0, VI=1, BE=2, BK=3
- *for rtl819x  BE=0, BK=1, VI=2, VO=3
+ *for mac80211 VO = 0, VI = 1, BE = 2, BK = 3
+ *for rtl819x  BE = 0, BK = 1, VI = 2, VO = 3
  */
 static int rtl_op_conf_tx(struct ieee80211_hw *hw,
                   struct ieee80211_vif *vif, u16 queue,
@@ -630,6 +707,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
        /*TODO: reference to enum ieee80211_bss_change */
        if (changed & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
+                       struct ieee80211_sta *sta = NULL;
                        /* we should reset all sec info & cam
                         * before set cam after linked, we should not
                         * reset in disassoc, that will cause tkip->wep
@@ -647,23 +725,39 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
 
                        if (rtlpriv->cfg->ops->linked_set_reg)
                                rtlpriv->cfg->ops->linked_set_reg(hw);
-                       if (mac->opmode == NL80211_IFTYPE_STATION && sta)
+                       rcu_read_lock();
+                       sta = ieee80211_find_sta(vif, (u8 *)bss_conf->bssid);
+
+                       if (vif->type == NL80211_IFTYPE_STATION && sta)
                                rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0);
+                       RT_TRACE(rtlpriv, COMP_EASY_CONCURRENT, DBG_LOUD,
+                                "send PS STATIC frame\n");
+                       if (rtlpriv->dm.supp_phymode_switch) {
+                               if (sta->ht_cap.ht_supported)
+                                       rtl_send_smps_action(hw, sta,
+                                                IEEE80211_SMPS_STATIC);
+                       }
+                       rcu_read_unlock();
+
                        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG,
                                 "BSS_CHANGED_ASSOC\n");
                } else {
-                       if (mac->link_state == MAC80211_LINKED)
-                               rtl_lps_leave(hw);
+                       if (mac->link_state == MAC80211_LINKED) {
+                               rtlpriv->enter_ps = false;
+                               schedule_work(&rtlpriv->works.lps_change_work);
+                       }
 
+                       if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE)
+                               rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
                        mac->link_state = MAC80211_NOLINK;
                        memset(mac->bssid, 0, 6);
-
-                       /* reset sec info */
-                       rtl_cam_reset_sec_info(hw);
-
-                       rtl_cam_reset_all_entry(hw);
                        mac->vendor = PEER_UNKNOWN;
 
+                       if (rtlpriv->dm.supp_phymode_switch) {
+                               if (rtlpriv->cfg->ops->chk_switch_dmdp)
+                                       rtlpriv->cfg->ops->chk_switch_dmdp(hw);
+                       }
+
                        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG,
                                 "BSS_CHANGED_UN_ASSOC\n");
                }
@@ -778,7 +872,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
                }
 
                if (changed & BSS_CHANGED_BASIC_RATES) {
-                       /* for 5G must << RATE_6M_INDEX=4,
+                       /* for 5G must << RATE_6M_INDEX = 4,
                         * because 5G have no cck rate*/
                        if (rtlhal->current_bandtype == BAND_ON_5G)
                                basic_rates = sta->supp_rates[1] << 4;
@@ -815,6 +909,9 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
                                ppsc->report_linked = false;
                        }
                }
+               if (rtlpriv->cfg->ops->bt_wifi_media_status_notify)
+                       rtlpriv->cfg->ops->bt_wifi_media_status_notify(hw,
+                                                        ppsc->report_linked);
        }
 
 out:
@@ -885,7 +982,6 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
                RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
                         "IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid);
                return rtl_tx_agg_stop(hw, sta, tid);
-               break;
        case IEEE80211_AMPDU_TX_OPERATIONAL:
                RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
                         "IEEE80211_AMPDU_TX_OPERATIONAL:TID:%d\n", tid);
@@ -894,11 +990,11 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
        case IEEE80211_AMPDU_RX_START:
                RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
                         "IEEE80211_AMPDU_RX_START:TID:%d\n", tid);
-               break;
+               return rtl_rx_agg_start(hw, sta, tid);
        case IEEE80211_AMPDU_RX_STOP:
                RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
                         "IEEE80211_AMPDU_RX_STOP:TID:%d\n", tid);
-               break;
+               return rtl_rx_agg_stop(hw, sta, tid);
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         "IEEE80211_AMPDU_ERR!!!!:\n");
@@ -912,12 +1008,20 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 
-       mac->act_scanning = true;
-
        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "\n");
+       mac->act_scanning = true;
+       if (rtlpriv->link_info.higher_busytraffic) {
+               mac->skip_scan = true;
+               return;
+       }
 
+       if (rtlpriv->dm.supp_phymode_switch) {
+               if (rtlpriv->cfg->ops->chk_switch_dmdp)
+                       rtlpriv->cfg->ops->chk_switch_dmdp(hw);
+       }
        if (mac->link_state == MAC80211_LINKED) {
-               rtl_lps_leave(hw);
+               rtlpriv->enter_ps = false;
+               schedule_work(&rtlpriv->works.lps_change_work);
                mac->link_state = MAC80211_LINKED_SCANNING;
        } else {
                rtl_ips_nic_on(hw);
@@ -937,6 +1041,16 @@ static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw)
 
        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "\n");
        mac->act_scanning = false;
+       mac->skip_scan = false;
+       if (rtlpriv->link_info.higher_busytraffic)
+               return;
+
+       /*p2p will use 1/6/11 to scan */
+       if (mac->n_channels == 3)
+               mac->p2p_in_use = true;
+       else
+               mac->p2p_in_use = false;
+       mac->n_channels = 0;
        /* Dual mac */
        rtlpriv->rtlhal.load_imrandiqk_setting_for2g = false;
 
@@ -970,6 +1084,11 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                         "not open hw encryption\n");
                return -ENOSPC; /*User disabled HW-crypto */
        }
+       /* To support IBSS, use sw-crypto for GTK */
+       if (((vif->type == NL80211_IFTYPE_ADHOC) ||
+            (vif->type == NL80211_IFTYPE_MESH_POINT)) &&
+             !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+               return -ENOSPC;
        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
                 "%s hardware based encryption for keyidx: %d, mac: %pM\n",
                 cmd == SET_KEY ? "Using" : "Disabling", key->keyidx,
@@ -996,6 +1115,14 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                key_type = AESCCMP_ENCRYPTION;
                RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "alg:CCMP\n");
                break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               /*HW doesn't support CMAC encryption, use software CMAC */
+               key_type = AESCMAC_ENCRYPTION;
+               RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "alg:CMAC\n");
+               RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+                        "HW don't support CMAC encryption, use software CMAC\n");
+               err = -EOPNOTSUPP;
+               goto out_unlock;
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "alg_err:%x!!!!\n",
                         key->cipher);
@@ -1017,13 +1144,14 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
         * 1) wep only: is just for wep enc, in this condition
         * rtlpriv->sec.pairwise_enc_algorithm == NO_ENCRYPTION
         * will be true & enable_hw_sec will be set when wep
-        * ke setting.
+        * key setting.
         * 2) wep(group) + AES(pairwise): some AP like cisco
         * may use it, in this condition enable_hw_sec will not
         * be set when wep key setting */
        /* we must reset sec_info after lingked before set key,
         * or some flag will be wrong*/
-       if (mac->opmode == NL80211_IFTYPE_AP) {
+       if (vif->type == NL80211_IFTYPE_AP ||
+           vif->type == NL80211_IFTYPE_MESH_POINT) {
                if (!group_key || key_type == WEP40_ENCRYPTION ||
                        key_type == WEP104_ENCRYPTION) {
                        if (group_key)
@@ -1098,12 +1226,16 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                key->hw_key_idx = key_idx;
                if (key_type == TKIP_ENCRYPTION)
                        key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               /*use software CCMP encryption for management frames (MFP) */
+               if (key_type == AESCCMP_ENCRYPTION)
+                       key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
                break;
        case DISABLE_KEY:
                RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
                         "disable key delete one entry\n");
                /*set local buf about wep key. */
-               if (mac->opmode == NL80211_IFTYPE_AP) {
+               if (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_MESH_POINT) {
                        if (sta)
                                rtl_cam_del_entry(hw, sta->addr);
                }
@@ -1163,10 +1295,10 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw)
 }
 
 /* this function is called by mac80211 to flush tx buffer
- * before switch channle or power save, or tx buffer packet
+ * before switch channel or power save, or tx buffer packet
  * maybe send after offchannel or rf sleep, this may cause
  * dis-association by AP */
-static void rtl_op_flush(struct ieee80211_hw *hw, bool drop)
+static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
@@ -1180,6 +1312,7 @@ const struct ieee80211_ops rtl_ops = {
        .tx = rtl_op_tx,
        .add_interface = rtl_op_add_interface,
        .remove_interface = rtl_op_remove_interface,
+       .change_interface = rtl_op_change_interface,
        .config = rtl_op_config,
        .configure_filter = rtl_op_configure_filter,
        .sta_add = rtl_op_sta_add,
index bdda9b2fffe151f79af75eb3042d311e054ff3ed..7d52d3d7769f0e357be0900ec0423dac70cb390c 100644 (file)
@@ -41,7 +41,10 @@ void rtl_dbgp_flag_init(struct ieee80211_hw *hw)
            COMP_BEACON | COMP_RATE | COMP_RXDESC | COMP_DIG | COMP_TXAGC |
            COMP_POWER | COMP_POWER_TRACKING | COMP_BB_POWERSAVING | COMP_SWAS |
            COMP_RF | COMP_TURBO | COMP_RATR | COMP_CMD |
-           COMP_EFUSE | COMP_QOS | COMP_MAC80211 | COMP_REGD | COMP_CHAN;
+           COMP_EFUSE | COMP_QOS | COMP_MAC80211 | COMP_REGD | COMP_CHAN |
+           COMP_EASY_CONCURRENT | COMP_EFUSE | COMP_QOS | COMP_MAC80211 |
+           COMP_REGD | COMP_CHAN | COMP_BT_COEXIST;
+
 
        for (i = 0; i < DBGP_TYPE_MAX; i++)
                rtlpriv->dbg.dbgp_type[i] = 0;
index fd3269f476854235993dbcf04990fcab6d43b810..6d669364e3d9923865f0a5d6f031647a3cf830bf 100644 (file)
 /* Define EEPROM and EFUSE  check module bit*/
 #define EEPROM_W                       BIT(0)
 #define EFUSE_PG                       BIT(1)
-#define EFUSE_READ_ALL         BIT(2)
+#define EFUSE_READ_ALL                 BIT(2)
 
 /* Define init check for module bit*/
 #define        INIT_EEPROM                     BIT(0)
-#define        INIT_TxPower            BIT(1)
+#define        INIT_TXPOWER                    BIT(1)
 #define        INIT_IQK                        BIT(2)
 #define        INIT_RF                         BIT(3)
 
 #define        PHY_TXPWR                       BIT(8)
 #define        PHY_PWRDIFF                     BIT(9)
 
+/* Define Dynamic Mechanism check module bit --> FDM */
+#define WA_IOT                         BIT(0)
+#define DM_PWDB                                BIT(1)
+#define DM_MONITOR                     BIT(2)
+#define DM_DIG                         BIT(3)
+#define DM_EDCA_TURBO                  BIT(4)
+
+#define DM_PWDB                                BIT(1)
+
 enum dbgp_flag_e {
        FQOS = 0,
        FTX = 1,
index 8e2f9afb125a86874359639bd17fa568332ccefd..9e3894178e7773b3d6aca8a4cca70fc518fd6a96 100644 (file)
@@ -35,8 +35,6 @@ static const u8 MAX_PGPKT_SIZE = 9;
 static const u8 PGPKT_DATA_SIZE = 8;
 static const int EFUSE_MAX_SIZE = 512;
 
-static const u8 EFUSE_OOB_PROTECT_BYTES = 15;
-
 static const struct efuse_map RTL8712_SDIO_EFUSE_TABLE[] = {
        {0, 0, 0, 2},
        {0, 1, 0, 2},
@@ -240,6 +238,7 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf)
        u8 rtemp8[1];
        u16 efuse_addr = 0;
        u8 offset, wren;
+       u8 u1temp = 0;
        u16 i;
        u16 j;
        const u16 efuse_max_section =
@@ -285,10 +284,31 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf)
        }
 
        while ((*rtemp8 != 0xFF) && (efuse_addr < efuse_len)) {
-               offset = ((*rtemp8 >> 4) & 0x0f);
+               /*  Check PG header for section num.  */
+               if ((*rtemp8 & 0x1F) == 0x0F) {/* extended header */
+                       u1temp = ((*rtemp8 & 0xE0) >> 5);
+                       read_efuse_byte(hw, efuse_addr, rtemp8);
 
-               if (offset < efuse_max_section) {
+                       if ((*rtemp8 & 0x0F) == 0x0F) {
+                               efuse_addr++;
+                               read_efuse_byte(hw, efuse_addr, rtemp8);
+
+                               if (*rtemp8 != 0xFF &&
+                                   (efuse_addr < efuse_len)) {
+                                       efuse_addr++;
+                               }
+                               continue;
+                       } else {
+                               offset = ((*rtemp8 & 0xF0) >> 1) | u1temp;
+                               wren = (*rtemp8 & 0x0F);
+                               efuse_addr++;
+                       }
+               } else {
+                       offset = ((*rtemp8 >> 4) & 0x0f);
                        wren = (*rtemp8 & 0x0f);
+               }
+
+               if (offset < efuse_max_section) {
                        RTPRINT(rtlpriv, FEEPROM, EFUSE_READ_ALL,
                                "offset-%d Worden=%x\n", offset, wren);
 
@@ -391,7 +411,8 @@ bool efuse_shadow_update_chk(struct ieee80211_hw *hw)
        efuse_used = rtlefuse->efuse_usedbytes;
 
        if ((totalbytes + efuse_used) >=
-           (EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES))
+           (EFUSE_MAX_SIZE -
+            rtlpriv->cfg->maps[EFUSE_OOB_PROTECT_BYTES_LEN]))
                result = false;
 
        RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD,
@@ -932,8 +953,8 @@ static int efuse_pg_packet_write(struct ieee80211_hw *hw,
        u8 badworden = 0x0F;
        static int repeat_times;
 
-       if (efuse_get_current_size(hw) >=
-           (EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES)) {
+       if (efuse_get_current_size(hw) >= (EFUSE_MAX_SIZE -
+           rtlpriv->cfg->maps[EFUSE_OOB_PROTECT_BYTES_LEN])) {
                RTPRINT(rtlpriv, FEEPROM, EFUSE_PG,
                        "efuse_pg_packet_write error\n");
                return false;
@@ -949,8 +970,8 @@ static int efuse_pg_packet_write(struct ieee80211_hw *hw,
 
        RTPRINT(rtlpriv, FEEPROM, EFUSE_PG,  "efuse Power ON\n");
 
-       while (continual && (efuse_addr <
-              (EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES))) {
+       while (continual && (efuse_addr < (EFUSE_MAX_SIZE -
+              rtlpriv->cfg->maps[EFUSE_OOB_PROTECT_BYTES_LEN]))) {
 
                if (write_state == PG_STATE_HEADER) {
                        badworden = 0x0F;
@@ -1003,7 +1024,8 @@ static int efuse_pg_packet_write(struct ieee80211_hw *hw,
                }
        }
 
-       if (efuse_addr >= (EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES)) {
+       if (efuse_addr >= (EFUSE_MAX_SIZE -
+           rtlpriv->cfg->maps[EFUSE_OOB_PROTECT_BYTES_LEN])) {
                RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD,
                         "efuse_addr(%#x) Out of size!!\n", efuse_addr);
        }
@@ -1102,8 +1124,11 @@ static void efuse_power_switch(struct ieee80211_hw *hw, u8 write, u8 pwrstate)
        u8 tempval;
        u16 tmpV16;
 
-       if (pwrstate && (rtlhal->hw_type !=
-               HARDWARE_TYPE_RTL8192SE)) {
+       if (pwrstate && (rtlhal->hw_type != HARDWARE_TYPE_RTL8192SE)) {
+               if (rtlhal->hw_type == HARDWARE_TYPE_RTL8188EE)
+                       rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_ACCESS],
+                                      0x69);
+
                tmpV16 = rtl_read_word(rtlpriv,
                                       rtlpriv->cfg->maps[SYS_ISO_CTRL]);
                if (!(tmpV16 & rtlpriv->cfg->maps[EFUSE_PWC_EV12V])) {
@@ -1153,6 +1178,10 @@ static void efuse_power_switch(struct ieee80211_hw *hw, u8 write, u8 pwrstate)
                }
 
        } else {
+               if (rtlhal->hw_type == HARDWARE_TYPE_RTL8188EE)
+                       rtl_write_byte(rtlpriv,
+                                      rtlpriv->cfg->maps[EFUSE_ACCESS], 0);
+
                if (write) {
                        tempval = rtl_read_byte(rtlpriv,
                                                rtlpriv->cfg->maps[EFUSE_TEST] +
index 2bdea9a8699e1cd653d85371bd54f9d23ec817e7..395a326acfb44a0874b331b64442d23b5e120d1a 100644 (file)
@@ -32,7 +32,6 @@
 
 #define EFUSE_IC_ID_OFFSET             506
 
-#define EFUSE_REAL_CONTENT_LEN         512
 #define EFUSE_MAP_LEN                  128
 #define EFUSE_MAX_WORD_UNIT            4
 
index 4261e8ecc4c382d84a9b19c40d8fed2b463d5a5a..999ffc12578be27fb9b07259e69c723eb0efdbd3 100644 (file)
@@ -59,7 +59,7 @@ static u8 _rtl_mac_to_hwqueue(struct ieee80211_hw *hw,
 
        if (unlikely(ieee80211_is_beacon(fc)))
                return BEACON_QUEUE;
-       if (ieee80211_is_mgmt(fc))
+       if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))
                return MGNT_QUEUE;
        if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SE)
                if (ieee80211_is_nullfunc(fc))
@@ -271,9 +271,6 @@ static void rtl_pci_enable_aspm(struct ieee80211_hw *hw)
        struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
        struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
        struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-       u8 pcibridge_busnum = pcipriv->ndis_adapter.pcibridge_busnum;
-       u8 pcibridge_devnum = pcipriv->ndis_adapter.pcibridge_devnum;
-       u8 pcibridge_funcnum = pcipriv->ndis_adapter.pcibridge_funcnum;
        u8 pcibridge_vendor = pcipriv->ndis_adapter.pcibridge_vendor;
        u8 num4bytes = pcipriv->ndis_adapter.num4bytes;
        u16 aspmlevel;
@@ -302,8 +299,7 @@ static void rtl_pci_enable_aspm(struct ieee80211_hw *hw)
                              u_pcibridge_aspmsetting);
 
        RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
-                "PlatformEnableASPM():PciBridge busnumber[%x], DevNumbe[%x], funcnumber[%x], Write reg[%x] = %x\n",
-                pcibridge_busnum, pcibridge_devnum, pcibridge_funcnum,
+                "PlatformEnableASPM(): Write reg[%x] = %x\n",
                 (pcipriv->ndis_adapter.pcibridge_pciehdr_offset + 0x10),
                 u_pcibridge_aspmsetting);
 
@@ -349,6 +345,49 @@ static bool rtl_pci_get_amd_l1_patch(struct ieee80211_hw *hw)
        return status;
 }
 
+static bool rtl_pci_check_buddy_priv(struct ieee80211_hw *hw,
+                                    struct rtl_priv **buddy_priv)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       bool find_buddy_priv = false;
+       struct rtl_priv *tpriv = NULL;
+       struct rtl_pci_priv *tpcipriv = NULL;
+
+       if (!list_empty(&rtlpriv->glb_var->glb_priv_list)) {
+               list_for_each_entry(tpriv, &rtlpriv->glb_var->glb_priv_list,
+                                   list) {
+                       if (tpriv) {
+                               tpcipriv = (struct rtl_pci_priv *)tpriv->priv;
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                                        "pcipriv->ndis_adapter.funcnumber %x\n",
+                                       pcipriv->ndis_adapter.funcnumber);
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                                        "tpcipriv->ndis_adapter.funcnumber %x\n",
+                                       tpcipriv->ndis_adapter.funcnumber);
+
+                               if ((pcipriv->ndis_adapter.busnumber ==
+                                    tpcipriv->ndis_adapter.busnumber) &&
+                                   (pcipriv->ndis_adapter.devnumber ==
+                                   tpcipriv->ndis_adapter.devnumber) &&
+                                   (pcipriv->ndis_adapter.funcnumber !=
+                                   tpcipriv->ndis_adapter.funcnumber)) {
+                                       find_buddy_priv = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "find_buddy_priv %d\n", find_buddy_priv);
+
+       if (find_buddy_priv)
+               *buddy_priv = tpriv;
+
+       return find_buddy_priv;
+}
+
 static void rtl_pci_get_linkcontrol_field(struct ieee80211_hw *hw)
 {
        struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
@@ -420,17 +459,14 @@ static void _rtl_pci_io_handler_init(struct device *dev,
 
 }
 
-static void _rtl_pci_io_handler_release(struct ieee80211_hw *hw)
-{
-}
-
 static bool _rtl_update_earlymode_info(struct ieee80211_hw *hw,
                struct sk_buff *skb, struct rtl_tcb_desc *tcb_desc, u8 tid)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       u8 additionlen = FCS_LEN;
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
        struct sk_buff *next_skb;
+       u8 additionlen = FCS_LEN;
 
        /* here open is 4, wep/tkip is 8, aes is 12*/
        if (info->control.hw_key)
@@ -455,7 +491,7 @@ static bool _rtl_update_earlymode_info(struct ieee80211_hw *hw,
                                      next_skb))
                        break;
 
-               if (tcb_desc->empkt_num >= 5)
+               if (tcb_desc->empkt_num >= rtlhal->max_earlymode_num)
                        break;
        }
        spin_unlock_bh(&rtlpriv->locks.waitq_lock);
@@ -471,11 +507,17 @@ static void _rtl_pci_tx_chk_waitq(struct ieee80211_hw *hw)
        struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
        struct sk_buff *skb = NULL;
        struct ieee80211_tx_info *info = NULL;
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
        int tid;
 
        if (!rtlpriv->rtlhal.earlymode_enable)
                return;
 
+       if (rtlpriv->dm.supp_phymode_switch &&
+           (rtlpriv->easy_concurrent_ctl.switch_in_process ||
+           (rtlpriv->buddy_priv &&
+           rtlpriv->buddy_priv->easy_concurrent_ctl.switch_in_process)))
+               return;
        /* we juse use em for BE/BK/VI/VO */
        for (tid = 7; tid >= 0; tid--) {
                u8 hw_queue = ac_to_hwq[rtl_tid_to_ac(tid)];
@@ -487,7 +529,8 @@ static void _rtl_pci_tx_chk_waitq(struct ieee80211_hw *hw)
 
                        spin_lock_bh(&rtlpriv->locks.waitq_lock);
                        if (!skb_queue_empty(&mac->skb_waitq[tid]) &&
-                          (ring->entries - skb_queue_len(&ring->queue) > 5)) {
+                           (ring->entries - skb_queue_len(&ring->queue) >
+                            rtlhal->max_earlymode_num)) {
                                skb = skb_dequeue(&mac->skb_waitq[tid]);
                        } else {
                                spin_unlock_bh(&rtlpriv->locks.waitq_lock);
@@ -525,9 +568,8 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio)
                u8 own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) entry, true,
                                                          HW_DESC_OWN);
 
-               /*
-                *beacon packet will only use the first
-                *descriptor defautly,and the own may not
+               /*beacon packet will only use the first
+                *descriptor by defaut, and the own may not
                 *be cleared by the hardware
                 */
                if (own)
@@ -558,8 +600,9 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio)
                }
 
                /* for sw LPS, just after NULL skb send out, we can
-                * sure AP kown we are sleeped, our we should not let
-                * rf to sleep*/
+                * sure AP knows we are sleeping, we should not let
+                * rf sleep
+                */
                fc = rtl_get_fc(skb);
                if (ieee80211_is_nullfunc(fc)) {
                        if (ieee80211_has_pm(fc)) {
@@ -569,6 +612,15 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio)
                                rtlpriv->psc.state_inap = false;
                        }
                }
+               if (ieee80211_is_action(fc)) {
+                       struct ieee80211_mgmt *action_frame =
+                               (struct ieee80211_mgmt *)skb->data;
+                       if (action_frame->u.action.u.ht_smps.action ==
+                           WLAN_HT_ACTION_SMPS) {
+                               dev_kfree_skb(skb);
+                               goto tx_status_ok;
+                       }
+               }
 
                /* update tid tx pkt num */
                tid = rtl_get_tid(skb);
@@ -602,7 +654,8 @@ tx_status_ok:
        if (((rtlpriv->link_info.num_rx_inperiod +
                rtlpriv->link_info.num_tx_inperiod) > 8) ||
                (rtlpriv->link_info.num_rx_inperiod > 2)) {
-               schedule_work(&rtlpriv->works.lps_leave_work);
+               rtlpriv->enter_ps = false;
+               schedule_work(&rtlpriv->works.lps_change_work);
        }
 }
 
@@ -637,6 +690,10 @@ static void _rtl_receive_one(struct ieee80211_hw *hw, struct sk_buff *skb,
                        rtlpriv->link_info.num_rx_inperiod++;
        }
 
+       /* static bcn for roaming */
+       rtl_beacon_statistic(hw, skb);
+       rtl_p2p_info(hw, (void *)skb->data, skb->len);
+
        /* for sw lps */
        rtl_swlps_beacon(hw, (void *)skb->data, skb->len);
        rtl_recognize_peer(hw, (void *)skb->data, skb->len);
@@ -727,9 +784,10 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
                _rtl_receive_one(hw, skb, rx_status);
 
                if (((rtlpriv->link_info.num_rx_inperiod +
-                       rtlpriv->link_info.num_tx_inperiod) > 8) ||
-                       (rtlpriv->link_info.num_rx_inperiod > 2)) {
-                       schedule_work(&rtlpriv->works.lps_leave_work);
+                     rtlpriv->link_info.num_tx_inperiod) > 8) ||
+                     (rtlpriv->link_info.num_rx_inperiod > 2)) {
+                       rtlpriv->enter_ps = false;
+                       schedule_work(&rtlpriv->works.lps_change_work);
                }
 
                dev_kfree_skb_any(skb);
@@ -803,7 +861,7 @@ static irqreturn_t _rtl_pci_interrupt(int irq, void *dev_id)
                RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "beacon interrupt!\n");
        }
 
-       if (inta & rtlpriv->cfg->maps[RTL_IMR_BcnInt]) {
+       if (inta & rtlpriv->cfg->maps[RTL_IMR_BCNINT]) {
                RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE,
                         "prepare beacon for interrupt!\n");
                tasklet_schedule(&rtlpriv->works.irq_prepare_bcn_tasklet);
@@ -884,6 +942,16 @@ static irqreturn_t _rtl_pci_interrupt(int irq, void *dev_id)
                _rtl_pci_rx_interrupt(hw);
        }
 
+       /*fw related*/
+       if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723AE) {
+               if (inta & rtlpriv->cfg->maps[RTL_IMR_C2HCMD]) {
+                       RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE,
+                                "firmware interrupt!\n");
+                       queue_delayed_work(rtlpriv->works.rtl_wq,
+                                          &rtlpriv->works.fwevt_wq, 0);
+               }
+       }
+
        if (rtlpriv->rtlhal.earlymode_enable)
                tasklet_schedule(&rtlpriv->works.irq_tasklet);
 
@@ -939,13 +1007,17 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw)
        return;
 }
 
-static void rtl_lps_leave_work_callback(struct work_struct *work)
+static void rtl_lps_change_work_callback(struct work_struct *work)
 {
        struct rtl_works *rtlworks =
-           container_of(work, struct rtl_works, lps_leave_work);
+           container_of(work, struct rtl_works, lps_change_work);
        struct ieee80211_hw *hw = rtlworks->hw;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
 
-       rtl_lps_leave(hw);
+       if (rtlpriv->enter_ps)
+               rtl_lps_enter(hw);
+       else
+               rtl_lps_leave(hw);
 }
 
 static void _rtl_pci_init_trx_var(struct ieee80211_hw *hw)
@@ -1009,7 +1081,8 @@ static void _rtl_pci_init_struct(struct ieee80211_hw *hw,
        tasklet_init(&rtlpriv->works.irq_prepare_bcn_tasklet,
                     (void (*)(unsigned long))_rtl_pci_prepare_bcn_tasklet,
                     (unsigned long)hw);
-       INIT_WORK(&rtlpriv->works.lps_leave_work, rtl_lps_leave_work_callback);
+       INIT_WORK(&rtlpriv->works.lps_change_work,
+                 rtl_lps_change_work_callback);
 }
 
 static int _rtl_pci_init_tx_ring(struct ieee80211_hw *hw,
@@ -1458,10 +1531,14 @@ static void rtl_pci_flush(struct ieee80211_hw *hw, bool drop)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
        u16 i = 0;
        int queue_id;
        struct rtl8192_tx_ring *ring;
 
+       if (mac->skip_scan)
+               return;
+
        for (queue_id = RTL_PCI_MAX_TX_QUEUE_COUNT - 1; queue_id >= 0;) {
                u32 queue_len;
                ring = &pcipriv->dev.tx_ring[queue_id];
@@ -1491,7 +1568,7 @@ static void rtl_pci_deinit(struct ieee80211_hw *hw)
 
        synchronize_irq(rtlpci->pdev->irq);
        tasklet_kill(&rtlpriv->works.irq_tasklet);
-       cancel_work_sync(&rtlpriv->works.lps_leave_work);
+       cancel_work_sync(&rtlpriv->works.lps_change_work);
 
        flush_workqueue(rtlpriv->works.rtl_wq);
        destroy_workqueue(rtlpriv->works.rtl_wq);
@@ -1566,7 +1643,7 @@ static void rtl_pci_stop(struct ieee80211_hw *hw)
        set_hal_stop(rtlhal);
 
        rtlpriv->cfg->ops->disable_interrupt(hw);
-       cancel_work_sync(&rtlpriv->works.lps_leave_work);
+       cancel_work_sync(&rtlpriv->works.lps_change_work);
 
        spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flags);
        while (ppsc->rfchange_inprogress) {
@@ -1673,6 +1750,10 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev,
                RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
                         "8192D PCI-E is found - vid/did=%x/%x\n",
                         venderid, deviceid);
+       } else if (deviceid == RTL_PCI_8188EE_DID) {
+               rtlhal->hw_type = HARDWARE_TYPE_RTL8188EE;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                        "Find adapter, Hardware type is 8188EE\n");
        } else {
                RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
                         "Err: Unknown device - vid/did=%x/%x\n",
@@ -1704,6 +1785,9 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev,
        pcipriv->ndis_adapter.devnumber = PCI_SLOT(pdev->devfn);
        pcipriv->ndis_adapter.funcnumber = PCI_FUNC(pdev->devfn);
 
+       /* some ARM have no bridge_pdev and will crash here
+        * so we should check if bridge_pdev is NULL
+        */
        if (bridge_pdev) {
                /*find bridge info if available */
                pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor;
@@ -1758,6 +1842,7 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev,
                 pcipriv->ndis_adapter.amd_l1_patch);
 
        rtl_pci_parse_configuration(pdev, hw);
+       list_add_tail(&rtlpriv->list, &rtlpriv->glb_var->glb_priv_list);
 
        return true;
 }
@@ -1804,6 +1889,7 @@ int rtl_pci_probe(struct pci_dev *pdev,
        pci_set_drvdata(pdev, hw);
 
        rtlpriv = hw->priv;
+       rtlpriv->hw = hw;
        pcipriv = (void *)rtlpriv->priv;
        pcipriv->dev.pdev = pdev;
        init_completion(&rtlpriv->firmware_loading_complete);
@@ -1812,6 +1898,7 @@ int rtl_pci_probe(struct pci_dev *pdev,
        rtlpriv->rtlhal.interface = INTF_PCI;
        rtlpriv->cfg = (struct rtl_hal_cfg *)(id->driver_data);
        rtlpriv->intf_ops = &rtl_pci_ops;
+       rtlpriv->glb_var = &global_var;
 
        /*
         *init dbgp flags before all
@@ -1916,7 +2003,6 @@ int rtl_pci_probe(struct pci_dev *pdev,
 
 fail3:
        rtl_deinit_core(hw);
-       _rtl_pci_io_handler_release(hw);
 
        if (rtlpriv->io.pci_mem_start != 0)
                pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start);
@@ -1965,14 +2051,15 @@ void rtl_pci_disconnect(struct pci_dev *pdev)
 
        rtl_pci_deinit(hw);
        rtl_deinit_core(hw);
-       _rtl_pci_io_handler_release(hw);
        rtlpriv->cfg->ops->deinit_sw_vars(hw);
 
        if (rtlpci->irq_alloc) {
+               synchronize_irq(rtlpci->pdev->irq);
                free_irq(rtlpci->pdev->irq, hw);
                rtlpci->irq_alloc = 0;
        }
 
+       list_del(&rtlpriv->list);
        if (rtlpriv->io.pci_mem_start != 0) {
                pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start);
                pci_release_regions(pdev);
@@ -2034,6 +2121,7 @@ struct rtl_intf_ops rtl_pci_ops = {
        .read_efuse_byte = read_efuse_byte,
        .adapter_start = rtl_pci_start,
        .adapter_stop = rtl_pci_stop,
+       .check_buddy_priv = rtl_pci_check_buddy_priv,
        .adapter_tx = rtl_pci_tx,
        .flush = rtl_pci_flush,
        .reset_trx_ring = rtl_pci_reset_trx_ring,
index 65b08f50022e64d76dd2956304bf1acf4b02be93..d3262ec45d230f0f8abd1744501a004006c48198 100644 (file)
@@ -94,6 +94,7 @@
 #define RTL_PCI_8192CU_DID     0x8191  /*8192ce */
 #define RTL_PCI_8192DE_DID     0x8193  /*8192de */
 #define RTL_PCI_8192DE_DID2    0x002B  /*92DE*/
+#define RTL_PCI_8188EE_DID     0x8179  /*8188ee*/
 
 /*8192 support 16 pages of IO registers*/
 #define RTL_MEM_MAPPED_IO_RANGE_8190PCI                0x1000
@@ -175,6 +176,7 @@ struct rtl_pci {
        /*irq */
        u8 irq_alloc;
        u32 irq_mask[2];
+       u32 sys_irq_mask;
 
        /*Bcn control register setting */
        u32 reg_bcn_ctrl_val;
index 13ad33e85577ad2caa852ee976e25fd539c08bd8..884bceae38a91d2b9ce4e07cf70c5c040e844df4 100644 (file)
@@ -180,6 +180,9 @@ void rtl_ips_nic_off_wq_callback(void *data)
                return;
        }
 
+       if (mac->p2p_in_use)
+               return;
+
        if (mac->link_state > MAC80211_NOLINK)
                return;
 
@@ -189,6 +192,9 @@ void rtl_ips_nic_off_wq_callback(void *data)
        if (rtlpriv->sec.being_setkey)
                return;
 
+       if (rtlpriv->cfg->ops->bt_coex_off_before_lps)
+               rtlpriv->cfg->ops->bt_coex_off_before_lps(hw);
+
        if (ppsc->inactiveps) {
                rtstate = ppsc->rfpwr_state;
 
@@ -231,6 +237,9 @@ void rtl_ips_nic_off(struct ieee80211_hw *hw)
                           &rtlpriv->works.ips_nic_off_wq, MSECS(100));
 }
 
+/* NOTICE: any opmode should exc nic_on, or disable without
+ * nic_on may something wrong, like adhoc TP
+ */
 void rtl_ips_nic_on(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -299,7 +308,7 @@ static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
        struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
-       u8 rpwm_val, fw_pwrmode;
+       bool enter_fwlps;
 
        if (mac->opmode == NL80211_IFTYPE_ADHOC)
                return;
@@ -324,43 +333,31 @@ static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
         */
 
        if ((ppsc->fwctrl_lps) && ppsc->report_linked) {
-               bool fw_current_inps;
                if (ppsc->dot11_psmode == EACTIVE) {
                        RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
                                 "FW LPS leave ps_mode:%x\n",
                                 FW_PS_ACTIVE_MODE);
-
-                       rpwm_val = 0x0C;        /* RF on */
-                       fw_pwrmode = FW_PS_ACTIVE_MODE;
-                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
-                                       &rpwm_val);
+                       enter_fwlps = false;
+                       ppsc->pwr_mode = FW_PS_ACTIVE_MODE;
+                       ppsc->smart_ps = 0;
                        rtlpriv->cfg->ops->set_hw_reg(hw,
-                                       HW_VAR_H2C_FW_PWRMODE,
-                                       &fw_pwrmode);
-                       fw_current_inps = false;
-
-                       rtlpriv->cfg->ops->set_hw_reg(hw,
-                                       HW_VAR_FW_PSMODE_STATUS,
-                                       (u8 *) (&fw_current_inps));
+                                               HW_VAR_FW_LPS_ACTION,
+                                               (u8 *)(&enter_fwlps));
+                       if (ppsc->p2p_ps_info.opp_ps)
+                               rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
 
                } else {
                        if (rtl_get_fwlps_doze(hw)) {
                                RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
                                         "FW LPS enter ps_mode:%x\n",
                                         ppsc->fwctrl_psmode);
-
-                               rpwm_val = 0x02;        /* RF off */
-                               fw_current_inps = true;
+                               enter_fwlps = true;
+                               ppsc->pwr_mode = ppsc->fwctrl_psmode;
+                               ppsc->smart_ps = 2;
                                rtlpriv->cfg->ops->set_hw_reg(hw,
-                                               HW_VAR_FW_PSMODE_STATUS,
-                                               (u8 *) (&fw_current_inps));
-                               rtlpriv->cfg->ops->set_hw_reg(hw,
-                                               HW_VAR_H2C_FW_PWRMODE,
-                                               &ppsc->fwctrl_psmode);
+                                                       HW_VAR_FW_LPS_ACTION,
+                                                       (u8 *)(&enter_fwlps));
 
-                               rtlpriv->cfg->ops->set_hw_reg(hw,
-                                               HW_VAR_SET_RPWM,
-                                               &rpwm_val);
                        } else {
                                /* Reset the power save related parameters. */
                                ppsc->dot11_psmode = EACTIVE;
@@ -642,3 +639,286 @@ void rtl_swlps_wq_callback(void *data)
                rtlpriv->psc.state = ps;
        }
 }
+
+static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
+                          unsigned int len)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct ieee80211_mgmt *mgmt = (void *)data;
+       struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
+       u8 *pos, *end, *ie;
+       u16 noa_len;
+       static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
+       u8 noa_num, index, i, noa_index = 0;
+       bool find_p2p_ie = false , find_p2p_ps_ie = false;
+       pos = (u8 *)mgmt->u.beacon.variable;
+       end = data + len;
+       ie = NULL;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       return;
+
+               if (pos[0] == 221 && pos[1] > 4) {
+                       if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) {
+                               ie = pos + 2+4;
+                               break;
+                       }
+               }
+               pos += 2 + pos[1];
+       }
+
+       if (ie == NULL)
+               return;
+       find_p2p_ie = true;
+       /*to find noa ie*/
+       while (ie + 1 < end) {
+               noa_len = READEF2BYTE(&ie[1]);
+               if (ie + 3 + ie[1] > end)
+                       return;
+
+               if (ie[0] == 12) {
+                       find_p2p_ps_ie = true;
+                       if ((noa_len - 2) % 13 != 0) {
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                                        "P2P notice of absence: invalid length.%d\n",
+                                        noa_len);
+                               return;
+                       } else {
+                               noa_num = (noa_len - 2) / 13;
+                       }
+                       noa_index = ie[3];
+                       if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
+                           P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
+                               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
+                                        "update NOA ie.\n");
+                               p2pinfo->noa_index = noa_index;
+                               p2pinfo->opp_ps = (ie[4] >> 7);
+                               p2pinfo->ctwindow = ie[4] & 0x7F;
+                               p2pinfo->noa_num = noa_num;
+                               index = 5;
+                               for (i = 0; i < noa_num; i++) {
+                                       p2pinfo->noa_count_type[i] =
+                                                READEF1BYTE(ie+index);
+                                       index += 1;
+                                       p2pinfo->noa_duration[i] =
+                                                READEF4BYTE(ie+index);
+                                       index += 4;
+                                       p2pinfo->noa_interval[i] =
+                                                READEF4BYTE(ie+index);
+                                       index += 4;
+                                       p2pinfo->noa_start_time[i] =
+                                                READEF4BYTE(ie+index);
+                                       index += 4;
+                               }
+
+                               if (p2pinfo->opp_ps == 1) {
+                                       p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
+                                       /* Driver should wait LPS entering
+                                        * CTWindow
+                                        */
+                                       if (rtlpriv->psc.fw_current_inpsmode)
+                                               rtl_p2p_ps_cmd(hw,
+                                                              P2P_PS_ENABLE);
+                               } else if (p2pinfo->noa_num > 0) {
+                                       p2pinfo->p2p_ps_mode = P2P_PS_NOA;
+                                       rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
+                               } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
+                                       rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
+                               }
+                       }
+               break;
+               }
+               ie += 3 + noa_len;
+       }
+
+       if (find_p2p_ie == true) {
+               if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) &&
+                   (find_p2p_ps_ie == false))
+                       rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
+       }
+}
+
+static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
+                             unsigned int len)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct ieee80211_mgmt *mgmt = (void *)data;
+       struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
+       u8 noa_num, index, i, noa_index = 0;
+       u8 *pos, *end, *ie;
+       u16 noa_len;
+       static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
+
+       pos = (u8 *)&mgmt->u.action.category;
+       end = data + len;
+       ie = NULL;
+
+       if (pos[0] == 0x7f) {
+               if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0)
+                       ie = pos + 3+4;
+       }
+
+       if (ie == NULL)
+               return;
+
+       RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n");
+       /*to find noa ie*/
+       while (ie + 1 < end) {
+               noa_len = READEF2BYTE(&ie[1]);
+               if (ie + 3 + ie[1] > end)
+                       return;
+
+               if (ie[0] == 12) {
+                       RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n");
+                       RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ",
+                                     ie, noa_len);
+                       if ((noa_len - 2) % 13 != 0) {
+                               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
+                                        "P2P notice of absence: invalid length.%d\n",
+                                        noa_len);
+                               return;
+                       } else {
+                               noa_num = (noa_len - 2) / 13;
+                       }
+                       noa_index = ie[3];
+                       if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
+                           P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
+                               p2pinfo->noa_index = noa_index;
+                               p2pinfo->opp_ps = (ie[4] >> 7);
+                               p2pinfo->ctwindow = ie[4] & 0x7F;
+                               p2pinfo->noa_num = noa_num;
+                               index = 5;
+                               for (i = 0; i < noa_num; i++) {
+                                       p2pinfo->noa_count_type[i] =
+                                                        READEF1BYTE(ie+index);
+                                       index += 1;
+                                       p2pinfo->noa_duration[i] =
+                                                        READEF4BYTE(ie+index);
+                                       index += 4;
+                                       p2pinfo->noa_interval[i] =
+                                                        READEF4BYTE(ie+index);
+                                       index += 4;
+                                       p2pinfo->noa_start_time[i] =
+                                                        READEF4BYTE(ie+index);
+                                       index += 4;
+                               }
+
+                               if (p2pinfo->opp_ps == 1) {
+                                       p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
+                                       /* Driver should wait LPS entering
+                                        * CTWindow
+                                        */
+                                       if (rtlpriv->psc.fw_current_inpsmode)
+                                               rtl_p2p_ps_cmd(hw,
+                                                              P2P_PS_ENABLE);
+                               } else if (p2pinfo->noa_num > 0) {
+                                       p2pinfo->p2p_ps_mode = P2P_PS_NOA;
+                                       rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
+                               } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
+                                       rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
+                               }
+                       }
+               break;
+               }
+               ie += 3 + noa_len;
+       }
+}
+
+void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
+       struct rtl_p2p_ps_info  *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
+
+       RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n", p2p_ps_state);
+       switch (p2p_ps_state) {
+       case P2P_PS_DISABLE:
+               p2pinfo->p2p_ps_state = p2p_ps_state;
+               rtlpriv->cfg->ops->set_hw_reg(hw,
+                                HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
+                                (u8 *)(&p2p_ps_state));
+
+               p2pinfo->noa_index = 0;
+               p2pinfo->ctwindow = 0;
+               p2pinfo->opp_ps = 0;
+               p2pinfo->noa_num = 0;
+               p2pinfo->p2p_ps_mode = P2P_PS_NONE;
+               if (rtlps->fw_current_inpsmode == true) {
+                       if (rtlps->smart_ps == 0) {
+                               rtlps->smart_ps = 2;
+                               rtlpriv->cfg->ops->set_hw_reg(hw,
+                                        HW_VAR_H2C_FW_PWRMODE,
+                                        (u8 *)(&rtlps->pwr_mode));
+                       }
+               }
+               break;
+       case P2P_PS_ENABLE:
+               if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
+                       p2pinfo->p2p_ps_state = p2p_ps_state;
+
+                       if (p2pinfo->ctwindow > 0) {
+                               if (rtlps->smart_ps != 0) {
+                                       rtlps->smart_ps = 0;
+                                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                                HW_VAR_H2C_FW_PWRMODE,
+                                                (u8 *)(&rtlps->pwr_mode));
+                               }
+                       }
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
+                                (u8 *)(&p2p_ps_state));
+               }
+               break;
+       case P2P_PS_SCAN:
+       case P2P_PS_SCAN_DONE:
+       case P2P_PS_ALLSTASLEEP:
+               if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
+                       p2pinfo->p2p_ps_state = p2p_ps_state;
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
+                                (u8 *)(&p2p_ps_state));
+               }
+               break;
+       default:
+               break;
+       }
+       RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
+                "ctwindow %x oppps %x\n", p2pinfo->ctwindow, p2pinfo->opp_ps);
+       RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
+                "count %x duration %x index %x interval %x start time %x noa num %x\n",
+                p2pinfo->noa_count_type[0], p2pinfo->noa_duration[0],
+                p2pinfo->noa_index, p2pinfo->noa_interval[0],
+                p2pinfo->noa_start_time[0], p2pinfo->noa_num);
+       RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "end\n");
+}
+
+void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct ieee80211_hdr *hdr = (void *)data;
+
+       if (!mac->p2p)
+               return;
+       if (mac->link_state != MAC80211_LINKED)
+               return;
+       /* min. beacon length + FCS_LEN */
+       if (len <= 40 + FCS_LEN)
+               return;
+
+       /* and only beacons from the associated BSSID, please */
+       if (compare_ether_addr(hdr->addr3, rtlpriv->mac80211.bssid))
+               return;
+
+       /* check if this really is a beacon */
+       if (!(ieee80211_is_beacon(hdr->frame_control) ||
+             ieee80211_is_probe_resp(hdr->frame_control) ||
+             ieee80211_is_action(hdr->frame_control)))
+               return;
+
+       if (ieee80211_is_action(hdr->frame_control))
+               rtl_p2p_action_ie(hw, data, len - FCS_LEN);
+       else
+               rtl_p2p_noa_ie(hw, data, len - FCS_LEN);
+}
index 1357856998c21b8a252c016af2a8af17c8af954b..4d682b753f503df0f61b5e58b98e871f58c31029 100644 (file)
@@ -47,5 +47,7 @@ void rtl_swlps_wq_callback(void *data);
 void rtl_swlps_rfon_wq_callback(void *data);
 void rtl_swlps_rf_awake(struct ieee80211_hw *hw);
 void rtl_swlps_rf_sleep(struct ieee80211_hw *hw);
+void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state);
+void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len);
 
 #endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile b/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile
new file mode 100644 (file)
index 0000000..5b194e9
--- /dev/null
@@ -0,0 +1,16 @@
+rtl8188ee-objs :=              \
+               dm.o            \
+               fw.o            \
+               hw.o            \
+               led.o           \
+               phy.o           \
+               pwrseq.o        \
+               pwrseqcmd.o     \
+               rf.o            \
+               sw.o            \
+               table.o         \
+               trx.o
+
+obj-$(CONFIG_RTL8188EE) += rtl8188ee.o
+
+ccflags-y += -Idrivers/net/wireless/rtlwifi -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/def.h b/drivers/net/wireless/rtlwifi/rtl8188ee/def.h
new file mode 100644 (file)
index 0000000..c764fff
--- /dev/null
@@ -0,0 +1,324 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92C_DEF_H__
+#define __RTL92C_DEF_H__
+
+#define HAL_RETRY_LIMIT_INFRA                          48
+#define HAL_RETRY_LIMIT_AP_ADHOC                       7
+
+#define RESET_DELAY_8185                               20
+
+#define RT_IBSS_INT_MASKS      (IMR_BCNINT | IMR_TBDOK | IMR_TBDER)
+#define RT_AC_INT_MASKS                (IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK)
+
+#define NUM_OF_FIRMWARE_QUEUE                          10
+#define NUM_OF_PAGES_IN_FW                             0x100
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK                     0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE                     0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI                     0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO                     0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA                   0x0
+#define NUM_OF_PAGE_IN_FW_QUEUE_CMD                    0x0
+#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT                   0x02
+#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH                   0x02
+#define NUM_OF_PAGE_IN_FW_QUEUE_BCN                    0x2
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB                    0xA1
+
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM                 0x026
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM                 0x048
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM                 0x048
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM                 0x026
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM                        0x00
+
+#define MAX_LINES_HWCONFIG_TXT                         1000
+#define MAX_BYTES_LINE_HWCONFIG_TXT                    256
+
+#define SW_THREE_WIRE                                  0
+#define HW_THREE_WIRE                                  2
+
+#define BT_DEMO_BOARD                                  0
+#define BT_QA_BOARD                                    1
+#define BT_FPGA                                                2
+
+#define HAL_PRIME_CHNL_OFFSET_DONT_CARE                        0
+#define HAL_PRIME_CHNL_OFFSET_LOWER                    1
+#define HAL_PRIME_CHNL_OFFSET_UPPER                    2
+
+#define MAX_H2C_QUEUE_NUM                              10
+
+#define RX_MPDU_QUEUE                                  0
+#define RX_CMD_QUEUE                                   1
+#define RX_MAX_QUEUE                                   2
+#define AC2QUEUEID(_AC)                                        (_AC)
+
+#define        C2H_RX_CMD_HDR_LEN                              8
+#define        GET_C2H_CMD_CMD_LEN(__prxhdr)                   \
+       LE_BITS_TO_4BYTE((__prxhdr), 0, 16)
+#define        GET_C2H_CMD_ELEMENT_ID(__prxhdr)                \
+       LE_BITS_TO_4BYTE((__prxhdr), 16, 8)
+#define        GET_C2H_CMD_CMD_SEQ(__prxhdr)                   \
+       LE_BITS_TO_4BYTE((__prxhdr), 24, 7)
+#define        GET_C2H_CMD_CONTINUE(__prxhdr)                  \
+       LE_BITS_TO_4BYTE((__prxhdr), 31, 1)
+#define        GET_C2H_CMD_CONTENT(__prxhdr)                   \
+       ((u8 *)(__prxhdr) + C2H_RX_CMD_HDR_LEN)
+
+#define        GET_C2H_CMD_FEEDBACK_ELEMENT_ID(__pcmdfbhdr)    \
+       LE_BITS_TO_4BYTE((__pcmdfbhdr), 0, 8)
+#define        GET_C2H_CMD_FEEDBACK_CCX_LEN(__pcmdfbhdr)       \
+       LE_BITS_TO_4BYTE((__pcmdfbhdr), 8, 8)
+#define        GET_C2H_CMD_FEEDBACK_CCX_CMD_CNT(__pcmdfbhdr)   \
+       LE_BITS_TO_4BYTE((__pcmdfbhdr), 16, 16)
+#define        GET_C2H_CMD_FEEDBACK_CCX_MAC_ID(__pcmdfbhdr)    \
+       LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 0, 5)
+#define        GET_C2H_CMD_FEEDBACK_CCX_VALID(__pcmdfbhdr)     \
+       LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 7, 1)
+#define        GET_C2H_CMD_FEEDBACK_CCX_RETRY_CNT(__pcmdfbhdr) \
+       LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 8, 5)
+#define        GET_C2H_CMD_FEEDBACK_CCX_TOK(__pcmdfbhdr)       \
+       LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 15, 1)
+#define        GET_C2H_CMD_FEEDBACK_CCX_QSEL(__pcmdfbhdr)      \
+       LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4)
+#define        GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr)       \
+       LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12)
+
+#define CHIP_BONDING_IDENTIFIER(_value)        (((_value)>>22)&0x3)
+
+
+/* [15:12] IC version(CUT): A-cut=0, B-cut=1, C-cut=2, D-cut=3
+ * [7] Manufacturer: TSMC=0, UMC=1
+ * [6:4] RF type: 1T1R=0, 1T2R=1, 2T2R=2
+ * [3] Chip type: TEST=0, NORMAL=1
+ * [2:0] IC type: 81xxC=0, 8723=1, 92D=2
+ */
+#define CHIP_8723                      BIT(0)
+#define CHIP_92D                       BIT(1)
+#define NORMAL_CHIP                    BIT(3)
+#define RF_TYPE_1T1R                   (~(BIT(4)|BIT(5)|BIT(6)))
+#define RF_TYPE_1T2R                   BIT(4)
+#define RF_TYPE_2T2R                   BIT(5)
+#define CHIP_VENDOR_UMC                        BIT(7)
+#define B_CUT_VERSION                  BIT(12)
+#define C_CUT_VERSION                  BIT(13)
+#define D_CUT_VERSION                  ((BIT(12)|BIT(13)))
+#define E_CUT_VERSION                  BIT(14)
+
+
+/* MASK */
+#define IC_TYPE_MASK                   (BIT(0)|BIT(1)|BIT(2))
+#define CHIP_TYPE_MASK                 BIT(3)
+#define RF_TYPE_MASK                   (BIT(4)|BIT(5)|BIT(6))
+#define MANUFACTUER_MASK               BIT(7)
+#define ROM_VERSION_MASK               (BIT(11)|BIT(10)|BIT(9)|BIT(8))
+#define CUT_VERSION_MASK               (BIT(15)|BIT(14)|BIT(13)|BIT(12))
+
+/* Get element */
+#define GET_CVID_IC_TYPE(version)      ((version) & IC_TYPE_MASK)
+#define GET_CVID_CHIP_TYPE(version)    ((version) & CHIP_TYPE_MASK)
+#define GET_CVID_RF_TYPE(version)      ((version) & RF_TYPE_MASK)
+#define GET_CVID_MANUFACTUER(version)  ((version) & MANUFACTUER_MASK)
+#define GET_CVID_ROM_VERSION(version)  ((version) & ROM_VERSION_MASK)
+#define GET_CVID_CUT_VERSION(version)  ((version) & CUT_VERSION_MASK)
+
+
+#define IS_81XXC(version)                                              \
+       ((GET_CVID_IC_TYPE(version) == 0) ? true : false)
+#define IS_8723_SERIES(version)                                                \
+       ((GET_CVID_IC_TYPE(version) == CHIP_8723) ? true : false)
+#define IS_92D(version)                                                        \
+       ((GET_CVID_IC_TYPE(version) == CHIP_92D) ? true : false)
+
+#define IS_NORMAL_CHIP(version)                                                \
+       ((GET_CVID_CHIP_TYPE(version)) ? true : false)
+#define IS_NORMAL_CHIP92D(version)                                     \
+       ((GET_CVID_CHIP_TYPE(version)) ? true : false)
+
+#define IS_1T1R(version)                                               \
+       ((GET_CVID_RF_TYPE(version)) ? false : true)
+#define IS_1T2R(version)                                               \
+       ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T2R) ? true : false)
+#define IS_2T2R(version)                                               \
+       ((GET_CVID_RF_TYPE(version) == RF_TYPE_2T2R) ? true : false)
+#define IS_CHIP_VENDOR_UMC(version)                                    \
+       ((GET_CVID_MANUFACTUER(version)) ? true : false)
+
+#define IS_92C_SERIAL(version)                                         \
+       ((IS_81XXC(version) && IS_2T2R(version)) ? true : false)
+#define IS_81xxC_VENDOR_UMC_A_CUT(version)                             \
+       (IS_81XXC(version) ? ((IS_CHIP_VENDOR_UMC(version)) ?           \
+        ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) : false)
+#define IS_81xxC_VENDOR_UMC_B_CUT(version)                             \
+       (IS_81XXC(version) ? (IS_CHIP_VENDOR_UMC(version) ?             \
+       ((GET_CVID_CUT_VERSION(version) == B_CUT_VERSION) ? true        \
+       : false) : false) : false)
+
+enum version_8188e {
+       VERSION_TEST_CHIP_88E = 0x00,
+       VERSION_NORMAL_CHIP_88E = 0x01,
+       VERSION_UNKNOWN = 0xFF,
+};
+
+enum rx_packet_type {
+       NORMAL_RX,
+       TX_REPORT1,
+       TX_REPORT2,
+       HIS_REPORT,
+};
+
+enum rtl819x_loopback_e {
+       RTL819X_NO_LOOPBACK = 0,
+       RTL819X_MAC_LOOPBACK = 1,
+       RTL819X_DMA_LOOPBACK = 2,
+       RTL819X_CCK_LOOPBACK = 3,
+};
+
+enum rf_optype {
+       RF_OP_BY_SW_3WIRE = 0,
+       RF_OP_BY_FW,
+       RF_OP_MAX
+};
+
+enum rf_power_state {
+       RF_ON,
+       RF_OFF,
+       RF_SLEEP,
+       RF_SHUT_DOWN,
+};
+
+enum power_save_mode {
+       POWER_SAVE_MODE_ACTIVE,
+       POWER_SAVE_MODE_SAVE,
+};
+
+enum power_polocy_config {
+       POWERCFG_MAX_POWER_SAVINGS,
+       POWERCFG_GLOBAL_POWER_SAVINGS,
+       POWERCFG_LOCAL_POWER_SAVINGS,
+       POWERCFG_LENOVO,
+};
+
+enum interface_select_pci {
+       INTF_SEL1_MINICARD,
+       INTF_SEL0_PCIE,
+       INTF_SEL2_RSV,
+       INTF_SEL3_RSV,
+};
+
+enum hal_fw_c2h_cmd_id {
+       HAL_FW_C2H_CMD_Read_MACREG,
+       HAL_FW_C2H_CMD_Read_BBREG,
+       HAL_FW_C2H_CMD_Read_RFREG,
+       HAL_FW_C2H_CMD_Read_EEPROM,
+       HAL_FW_C2H_CMD_Read_EFUSE,
+       HAL_FW_C2H_CMD_Read_CAM,
+       HAL_FW_C2H_CMD_Get_BasicRate,
+       HAL_FW_C2H_CMD_Get_DataRate,
+       HAL_FW_C2H_CMD_Survey,
+       HAL_FW_C2H_CMD_SurveyDone,
+       HAL_FW_C2H_CMD_JoinBss,
+       HAL_FW_C2H_CMD_AddSTA,
+       HAL_FW_C2H_CMD_DelSTA,
+       HAL_FW_C2H_CMD_AtimDone,
+       HAL_FW_C2H_CMD_TX_Report,
+       HAL_FW_C2H_CMD_CCX_Report,
+       HAL_FW_C2H_CMD_DTM_Report,
+       HAL_FW_C2H_CMD_TX_Rate_Statistics,
+       HAL_FW_C2H_CMD_C2HLBK,
+       HAL_FW_C2H_CMD_C2HDBG,
+       HAL_FW_C2H_CMD_C2HFEEDBACK,
+       HAL_FW_C2H_CMD_MAX
+};
+
+enum wake_on_wlan_mode {
+       ewowlandisable,
+       ewakeonmagicpacketonly,
+       ewakeonpatternmatchonly,
+       ewakeonbothtypepacket
+};
+
+enum rtl_desc_qsel {
+       QSLT_BK = 0x2,
+       QSLT_BE = 0x0,
+       QSLT_VI = 0x5,
+       QSLT_VO = 0x7,
+       QSLT_BEACON = 0x10,
+       QSLT_HIGH = 0x11,
+       QSLT_MGNT = 0x12,
+       QSLT_CMD = 0x13,
+};
+
+enum rtl_desc92c_rate {
+       DESC92C_RATE1M = 0x00,
+       DESC92C_RATE2M = 0x01,
+       DESC92C_RATE5_5M = 0x02,
+       DESC92C_RATE11M = 0x03,
+
+       DESC92C_RATE6M = 0x04,
+       DESC92C_RATE9M = 0x05,
+       DESC92C_RATE12M = 0x06,
+       DESC92C_RATE18M = 0x07,
+       DESC92C_RATE24M = 0x08,
+       DESC92C_RATE36M = 0x09,
+       DESC92C_RATE48M = 0x0a,
+       DESC92C_RATE54M = 0x0b,
+
+       DESC92C_RATEMCS0 = 0x0c,
+       DESC92C_RATEMCS1 = 0x0d,
+       DESC92C_RATEMCS2 = 0x0e,
+       DESC92C_RATEMCS3 = 0x0f,
+       DESC92C_RATEMCS4 = 0x10,
+       DESC92C_RATEMCS5 = 0x11,
+       DESC92C_RATEMCS6 = 0x12,
+       DESC92C_RATEMCS7 = 0x13,
+       DESC92C_RATEMCS8 = 0x14,
+       DESC92C_RATEMCS9 = 0x15,
+       DESC92C_RATEMCS10 = 0x16,
+       DESC92C_RATEMCS11 = 0x17,
+       DESC92C_RATEMCS12 = 0x18,
+       DESC92C_RATEMCS13 = 0x19,
+       DESC92C_RATEMCS14 = 0x1a,
+       DESC92C_RATEMCS15 = 0x1b,
+       DESC92C_RATEMCS15_SG = 0x1c,
+       DESC92C_RATEMCS32 = 0x20,
+};
+
+struct phy_sts_cck_8192s_t {
+       u8 adc_pwdb_X[4];
+       u8 sq_rpt;
+       u8 cck_agc_rpt;
+};
+
+struct h2c_cmd_8192c {
+       u8 element_id;
+       u32 cmd_len;
+       u8 *p_cmdbuffer;
+};
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c
new file mode 100644 (file)
index 0000000..21a5cf0
--- /dev/null
@@ -0,0 +1,1794 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../base.h"
+#include "../pci.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "dm.h"
+#include "fw.h"
+#include "trx.h"
+
+static const u32 ofdmswing_table[OFDM_TABLE_SIZE] = {
+       0x7f8001fe,             /* 0, +6.0dB */
+       0x788001e2,             /* 1, +5.5dB */
+       0x71c001c7,             /* 2, +5.0dB */
+       0x6b8001ae,             /* 3, +4.5dB */
+       0x65400195,             /* 4, +4.0dB */
+       0x5fc0017f,             /* 5, +3.5dB */
+       0x5a400169,             /* 6, +3.0dB */
+       0x55400155,             /* 7, +2.5dB */
+       0x50800142,             /* 8, +2.0dB */
+       0x4c000130,             /* 9, +1.5dB */
+       0x47c0011f,             /* 10, +1.0dB */
+       0x43c0010f,             /* 11, +0.5dB */
+       0x40000100,             /* 12, +0dB */
+       0x3c8000f2,             /* 13, -0.5dB */
+       0x390000e4,             /* 14, -1.0dB */
+       0x35c000d7,             /* 15, -1.5dB */
+       0x32c000cb,             /* 16, -2.0dB */
+       0x300000c0,             /* 17, -2.5dB */
+       0x2d4000b5,             /* 18, -3.0dB */
+       0x2ac000ab,             /* 19, -3.5dB */
+       0x288000a2,             /* 20, -4.0dB */
+       0x26000098,             /* 21, -4.5dB */
+       0x24000090,             /* 22, -5.0dB */
+       0x22000088,             /* 23, -5.5dB */
+       0x20000080,             /* 24, -6.0dB */
+       0x1e400079,             /* 25, -6.5dB */
+       0x1c800072,             /* 26, -7.0dB */
+       0x1b00006c,             /* 27. -7.5dB */
+       0x19800066,             /* 28, -8.0dB */
+       0x18000060,             /* 29, -8.5dB */
+       0x16c0005b,             /* 30, -9.0dB */
+       0x15800056,             /* 31, -9.5dB */
+       0x14400051,             /* 32, -10.0dB */
+       0x1300004c,             /* 33, -10.5dB */
+       0x12000048,             /* 34, -11.0dB */
+       0x11000044,             /* 35, -11.5dB */
+       0x10000040,             /* 36, -12.0dB */
+       0x0f00003c,             /* 37, -12.5dB */
+       0x0e400039,             /* 38, -13.0dB */
+       0x0d800036,             /* 39, -13.5dB */
+       0x0cc00033,             /* 40, -14.0dB */
+       0x0c000030,             /* 41, -14.5dB */
+       0x0b40002d,             /* 42, -15.0dB */
+};
+
+static const u8 cck_tbl_ch1_13[CCK_TABLE_SIZE][8] = {
+       {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04},       /* 0, +0dB */
+       {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04},       /* 1, -0.5dB */
+       {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03},       /* 2, -1.0dB */
+       {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03},       /* 3, -1.5dB */
+       {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03},       /* 4, -2.0dB */
+       {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03},       /* 5, -2.5dB */
+       {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03},       /* 6, -3.0dB */
+       {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03},       /* 7, -3.5dB */
+       {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02},       /* 8, -4.0dB */
+       {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02},       /* 9, -4.5dB */
+       {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02},       /* 10, -5.0dB */
+       {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02},       /* 11, -5.5dB */
+       {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02},       /* 12, -6.0dB */
+       {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02},       /* 13, -6.5dB */
+       {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02},       /* 14, -7.0dB */
+       {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02},       /* 15, -7.5dB */
+       {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01},       /* 16, -8.0dB */
+       {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02},       /* 17, -8.5dB */
+       {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01},       /* 18, -9.0dB */
+       {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},       /* 19, -9.5dB */
+       {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},       /* 20, -10.0dB*/
+       {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01},       /* 21, -10.5dB*/
+       {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01},       /* 22, -11.0dB*/
+       {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01},       /* 23, -11.5dB*/
+       {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01},       /* 24, -12.0dB*/
+       {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01},       /* 25, -12.5dB*/
+       {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01},       /* 26, -13.0dB*/
+       {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01},       /* 27, -13.5dB*/
+       {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01},       /* 28, -14.0dB*/
+       {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01},       /* 29, -14.5dB*/
+       {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01},       /* 30, -15.0dB*/
+       {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01},       /* 31, -15.5dB*/
+       {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01}        /* 32, -16.0dB*/
+};
+
+static const u8 cck_tbl_ch14[CCK_TABLE_SIZE][8] = {
+       {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00},       /* 0, +0dB */
+       {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00},       /* 1, -0.5dB */
+       {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00},       /* 2, -1.0dB */
+       {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00},       /* 3, -1.5dB */
+       {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00},       /* 4, -2.0dB */
+       {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00},       /* 5, -2.5dB */
+       {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00},       /* 6, -3.0dB */
+       {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00},       /* 7, -3.5dB */
+       {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00},       /* 8, -4.0dB */
+       {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00},       /* 9, -4.5dB */
+       {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00},       /* 10, -5.0dB */
+       {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00},       /* 11, -5.5dB */
+       {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00},       /* 12, -6.0dB */
+       {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00},       /* 13, -6.5dB */
+       {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00},       /* 14, -7.0dB */
+       {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00},       /* 15, -7.5dB */
+       {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00},       /* 16, -8.0dB */
+       {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00},       /* 17, -8.5dB */
+       {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00},       /* 18, -9.0dB */
+       {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},       /* 19, -9.5dB */
+       {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},       /* 20, -10.0dB*/
+       {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00},       /* 21, -10.5dB*/
+       {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00},       /* 22, -11.0dB*/
+       {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00},       /* 23, -11.5dB*/
+       {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00},       /* 24, -12.0dB*/
+       {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00},       /* 25, -12.5dB*/
+       {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00},       /* 26, -13.0dB*/
+       {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00},       /* 27, -13.5dB*/
+       {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00},       /* 28, -14.0dB*/
+       {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00},       /* 29, -14.5dB*/
+       {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00},       /* 30, -15.0dB*/
+       {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00},       /* 31, -15.5dB*/
+       {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00}        /* 32, -16.0dB*/
+};
+
+#define        CAL_SWING_OFF(_off, _dir, _size, _del)                          \
+       do {                                                            \
+               for (_off = 0; _off < _size; _off++) {                  \
+                       if (_del < thermal_threshold[_dir][_off]) {     \
+                               if (_off != 0)                          \
+                                       _off--;                         \
+                               break;                                  \
+                       }                                               \
+               }                                                       \
+               if (_off >= _size)                                      \
+                       _off = _size - 1;                               \
+       } while (0)
+
+static void rtl88e_set_iqk_matrix(struct ieee80211_hw *hw,
+                                 u8 ofdm_index, u8 rfpath,
+                                 long iqk_result_x, long iqk_result_y)
+{
+       long ele_a = 0, ele_d, ele_c = 0, value32;
+
+       ele_d = (ofdmswing_table[ofdm_index] & 0xFFC00000)>>22;
+
+       if (iqk_result_x != 0) {
+               if ((iqk_result_x & 0x00000200) != 0)
+                       iqk_result_x = iqk_result_x | 0xFFFFFC00;
+               ele_a = ((iqk_result_x * ele_d)>>8)&0x000003FF;
+
+               if ((iqk_result_y & 0x00000200) != 0)
+                       iqk_result_y = iqk_result_y | 0xFFFFFC00;
+               ele_c = ((iqk_result_y * ele_d)>>8)&0x000003FF;
+
+               switch (rfpath) {
+               case RF90_PATH_A:
+                       value32 = (ele_d << 22)|((ele_c & 0x3F)<<16) | ele_a;
+                       rtl_set_bbreg(hw, ROFDM0_XATXIQIMBAL, MASKDWORD,
+                                     value32);
+                       value32 = (ele_c & 0x000003C0) >> 6;
+                       rtl_set_bbreg(hw, ROFDM0_XCTXAFE, MASKH4BITS, value32);
+                       value32 = ((iqk_result_x * ele_d) >> 7) & 0x01;
+                       rtl_set_bbreg(hw, ROFDM0_ECCATHRES, BIT(24), value32);
+                       break;
+               case RF90_PATH_B:
+                       value32 = (ele_d << 22)|((ele_c & 0x3F)<<16) | ele_a;
+                       rtl_set_bbreg(hw, ROFDM0_XBTXIQIMBAL,
+                                     MASKDWORD, value32);
+                       value32 = (ele_c & 0x000003C0) >> 6;
+                       rtl_set_bbreg(hw, ROFDM0_XDTXAFE, MASKH4BITS, value32);
+                       value32 = ((iqk_result_x * ele_d) >> 7) & 0x01;
+                       rtl_set_bbreg(hw, ROFDM0_ECCATHRES, BIT(28), value32);
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               switch (rfpath) {
+               case RF90_PATH_A:
+                       rtl_set_bbreg(hw, ROFDM0_XATXIQIMBAL, MASKDWORD,
+                                     ofdmswing_table[ofdm_index]);
+                       rtl_set_bbreg(hw, ROFDM0_XCTXAFE, MASKH4BITS, 0x00);
+                       rtl_set_bbreg(hw, ROFDM0_ECCATHRES, BIT(24), 0x00);
+                       break;
+               case RF90_PATH_B:
+                       rtl_set_bbreg(hw, ROFDM0_XBTXIQIMBAL, MASKDWORD,
+                                     ofdmswing_table[ofdm_index]);
+                       rtl_set_bbreg(hw, ROFDM0_XDTXAFE, MASKH4BITS, 0x00);
+                       rtl_set_bbreg(hw, ROFDM0_ECCATHRES, BIT(28), 0x00);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+void rtl88e_dm_txpower_track_adjust(struct ieee80211_hw *hw,
+       u8 type, u8 *pdirection, u32 *poutwrite_val)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       u8 pwr_val = 0;
+       u8 cck_base = rtldm->swing_idx_cck_base;
+       u8 cck_val = rtldm->swing_idx_cck;
+       u8 ofdm_base = rtldm->swing_idx_ofdm_base;
+       u8 ofdm_val = rtlpriv->dm.swing_idx_ofdm[RF90_PATH_A];
+
+       if (type == 0) {
+               if (ofdm_val <= ofdm_base) {
+                       *pdirection = 1;
+                       pwr_val = ofdm_base - ofdm_val;
+               } else {
+                       *pdirection = 2;
+                       pwr_val = ofdm_val - ofdm_base;
+               }
+       } else if (type == 1) {
+               if (cck_val <= cck_base) {
+                       *pdirection = 1;
+                       pwr_val = cck_base - cck_val;
+               } else {
+                       *pdirection = 2;
+                       pwr_val = cck_val - cck_base;
+               }
+       }
+
+       if (pwr_val >= TXPWRTRACK_MAX_IDX && (*pdirection == 1))
+               pwr_val = TXPWRTRACK_MAX_IDX;
+
+       *poutwrite_val = pwr_val | (pwr_val << 8) | (pwr_val << 16) |
+                        (pwr_val << 24);
+}
+
+
+static void rtl88e_chk_tx_track(struct ieee80211_hw *hw,
+                               enum pwr_track_control_method method,
+                               u8 rfpath, u8 index)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       int jj = rtldm->swing_idx_cck;
+       int i;
+
+       if (method == TXAGC) {
+               if (rtldm->swing_flag_ofdm == true ||
+                   rtldm->swing_flag_cck == true) {
+                       u8 chan = rtlphy->current_channel;
+                       rtl88e_phy_set_txpower_level(hw, chan);
+                       rtldm->swing_flag_ofdm = false;
+                       rtldm->swing_flag_cck = false;
+               }
+       } else if (method == BBSWING) {
+               if (!rtldm->cck_inch14) {
+                       for (i = 0; i < 8; i++)
+                               rtl_write_byte(rtlpriv, 0xa22 + i,
+                                              cck_tbl_ch1_13[jj][i]);
+               } else {
+                       for (i = 0; i < 8; i++)
+                               rtl_write_byte(rtlpriv, 0xa22 + i,
+                                              cck_tbl_ch14[jj][i]);
+               }
+
+               if (rfpath == RF90_PATH_A) {
+                       long x = rtlphy->iqk_matrix[index].value[0][0];
+                       long y = rtlphy->iqk_matrix[index].value[0][1];
+                       u8 indx = rtldm->swing_idx_ofdm[rfpath];
+                       rtl88e_set_iqk_matrix(hw, indx, rfpath, x, y);
+               } else if (rfpath == RF90_PATH_B) {
+                       u8 indx = rtldm->swing_idx_ofdm[rfpath];
+                       long x = rtlphy->iqk_matrix[indx].value[0][4];
+                       long y = rtlphy->iqk_matrix[indx].value[0][5];
+                       rtl88e_set_iqk_matrix(hw, indx, rfpath, x, y);
+               }
+       } else {
+               return;
+       }
+}
+
+static void rtl88e_dm_diginit(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct dig_t *dm_dig = &rtlpriv->dm_digtable;
+
+       dm_dig->dig_enable_flag = true;
+       dm_dig->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f);
+       dm_dig->pre_igvalue = 0;
+       dm_dig->cursta_cstate = DIG_STA_DISCONNECT;
+       dm_dig->presta_cstate = DIG_STA_DISCONNECT;
+       dm_dig->curmultista_cstate = DIG_MULTISTA_DISCONNECT;
+       dm_dig->rssi_lowthresh = DM_DIG_THRESH_LOW;
+       dm_dig->rssi_highthresh = DM_DIG_THRESH_HIGH;
+       dm_dig->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
+       dm_dig->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
+       dm_dig->rx_gain_max = DM_DIG_MAX;
+       dm_dig->rx_gain_min = DM_DIG_MIN;
+       dm_dig->back_val = DM_DIG_BACKOFF_DEFAULT;
+       dm_dig->back_range_max = DM_DIG_BACKOFF_MAX;
+       dm_dig->back_range_min = DM_DIG_BACKOFF_MIN;
+       dm_dig->pre_cck_cca_thres = 0xff;
+       dm_dig->cur_cck_cca_thres = 0x83;
+       dm_dig->forbidden_igi = DM_DIG_MIN;
+       dm_dig->large_fa_hit = 0;
+       dm_dig->recover_cnt = 0;
+       dm_dig->dig_min_0 = 0x25;
+       dm_dig->dig_min_1 = 0x25;
+       dm_dig->media_connect_0 = false;
+       dm_dig->media_connect_1 = false;
+       rtlpriv->dm.dm_initialgain_enable = true;
+}
+
+static u8 rtl88e_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct dig_t *dm_dig = &rtlpriv->dm_digtable;
+       long rssi_val_min = 0;
+
+       if ((dm_dig->curmultista_cstate == DIG_MULTISTA_CONNECT) &&
+           (dm_dig->cursta_cstate == DIG_STA_CONNECT)) {
+               if (rtlpriv->dm.entry_min_undec_sm_pwdb != 0)
+                       rssi_val_min =
+                           (rtlpriv->dm.entry_min_undec_sm_pwdb >
+                           rtlpriv->dm.undec_sm_pwdb) ?
+                           rtlpriv->dm.undec_sm_pwdb :
+                           rtlpriv->dm.entry_min_undec_sm_pwdb;
+               else
+                       rssi_val_min = rtlpriv->dm.undec_sm_pwdb;
+       } else if (dm_dig->cursta_cstate == DIG_STA_CONNECT ||
+                  dm_dig->cursta_cstate == DIG_STA_BEFORE_CONNECT) {
+               rssi_val_min = rtlpriv->dm.undec_sm_pwdb;
+       } else if (dm_dig->curmultista_cstate ==
+               DIG_MULTISTA_CONNECT) {
+               rssi_val_min = rtlpriv->dm.entry_min_undec_sm_pwdb;
+       }
+       return (u8)rssi_val_min;
+}
+
+static void rtl88e_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw)
+{
+       u32 ret_value;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct false_alarm_statistics *alm_cnt = &(rtlpriv->falsealm_cnt);
+
+       rtl_set_bbreg(hw, ROFDM0_LSTF, BIT(31), 1);
+       rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(31), 1);
+
+       ret_value = rtl_get_bbreg(hw, ROFDM0_FRAMESYNC, MASKDWORD);
+       alm_cnt->cnt_fast_fsync_fail = (ret_value&0xffff);
+       alm_cnt->cnt_sb_search_fail = ((ret_value&0xffff0000)>>16);
+
+       ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, MASKDWORD);
+       alm_cnt->cnt_ofdm_cca = (ret_value&0xffff);
+       alm_cnt->cnt_parity_fail = ((ret_value & 0xffff0000) >> 16);
+
+       ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, MASKDWORD);
+       alm_cnt->cnt_rate_illegal = (ret_value & 0xffff);
+       alm_cnt->cnt_crc8_fail = ((ret_value & 0xffff0000) >> 16);
+
+       ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, MASKDWORD);
+       alm_cnt->cnt_mcs_fail = (ret_value & 0xffff);
+       alm_cnt->cnt_ofdm_fail = alm_cnt->cnt_parity_fail +
+                                alm_cnt->cnt_rate_illegal +
+                                alm_cnt->cnt_crc8_fail +
+                                alm_cnt->cnt_mcs_fail +
+                                alm_cnt->cnt_fast_fsync_fail +
+                                alm_cnt->cnt_sb_search_fail;
+
+       ret_value = rtl_get_bbreg(hw, REG_SC_CNT, MASKDWORD);
+       alm_cnt->cnt_bw_lsc = (ret_value & 0xffff);
+       alm_cnt->cnt_bw_usc = ((ret_value & 0xffff0000) >> 16);
+
+       rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(12), 1);
+       rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(14), 1);
+
+       ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, MASKBYTE0);
+       alm_cnt->cnt_cck_fail = ret_value;
+
+       ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, MASKBYTE3);
+       alm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8;
+
+       ret_value = rtl_get_bbreg(hw, RCCK0_CCA_CNT, MASKDWORD);
+       alm_cnt->cnt_cck_cca = ((ret_value & 0xff) << 8) |
+                               ((ret_value&0xFF00)>>8);
+
+       alm_cnt->cnt_all = alm_cnt->cnt_fast_fsync_fail +
+                          alm_cnt->cnt_sb_search_fail +
+                          alm_cnt->cnt_parity_fail +
+                          alm_cnt->cnt_rate_illegal +
+                          alm_cnt->cnt_crc8_fail +
+                          alm_cnt->cnt_mcs_fail +
+                          alm_cnt->cnt_cck_fail;
+       alm_cnt->cnt_cca_all = alm_cnt->cnt_ofdm_cca + alm_cnt->cnt_cck_cca;
+
+       rtl_set_bbreg(hw, ROFDM0_TRSWISOLATION, BIT(31), 1);
+       rtl_set_bbreg(hw, ROFDM0_TRSWISOLATION, BIT(31), 0);
+       rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(27), 1);
+       rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(27), 0);
+       rtl_set_bbreg(hw, ROFDM0_LSTF, BIT(31), 0);
+       rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(31), 0);
+       rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(13)|BIT(12), 0);
+       rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(13)|BIT(12), 2);
+       rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(15)|BIT(14), 0);
+       rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(15)|BIT(14), 2);
+
+       RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE,
+                "cnt_parity_fail = %d, cnt_rate_illegal = %d, "
+                "cnt_crc8_fail = %d, cnt_mcs_fail = %d\n",
+                alm_cnt->cnt_parity_fail,
+                alm_cnt->cnt_rate_illegal,
+                alm_cnt->cnt_crc8_fail, alm_cnt->cnt_mcs_fail);
+
+       RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE,
+                "cnt_ofdm_fail = %x, cnt_cck_fail = %x, cnt_all = %x\n",
+                alm_cnt->cnt_ofdm_fail,
+                alm_cnt->cnt_cck_fail, alm_cnt->cnt_all);
+}
+
+static void rtl88e_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct dig_t *dm_dig = &rtlpriv->dm_digtable;
+       u8 cur_cck_cca_thresh;
+
+       if (dm_dig->cursta_cstate == DIG_STA_CONNECT) {
+               dm_dig->rssi_val_min = rtl88e_dm_initial_gain_min_pwdb(hw);
+               if (dm_dig->rssi_val_min > 25) {
+                       cur_cck_cca_thresh = 0xcd;
+               } else if ((dm_dig->rssi_val_min <= 25) &&
+                          (dm_dig->rssi_val_min > 10)) {
+                       cur_cck_cca_thresh = 0x83;
+               } else {
+                       if (rtlpriv->falsealm_cnt.cnt_cck_fail > 1000)
+                               cur_cck_cca_thresh = 0x83;
+                       else
+                               cur_cck_cca_thresh = 0x40;
+               }
+
+       } else {
+               if (rtlpriv->falsealm_cnt.cnt_cck_fail > 1000)
+                       cur_cck_cca_thresh = 0x83;
+               else
+                       cur_cck_cca_thresh = 0x40;
+       }
+
+       if (dm_dig->cur_cck_cca_thres != cur_cck_cca_thresh)
+               rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, cur_cck_cca_thresh);
+
+       dm_dig->cur_cck_cca_thres = cur_cck_cca_thresh;
+       dm_dig->pre_cck_cca_thres = dm_dig->cur_cck_cca_thres;
+       RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE,
+                "CCK cca thresh hold =%x\n", dm_dig->cur_cck_cca_thres);
+}
+
+static void rtl88e_dm_dig(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct dig_t *dm_dig = &rtlpriv->dm_digtable;
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       u8 dig_min, dig_maxofmin;
+       bool bfirstconnect;
+       u8 dm_dig_max, dm_dig_min;
+       u8 current_igi = dm_dig->cur_igvalue;
+
+       if (rtlpriv->dm.dm_initialgain_enable == false)
+               return;
+       if (dm_dig->dig_enable_flag == false)
+               return;
+       if (mac->act_scanning == true)
+               return;
+
+       if (mac->link_state >= MAC80211_LINKED)
+               dm_dig->cursta_cstate = DIG_STA_CONNECT;
+       else
+               dm_dig->cursta_cstate = DIG_STA_DISCONNECT;
+       if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_AP ||
+           rtlpriv->mac80211.opmode == NL80211_IFTYPE_ADHOC)
+               dm_dig->cursta_cstate = DIG_STA_DISCONNECT;
+
+       dm_dig_max = DM_DIG_MAX;
+       dm_dig_min = DM_DIG_MIN;
+       dig_maxofmin = DM_DIG_MAX_AP;
+       dig_min = dm_dig->dig_min_0;
+       bfirstconnect = ((mac->link_state >= MAC80211_LINKED) ? true : false) &&
+                        (dm_dig->media_connect_0 == false);
+
+       dm_dig->rssi_val_min =
+               rtl88e_dm_initial_gain_min_pwdb(hw);
+
+       if (mac->link_state >= MAC80211_LINKED) {
+               if ((dm_dig->rssi_val_min + 20) > dm_dig_max)
+                       dm_dig->rx_gain_max = dm_dig_max;
+               else if ((dm_dig->rssi_val_min + 20) < dm_dig_min)
+                       dm_dig->rx_gain_max = dm_dig_min;
+               else
+                       dm_dig->rx_gain_max = dm_dig->rssi_val_min + 20;
+
+               if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) {
+                       dig_min  = dm_dig->antdiv_rssi_max;
+               } else {
+                       if (dm_dig->rssi_val_min < dm_dig_min)
+                               dig_min = dm_dig_min;
+                       else if (dm_dig->rssi_val_min < dig_maxofmin)
+                               dig_min = dig_maxofmin;
+                       else
+                               dig_min = dm_dig->rssi_val_min;
+               }
+       } else {
+               dm_dig->rx_gain_max = dm_dig_max;
+               dig_min = dm_dig_min;
+               RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n");
+       }
+
+       if (rtlpriv->falsealm_cnt.cnt_all > 10000) {
+               dm_dig->large_fa_hit++;
+               if (dm_dig->forbidden_igi < current_igi) {
+                       dm_dig->forbidden_igi = current_igi;
+                       dm_dig->large_fa_hit = 1;
+               }
+
+               if (dm_dig->large_fa_hit >= 3) {
+                       if ((dm_dig->forbidden_igi + 1) > dm_dig->rx_gain_max)
+                               dm_dig->rx_gain_min = dm_dig->rx_gain_max;
+                       else
+                               dm_dig->rx_gain_min = dm_dig->forbidden_igi + 1;
+                       dm_dig->recover_cnt = 3600;
+               }
+       } else {
+               if (dm_dig->recover_cnt != 0) {
+                       dm_dig->recover_cnt--;
+               } else {
+                       if (dm_dig->large_fa_hit == 0) {
+                               if ((dm_dig->forbidden_igi - 1) < dig_min) {
+                                       dm_dig->forbidden_igi = dig_min;
+                                       dm_dig->rx_gain_min = dig_min;
+                               } else {
+                                       dm_dig->forbidden_igi--;
+                                       dm_dig->rx_gain_min =
+                                                dm_dig->forbidden_igi + 1;
+                               }
+                       } else if (dm_dig->large_fa_hit == 3) {
+                               dm_dig->large_fa_hit = 0;
+                       }
+               }
+       }
+
+       if (dm_dig->cursta_cstate == DIG_STA_CONNECT) {
+               if (bfirstconnect) {
+                       current_igi = dm_dig->rssi_val_min;
+               } else {
+                       if (rtlpriv->falsealm_cnt.cnt_all > DM_DIG_FA_TH2)
+                               current_igi += 2;
+                       else if (rtlpriv->falsealm_cnt.cnt_all > DM_DIG_FA_TH1)
+                               current_igi++;
+                       else if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH0)
+                               current_igi--;
+               }
+       } else {
+               if (rtlpriv->falsealm_cnt.cnt_all > 10000)
+                       current_igi += 2;
+               else if (rtlpriv->falsealm_cnt.cnt_all > 8000)
+                       current_igi++;
+               else if (rtlpriv->falsealm_cnt.cnt_all < 500)
+                       current_igi--;
+       }
+
+       if (current_igi > DM_DIG_FA_UPPER)
+               current_igi = DM_DIG_FA_UPPER;
+       else if (current_igi < DM_DIG_FA_LOWER)
+               current_igi = DM_DIG_FA_LOWER;
+
+       if (rtlpriv->falsealm_cnt.cnt_all > 10000)
+               current_igi = DM_DIG_FA_UPPER;
+
+       dm_dig->cur_igvalue = current_igi;
+       rtl88e_dm_write_dig(hw);
+       dm_dig->media_connect_0 = ((mac->link_state >= MAC80211_LINKED) ?
+                                   true : false);
+       dm_dig->dig_min_0 = dig_min;
+
+       rtl88e_dm_cck_packet_detection_thresh(hw);
+}
+
+static void rtl88e_dm_init_dynamic_txpower(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       rtlpriv->dm.dynamic_txpower_enable = false;
+
+       rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL;
+       rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
+}
+
+static void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       long undec_sm_pwdb;
+
+       if (!rtlpriv->dm.dynamic_txpower_enable)
+               return;
+
+       if (rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) {
+               rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
+               return;
+       }
+
+       if ((mac->link_state < MAC80211_LINKED) &&
+           (rtlpriv->dm.entry_min_undec_sm_pwdb == 0)) {
+               RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE,
+                        "Not connected\n");
+
+               rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
+
+               rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL;
+               return;
+       }
+
+       if (mac->link_state >= MAC80211_LINKED) {
+               if (mac->opmode == NL80211_IFTYPE_ADHOC) {
+                       undec_sm_pwdb =
+                           rtlpriv->dm.entry_min_undec_sm_pwdb;
+                       RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                                "AP Client PWDB = 0x%lx\n",
+                                 undec_sm_pwdb);
+               } else {
+                       undec_sm_pwdb =
+                           rtlpriv->dm.undec_sm_pwdb;
+                       RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                                "STA Default Port PWDB = 0x%lx\n",
+                                 undec_sm_pwdb);
+               }
+       } else {
+               undec_sm_pwdb = rtlpriv->dm.entry_min_undec_sm_pwdb;
+
+               RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                        "AP Ext Port PWDB = 0x%lx\n", undec_sm_pwdb);
+       }
+
+       if (undec_sm_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL2) {
+               rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1;
+               RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                        "TXHIGHPWRLEVEL_LEVEL1 (TxPwr = 0x0)\n");
+       } else if ((undec_sm_pwdb <
+                   (TX_POWER_NEAR_FIELD_THRESH_LVL2 - 3)) &&
+                  (undec_sm_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL1)) {
+               rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1;
+               RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                        "TXHIGHPWRLEVEL_LEVEL1 (TxPwr = 0x10)\n");
+       } else if (undec_sm_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL1 - 5)) {
+               rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
+               RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                        "TXHIGHPWRLEVEL_NORMAL\n");
+       }
+
+       if ((rtlpriv->dm.dynamic_txhighpower_lvl != rtlpriv->dm.last_dtp_lvl)) {
+               RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                        "PHY_SetTxPowerLevel8192S() Channel = %d\n",
+                         rtlphy->current_channel);
+               rtl88e_phy_set_txpower_level(hw, rtlphy->current_channel);
+       }
+
+       rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl;
+}
+
+void rtl88e_dm_write_dig(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct dig_t *dm_dig = &rtlpriv->dm_digtable;
+
+       RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+                "cur_igvalue = 0x%x, "
+                 "pre_igvalue = 0x%x, back_val = %d\n",
+                 dm_dig->cur_igvalue, dm_dig->pre_igvalue,
+                 dm_dig->back_val);
+
+       if (dm_dig->cur_igvalue > 0x3f)
+               dm_dig->cur_igvalue = 0x3f;
+       if (dm_dig->pre_igvalue != dm_dig->cur_igvalue) {
+               rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f,
+                             dm_dig->cur_igvalue);
+
+               dm_dig->pre_igvalue = dm_dig->cur_igvalue;
+       }
+}
+
+static void rtl88e_dm_pwdb_monitor(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_sta_info *drv_priv;
+       static u64 last_txok;
+       static u64 last_rx;
+       long tmp_entry_max_pwdb = 0, tmp_entry_min_pwdb = 0xff;
+
+       if (rtlhal->oem_id == RT_CID_819x_HP) {
+               u64 cur_txok_cnt = 0;
+               u64 cur_rxok_cnt = 0;
+               cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok;
+               cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - last_rx;
+               last_txok = cur_txok_cnt;
+               last_rx = cur_rxok_cnt;
+
+               if (cur_rxok_cnt > (cur_txok_cnt * 6))
+                       rtl_write_dword(rtlpriv, REG_ARFR0, 0x8f015);
+               else
+                       rtl_write_dword(rtlpriv, REG_ARFR0, 0xff015);
+       }
+
+       /* AP & ADHOC & MESH */
+       spin_lock_bh(&rtlpriv->locks.entry_list_lock);
+       list_for_each_entry(drv_priv, &rtlpriv->entry_list, list) {
+               if (drv_priv->rssi_stat.undec_sm_pwdb < tmp_entry_min_pwdb)
+                       tmp_entry_min_pwdb = drv_priv->rssi_stat.undec_sm_pwdb;
+               if (drv_priv->rssi_stat.undec_sm_pwdb > tmp_entry_max_pwdb)
+                       tmp_entry_max_pwdb = drv_priv->rssi_stat.undec_sm_pwdb;
+       }
+       spin_unlock_bh(&rtlpriv->locks.entry_list_lock);
+
+       /* If associated entry is found */
+       if (tmp_entry_max_pwdb != 0) {
+               rtlpriv->dm.entry_max_undec_sm_pwdb = tmp_entry_max_pwdb;
+               RTPRINT(rtlpriv, FDM, DM_PWDB, "EntryMaxPWDB = 0x%lx(%ld)\n",
+                       tmp_entry_max_pwdb, tmp_entry_max_pwdb);
+       } else {
+               rtlpriv->dm.entry_max_undec_sm_pwdb = 0;
+       }
+       /* If associated entry is found */
+       if (tmp_entry_min_pwdb != 0xff) {
+               rtlpriv->dm.entry_min_undec_sm_pwdb = tmp_entry_min_pwdb;
+               RTPRINT(rtlpriv, FDM, DM_PWDB, "EntryMinPWDB = 0x%lx(%ld)\n",
+                       tmp_entry_min_pwdb, tmp_entry_min_pwdb);
+       } else {
+               rtlpriv->dm.entry_min_undec_sm_pwdb = 0;
+       }
+       /* Indicate Rx signal strength to FW. */
+       if (!rtlpriv->dm.useramask)
+               rtl_write_byte(rtlpriv, 0x4fe, rtlpriv->dm.undec_sm_pwdb);
+}
+
+void rtl88e_dm_init_edca_turbo(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       rtlpriv->dm.current_turbo_edca = false;
+       rtlpriv->dm.is_any_nonbepkts = false;
+       rtlpriv->dm.is_cur_rdlstate = false;
+}
+
+static void rtl88e_dm_check_edca_turbo(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       static u64 last_txok_cnt;
+       static u64 last_rxok_cnt;
+       static u32 last_bt_edca_ul;
+       static u32 last_bt_edca_dl;
+       u64 cur_txok_cnt = 0;
+       u64 cur_rxok_cnt = 0;
+       u32 edca_be_ul = 0x5ea42b;
+       u32 edca_be_dl = 0x5ea42b;
+       bool change_edca = false;
+
+       if ((last_bt_edca_ul != rtlpcipriv->bt_coexist.bt_edca_ul) ||
+           (last_bt_edca_dl != rtlpcipriv->bt_coexist.bt_edca_dl)) {
+               rtlpriv->dm.current_turbo_edca = false;
+               last_bt_edca_ul = rtlpcipriv->bt_coexist.bt_edca_ul;
+               last_bt_edca_dl = rtlpcipriv->bt_coexist.bt_edca_dl;
+       }
+
+       if (rtlpcipriv->bt_coexist.bt_edca_ul != 0) {
+               edca_be_ul = rtlpcipriv->bt_coexist.bt_edca_ul;
+               change_edca = true;
+       }
+
+       if (rtlpcipriv->bt_coexist.bt_edca_dl != 0) {
+               edca_be_ul = rtlpcipriv->bt_coexist.bt_edca_dl;
+               change_edca = true;
+       }
+
+       if (mac->link_state != MAC80211_LINKED) {
+               rtlpriv->dm.current_turbo_edca = false;
+               return;
+       }
+
+       if ((!mac->ht_enable) && (!rtlpcipriv->bt_coexist.bt_coexistence)) {
+               if (!(edca_be_ul & 0xffff0000))
+                       edca_be_ul |= 0x005e0000;
+
+               if (!(edca_be_dl & 0xffff0000))
+                       edca_be_dl |= 0x005e0000;
+       }
+
+       if ((change_edca) || ((!rtlpriv->dm.is_any_nonbepkts) &&
+                             (!rtlpriv->dm.disable_framebursting))) {
+               cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok_cnt;
+               cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - last_rxok_cnt;
+
+               if (cur_rxok_cnt > 4 * cur_txok_cnt) {
+                       if (!rtlpriv->dm.is_cur_rdlstate ||
+                           !rtlpriv->dm.current_turbo_edca) {
+                               rtl_write_dword(rtlpriv,
+                                               REG_EDCA_BE_PARAM,
+                                               edca_be_dl);
+                               rtlpriv->dm.is_cur_rdlstate = true;
+                       }
+               } else {
+                       if (rtlpriv->dm.is_cur_rdlstate ||
+                           !rtlpriv->dm.current_turbo_edca) {
+                               rtl_write_dword(rtlpriv,
+                                               REG_EDCA_BE_PARAM,
+                                               edca_be_ul);
+                               rtlpriv->dm.is_cur_rdlstate = false;
+                       }
+               }
+               rtlpriv->dm.current_turbo_edca = true;
+       } else {
+               if (rtlpriv->dm.current_turbo_edca) {
+                       u8 tmp = AC0_BE;
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                                     HW_VAR_AC_PARAM,
+                                                     (u8 *)(&tmp));
+                       rtlpriv->dm.current_turbo_edca = false;
+               }
+       }
+
+       rtlpriv->dm.is_any_nonbepkts = false;
+       last_txok_cnt = rtlpriv->stats.txbytesunicast;
+       last_rxok_cnt = rtlpriv->stats.rxbytesunicast;
+}
+
+static void rtl88e_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw
+                                                            *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_dm   *rtldm = rtl_dm(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u8 thermalvalue = 0, delta, delta_lck, delta_iqk, off;
+       u8 th_avg_cnt = 0;
+       u32 thermalvalue_avg = 0;
+       long  ele_d, temp_cck;
+       char ofdm_index[2], cck_index = 0, ofdm_old[2] = {0, 0}, cck_old = 0;
+       int i = 0;
+       bool is2t = false;
+
+       u8 ofdm_min_index = 6, rf = (is2t) ? 2 : 1;
+       u8 index_for_channel;
+       enum _dec_inc {dec, power_inc};
+
+       /* 0.1 the following TWO tables decide the final index of
+        * OFDM/CCK swing table
+        */
+       char del_tbl_idx[2][15] = {
+               {0, 0, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11},
+               {0, 0, -1, -2, -3, -4, -4, -4, -4, -5, -7, -8, -9, -9, -10}
+       };
+       u8 thermal_threshold[2][15] = {
+               {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 27},
+               {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 25, 25, 25}
+       };
+
+       /*Initilization (7 steps in total) */
+       rtlpriv->dm.txpower_trackinginit = true;
+       RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                "rtl88e_dm_txpower_tracking_callback_thermalmeter\n");
+
+       thermalvalue = (u8) rtl_get_rfreg(hw, RF90_PATH_A, RF_T_METER, 0xfc00);
+       if (!thermalvalue)
+               return;
+       RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                "Readback Thermal Meter = 0x%x pre thermal meter 0x%x eeprom_thermalmeter 0x%x\n",
+                thermalvalue, rtlpriv->dm.thermalvalue,
+                rtlefuse->eeprom_thermalmeter);
+
+       /*1. Query OFDM Default Setting: Path A*/
+       ele_d = rtl_get_bbreg(hw, ROFDM0_XATXIQIMBAL, MASKDWORD) & MASKOFDM_D;
+       for (i = 0; i < OFDM_TABLE_LENGTH; i++) {
+               if (ele_d == (ofdmswing_table[i] & MASKOFDM_D)) {
+                       ofdm_old[0] = (u8) i;
+                       rtldm->swing_idx_ofdm_base = (u8)i;
+                       RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                                "Initial pathA ele_d reg0x%x = 0x%lx, ofdm_index = 0x%x\n",
+                                ROFDM0_XATXIQIMBAL,
+                                ele_d, ofdm_old[0]);
+                       break;
+               }
+       }
+
+       if (is2t) {
+               ele_d = rtl_get_bbreg(hw, ROFDM0_XBTXIQIMBAL,
+                                     MASKDWORD) & MASKOFDM_D;
+               for (i = 0; i < OFDM_TABLE_LENGTH; i++) {
+                       if (ele_d == (ofdmswing_table[i] & MASKOFDM_D)) {
+                               ofdm_old[1] = (u8)i;
+
+                               RT_TRACE(rtlpriv, COMP_POWER_TRACKING,
+                                        DBG_LOUD,
+                                        "Initial pathB ele_d reg0x%x = 0x%lx, ofdm_index = 0x%x\n",
+                                        ROFDM0_XBTXIQIMBAL, ele_d,
+                                        ofdm_old[1]);
+                               break;
+                       }
+               }
+       }
+       /*2.Query CCK default setting From 0xa24*/
+       temp_cck = rtl_get_bbreg(hw, RCCK0_TXFILTER2, MASKDWORD) & MASKCCK;
+       for (i = 0; i < CCK_TABLE_LENGTH; i++) {
+               if (rtlpriv->dm.cck_inch14) {
+                       if (memcmp(&temp_cck, &cck_tbl_ch14[i][2], 4) == 0) {
+                               cck_old = (u8)i;
+                               rtldm->swing_idx_cck_base = (u8)i;
+                               RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                                        "Initial reg0x%x = 0x%lx, cck_index = 0x%x, ch 14 %d\n",
+                                        RCCK0_TXFILTER2, temp_cck, cck_old,
+                                        rtlpriv->dm.cck_inch14);
+                               break;
+                       }
+               } else {
+                       if (memcmp(&temp_cck, &cck_tbl_ch1_13[i][2], 4) == 0) {
+                               cck_old = (u8)i;
+                               rtldm->swing_idx_cck_base = (u8)i;
+                               RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                                        "Initial reg0x%x = 0x%lx, cck_index = 0x%x, ch14 %d\n",
+                                        RCCK0_TXFILTER2, temp_cck, cck_old,
+                                        rtlpriv->dm.cck_inch14);
+                               break;
+                       }
+               }
+       }
+
+       /*3 Initialize ThermalValues of RFCalibrateInfo*/
+       if (!rtldm->thermalvalue) {
+               rtlpriv->dm.thermalvalue = rtlefuse->eeprom_thermalmeter;
+               rtlpriv->dm.thermalvalue_lck = thermalvalue;
+               rtlpriv->dm.thermalvalue_iqk = thermalvalue;
+               for (i = 0; i < rf; i++)
+                       rtlpriv->dm.ofdm_index[i] = ofdm_old[i];
+               rtlpriv->dm.cck_index = cck_old;
+       }
+
+       /*4 Calculate average thermal meter*/
+       rtldm->thermalvalue_avg[rtldm->thermalvalue_avg_index] = thermalvalue;
+       rtldm->thermalvalue_avg_index++;
+       if (rtldm->thermalvalue_avg_index == AVG_THERMAL_NUM_88E)
+               rtldm->thermalvalue_avg_index = 0;
+
+       for (i = 0; i < AVG_THERMAL_NUM_88E; i++) {
+               if (rtldm->thermalvalue_avg[i]) {
+                       thermalvalue_avg += rtldm->thermalvalue_avg[i];
+                       th_avg_cnt++;
+               }
+       }
+
+       if (th_avg_cnt)
+               thermalvalue = (u8)(thermalvalue_avg / th_avg_cnt);
+
+       /* 5 Calculate delta, delta_LCK, delta_IQK.*/
+       if (rtlhal->reloadtxpowerindex) {
+               delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ?
+                   (thermalvalue - rtlefuse->eeprom_thermalmeter) :
+                   (rtlefuse->eeprom_thermalmeter - thermalvalue);
+               rtlhal->reloadtxpowerindex = false;
+               rtlpriv->dm.done_txpower = false;
+       } else if (rtlpriv->dm.done_txpower) {
+               delta = (thermalvalue > rtlpriv->dm.thermalvalue) ?
+                       (thermalvalue - rtlpriv->dm.thermalvalue) :
+                       (rtlpriv->dm.thermalvalue - thermalvalue);
+       } else {
+               delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ?
+                       (thermalvalue - rtlefuse->eeprom_thermalmeter) :
+                       (rtlefuse->eeprom_thermalmeter - thermalvalue);
+       }
+       delta_lck = (thermalvalue > rtlpriv->dm.thermalvalue_lck) ?
+                   (thermalvalue - rtlpriv->dm.thermalvalue_lck) :
+                   (rtlpriv->dm.thermalvalue_lck - thermalvalue);
+       delta_iqk = (thermalvalue > rtlpriv->dm.thermalvalue_iqk) ?
+                   (thermalvalue - rtlpriv->dm.thermalvalue_iqk) :
+                   (rtlpriv->dm.thermalvalue_iqk - thermalvalue);
+
+       RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                "Readback Thermal Meter = 0x%x pre thermal meter 0x%x "
+                "eeprom_thermalmeter 0x%x delta 0x%x "
+                "delta_lck 0x%x delta_iqk 0x%x\n",
+                thermalvalue, rtlpriv->dm.thermalvalue,
+                rtlefuse->eeprom_thermalmeter, delta, delta_lck,
+                delta_iqk);
+       /* 6 If necessary, do LCK.*/
+       if (delta_lck >= 8) {
+               rtlpriv->dm.thermalvalue_lck = thermalvalue;
+               rtl88e_phy_lc_calibrate(hw);
+       }
+
+       /* 7 If necessary, move the index of swing table to adjust Tx power. */
+       if (delta > 0 && rtlpriv->dm.txpower_track_control) {
+               delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ?
+                       (thermalvalue - rtlefuse->eeprom_thermalmeter) :
+                       (rtlefuse->eeprom_thermalmeter - thermalvalue);
+
+               /* 7.1 Get the final CCK_index and OFDM_index for each
+                * swing table.
+                */
+               if (thermalvalue > rtlefuse->eeprom_thermalmeter) {
+                       CAL_SWING_OFF(off, power_inc, IDX_MAP, delta);
+                       for (i = 0; i < rf; i++)
+                               ofdm_index[i] = rtldm->ofdm_index[i] +
+                                               del_tbl_idx[power_inc][off];
+                       cck_index = rtldm->cck_index +
+                                   del_tbl_idx[power_inc][off];
+               } else {
+                       CAL_SWING_OFF(off, dec, IDX_MAP, delta);
+                       for (i = 0; i < rf; i++)
+                               ofdm_index[i] = rtldm->ofdm_index[i] +
+                                               del_tbl_idx[dec][off];
+                       cck_index = rtldm->cck_index + del_tbl_idx[dec][off];
+               }
+
+               /* 7.2 Handle boundary conditions of index.*/
+               for (i = 0; i < rf; i++) {
+                       if (ofdm_index[i] > OFDM_TABLE_SIZE-1)
+                               ofdm_index[i] = OFDM_TABLE_SIZE-1;
+                       else if (rtldm->ofdm_index[i] < ofdm_min_index)
+                               ofdm_index[i] = ofdm_min_index;
+               }
+
+               if (cck_index > CCK_TABLE_SIZE - 1)
+                       cck_index = CCK_TABLE_SIZE - 1;
+               else if (cck_index < 0)
+                       cck_index = 0;
+
+               /*7.3Configure the Swing Table to adjust Tx Power.*/
+               if (rtlpriv->dm.txpower_track_control) {
+                       rtldm->done_txpower = true;
+                       rtldm->swing_idx_ofdm[RF90_PATH_A] =
+                                (u8)ofdm_index[RF90_PATH_A];
+                       if (is2t)
+                               rtldm->swing_idx_ofdm[RF90_PATH_B] =
+                                        (u8)ofdm_index[RF90_PATH_B];
+                       rtldm->swing_idx_cck = cck_index;
+                       if (rtldm->swing_idx_ofdm_cur !=
+                           rtldm->swing_idx_ofdm[0]) {
+                               rtldm->swing_idx_ofdm_cur =
+                                        rtldm->swing_idx_ofdm[0];
+                               rtldm->swing_flag_ofdm = true;
+                       }
+
+                       if (rtldm->swing_idx_cck != rtldm->swing_idx_cck) {
+                               rtldm->swing_idx_cck_cur = rtldm->swing_idx_cck;
+                               rtldm->swing_flag_cck = true;
+                       }
+
+                       rtl88e_chk_tx_track(hw, TXAGC, 0, 0);
+
+                       if (is2t)
+                               rtl88e_chk_tx_track(hw, BBSWING,
+                                                   RF90_PATH_B,
+                                                   index_for_channel);
+               }
+       }
+
+       if (delta_iqk >= 8) {
+               rtlpriv->dm.thermalvalue_iqk = thermalvalue;
+               rtl88e_phy_iq_calibrate(hw, false);
+       }
+
+       if (rtldm->txpower_track_control)
+               rtldm->thermalvalue = thermalvalue;
+       rtldm->txpowercount = 0;
+       RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "end\n");
+}
+
+static void rtl88e_dm_init_txpower_tracking(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       rtlpriv->dm.txpower_tracking = true;
+       rtlpriv->dm.txpower_trackinginit = false;
+       rtlpriv->dm.txpowercount = 0;
+       rtlpriv->dm.txpower_track_control = true;
+
+       rtlpriv->dm.swing_idx_ofdm[RF90_PATH_A] = 12;
+       rtlpriv->dm.swing_idx_ofdm_cur = 12;
+       rtlpriv->dm.swing_flag_ofdm = false;
+       RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                "  rtlpriv->dm.txpower_tracking = %d\n",
+                rtlpriv->dm.txpower_tracking);
+}
+
+void rtl88e_dm_check_txpower_tracking(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       static u8 tm_trigger;
+
+       if (!rtlpriv->dm.txpower_tracking)
+               return;
+
+       if (!tm_trigger) {
+               rtl_set_rfreg(hw, RF90_PATH_A, RF_T_METER, BIT(17)|BIT(16),
+                             0x03);
+               RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                        "Trigger 88E Thermal Meter!!\n");
+               tm_trigger = 1;
+               return;
+       } else {
+               RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                        "Schedule TxPowerTracking !!\n");
+               rtl88e_dm_txpower_tracking_callback_thermalmeter(hw);
+               tm_trigger = 0;
+       }
+}
+
+void rtl88e_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rate_adaptive *p_ra = &(rtlpriv->ra);
+
+       p_ra->ratr_state = DM_RATR_STA_INIT;
+       p_ra->pre_ratr_state = DM_RATR_STA_INIT;
+
+       if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER)
+               rtlpriv->dm.useramask = true;
+       else
+               rtlpriv->dm.useramask = false;
+}
+
+static void rtl88e_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rate_adaptive *p_ra = &(rtlpriv->ra);
+       struct ieee80211_sta *sta = NULL;
+       u32 low_rssi, hi_rssi;
+
+       if (is_hal_stop(rtlhal)) {
+               RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                        "driver is going to unload\n");
+               return;
+       }
+
+       if (!rtlpriv->dm.useramask) {
+               RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                        "driver does not control rate adaptive mask\n");
+               return;
+       }
+
+       if (mac->link_state == MAC80211_LINKED &&
+           mac->opmode == NL80211_IFTYPE_STATION) {
+               switch (p_ra->pre_ratr_state) {
+               case DM_RATR_STA_HIGH:
+                       hi_rssi = 50;
+                       low_rssi = 20;
+                       break;
+               case DM_RATR_STA_MIDDLE:
+                       hi_rssi = 55;
+                       low_rssi = 20;
+                       break;
+               case DM_RATR_STA_LOW:
+                       hi_rssi = 50;
+                       low_rssi = 25;
+                       break;
+               default:
+                       hi_rssi = 50;
+                       low_rssi = 20;
+                       break;
+               }
+
+               if (rtlpriv->dm.undec_sm_pwdb > (long)hi_rssi)
+                       p_ra->ratr_state = DM_RATR_STA_HIGH;
+               else if (rtlpriv->dm.undec_sm_pwdb > (long)low_rssi)
+                       p_ra->ratr_state = DM_RATR_STA_MIDDLE;
+               else
+                       p_ra->ratr_state = DM_RATR_STA_LOW;
+
+               if (p_ra->pre_ratr_state != p_ra->ratr_state) {
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "RSSI = %ld\n",
+                                rtlpriv->dm.undec_sm_pwdb);
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "RSSI_LEVEL = %d\n", p_ra->ratr_state);
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "PreState = %d, CurState = %d\n",
+                                 p_ra->pre_ratr_state, p_ra->ratr_state);
+
+                       rcu_read_lock();
+                       sta = rtl_find_sta(hw, mac->bssid);
+                       if (sta)
+                               rtlpriv->cfg->ops->update_rate_tbl(hw, sta,
+                                                  p_ra->ratr_state);
+                       rcu_read_unlock();
+
+                       p_ra->pre_ratr_state = p_ra->ratr_state;
+               }
+       }
+}
+
+static void rtl92c_dm_init_dynamic_bb_powersaving(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct ps_t *dm_pstable = &rtlpriv->dm_pstable;
+
+       dm_pstable->pre_ccastate = CCA_MAX;
+       dm_pstable->cur_ccasate = CCA_MAX;
+       dm_pstable->pre_rfstate = RF_MAX;
+       dm_pstable->cur_rfstate = RF_MAX;
+       dm_pstable->rssi_val_min = 0;
+}
+
+static void rtl88e_dm_update_rx_idle_ant(struct ieee80211_hw *hw, u8 ant)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+       u32 def_ant, opt_ant;
+
+       if (fat_tbl->rx_idle_ant != ant) {
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                        "need to update rx idle ant\n");
+               if (ant == MAIN_ANT) {
+                       def_ant = (fat_tbl->rx_idle_ant == CG_TRX_HW_ANTDIV) ?
+                                  MAIN_ANT_CG_TRX : MAIN_ANT_CGCS_RX;
+                       opt_ant = (fat_tbl->rx_idle_ant == CG_TRX_HW_ANTDIV) ?
+                                  AUX_ANT_CG_TRX : AUX_ANT_CGCS_RX;
+               } else {
+                       def_ant = (fat_tbl->rx_idle_ant == CG_TRX_HW_ANTDIV) ?
+                                  AUX_ANT_CG_TRX : AUX_ANT_CGCS_RX;
+                       opt_ant = (fat_tbl->rx_idle_ant == CG_TRX_HW_ANTDIV) ?
+                                  MAIN_ANT_CG_TRX : MAIN_ANT_CGCS_RX;
+               }
+
+               if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) {
+                       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(5) |
+                                     BIT(4) | BIT(3), def_ant);
+                       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(8) |
+                                     BIT(7) | BIT(6), opt_ant);
+                       rtl_set_bbreg(hw, DM_REG_ANTSEL_CTRL_11N, BIT(14) |
+                                     BIT(13) | BIT(12), def_ant);
+                       rtl_set_bbreg(hw, DM_REG_RESP_TX_11N, BIT(6) | BIT(7),
+                                     def_ant);
+               } else if (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV) {
+                       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(5) |
+                                     BIT(4) | BIT(3), def_ant);
+                       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(8) |
+                                     BIT(7) | BIT(6), opt_ant);
+               }
+       }
+       fat_tbl->rx_idle_ant = ant;
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "RxIdleAnt %s\n",
+                ((ant == MAIN_ANT) ? ("MAIN_ANT") : ("AUX_ANT")));
+}
+
+static void rtl88e_dm_update_tx_ant(struct ieee80211_hw *hw,
+       u8 ant, u32 mac_id)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+       u8 target_ant;
+
+       if (ant == MAIN_ANT)
+               target_ant = MAIN_ANT_CG_TRX;
+       else
+               target_ant = AUX_ANT_CG_TRX;
+
+       fat_tbl->antsel_a[mac_id] = target_ant & BIT(0);
+       fat_tbl->antsel_b[mac_id] = (target_ant & BIT(1)) >> 1;
+       fat_tbl->antsel_c[mac_id] = (target_ant & BIT(2)) >> 2;
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "txfrominfo target ant %s\n",
+                ((ant == MAIN_ANT) ? ("MAIN_ANT") : ("AUX_ANT")));
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "antsel_tr_mux = 3'b%d%d%d\n",
+                fat_tbl->antsel_c[mac_id],
+                fat_tbl->antsel_b[mac_id], fat_tbl->antsel_a[mac_id]);
+}
+
+static void rtl88e_dm_rx_hw_antena_div_init(struct ieee80211_hw *hw)
+{
+       u32  value32;
+       /*MAC Setting*/
+       value32 = rtl_get_bbreg(hw, DM_REG_ANTSEL_PIN_11N, MASKDWORD);
+       rtl_set_bbreg(hw, DM_REG_ANTSEL_PIN_11N, MASKDWORD, value32 |
+                    (BIT(23) | BIT(25)));
+       /*Pin Setting*/
+       rtl_set_bbreg(hw, DM_REG_PIN_CTRL_11N, BIT(9) | BIT(8), 0);
+       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(10), 0);
+       rtl_set_bbreg(hw, DM_REG_LNA_SWITCH_11N, BIT(22), 1);
+       rtl_set_bbreg(hw, DM_REG_LNA_SWITCH_11N, BIT(31), 1);
+       /*OFDM Setting*/
+       rtl_set_bbreg(hw, DM_REG_ANTDIV_PARA1_11N, MASKDWORD, 0x000000a0);
+       /*CCK Setting*/
+       rtl_set_bbreg(hw, DM_REG_BB_PWR_SAV4_11N, BIT(7), 1);
+       rtl_set_bbreg(hw, DM_REG_CCK_ANTDIV_PARA2_11N, BIT(4), 1);
+       rtl88e_dm_update_rx_idle_ant(hw, MAIN_ANT);
+       rtl_set_bbreg(hw, DM_REG_ANT_MAPPING1_11N, MASKLWORD, 0x0201);
+}
+
+static void rtl88e_dm_trx_hw_antenna_div_init(struct ieee80211_hw *hw)
+{
+       u32  value32;
+
+       /*MAC Setting*/
+       value32 = rtl_get_bbreg(hw, DM_REG_ANTSEL_PIN_11N, MASKDWORD);
+       rtl_set_bbreg(hw, DM_REG_ANTSEL_PIN_11N, MASKDWORD, value32 |
+                    (BIT(23) | BIT(25)));
+       /*Pin Setting*/
+       rtl_set_bbreg(hw, DM_REG_PIN_CTRL_11N, BIT(9) | BIT(8), 0);
+       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(10), 0);
+       rtl_set_bbreg(hw, DM_REG_LNA_SWITCH_11N, BIT(22), 0);
+       rtl_set_bbreg(hw, DM_REG_LNA_SWITCH_11N, BIT(31), 1);
+       /*OFDM Setting*/
+       rtl_set_bbreg(hw, DM_REG_ANTDIV_PARA1_11N, MASKDWORD, 0x000000a0);
+       /*CCK Setting*/
+       rtl_set_bbreg(hw, DM_REG_BB_PWR_SAV4_11N, BIT(7), 1);
+       rtl_set_bbreg(hw, DM_REG_CCK_ANTDIV_PARA2_11N, BIT(4), 1);
+       /*TX Setting*/
+       rtl_set_bbreg(hw, DM_REG_TX_ANT_CTRL_11N, BIT(21), 0);
+       rtl88e_dm_update_rx_idle_ant(hw, MAIN_ANT);
+       rtl_set_bbreg(hw, DM_REG_ANT_MAPPING1_11N, MASKLWORD, 0x0201);
+}
+
+static void rtl88e_dm_fast_training_init(struct ieee80211_hw *hw)
+{
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+       u32 ant_combo = 2;
+       u32 value32, i;
+
+       for (i = 0; i < 6; i++) {
+               fat_tbl->bssid[i] = 0;
+               fat_tbl->ant_sum[i] = 0;
+               fat_tbl->ant_cnt[i] = 0;
+               fat_tbl->ant_ave[i] = 0;
+       }
+       fat_tbl->train_idx = 0;
+       fat_tbl->fat_state = FAT_NORMAL_STATE;
+
+       /*MAC Setting*/
+       value32 = rtl_get_bbreg(hw, DM_REG_ANTSEL_PIN_11N, MASKDWORD);
+       rtl_set_bbreg(hw, DM_REG_ANTSEL_PIN_11N, MASKDWORD, value32 | (BIT(23) |
+                     BIT(25)));
+       value32 = rtl_get_bbreg(hw, DM_REG_ANT_TRAIN_2, MASKDWORD);
+       rtl_set_bbreg(hw, DM_REG_ANT_TRAIN_2, MASKDWORD, value32 | (BIT(16) |
+                     BIT(17)));
+       rtl_set_bbreg(hw, DM_REG_ANT_TRAIN_2, MASKLWORD, 0);
+       rtl_set_bbreg(hw, DM_REG_ANT_TRAIN_1, MASKDWORD, 0);
+
+       /*Pin Setting*/
+       rtl_set_bbreg(hw, DM_REG_PIN_CTRL_11N, BIT(9) | BIT(8), 0);
+       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(10), 0);
+       rtl_set_bbreg(hw, DM_REG_LNA_SWITCH_11N, BIT(22), 0);
+       rtl_set_bbreg(hw, DM_REG_LNA_SWITCH_11N, BIT(31), 1);
+
+       /*OFDM Setting*/
+       rtl_set_bbreg(hw, DM_REG_ANTDIV_PARA1_11N, MASKDWORD, 0x000000a0);
+       /*antenna mapping table*/
+       if (ant_combo == 2) {
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING1_11N, MASKBYTE0, 1);
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING1_11N, MASKBYTE1, 2);
+       } else if (ant_combo == 7) {
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING1_11N, MASKBYTE0, 1);
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING1_11N, MASKBYTE1, 2);
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING1_11N, MASKBYTE2, 2);
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING1_11N, MASKBYTE3, 3);
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING2_11N, MASKBYTE0, 4);
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING2_11N, MASKBYTE1, 5);
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING2_11N, MASKBYTE2, 6);
+               rtl_set_bbreg(hw, DM_REG_ANT_MAPPING2_11N, MASKBYTE3, 7);
+       }
+
+       /*TX Setting*/
+       rtl_set_bbreg(hw, DM_REG_TX_ANT_CTRL_11N, BIT(21), 1);
+       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(5) | BIT(4) | BIT(3), 0);
+       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(8) | BIT(7) | BIT(6), 1);
+       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(2) | BIT(1) | BIT(0),
+                     (ant_combo - 1));
+
+       rtl_set_bbreg(hw, DM_REG_IGI_A_11N, BIT(7), 1);
+}
+
+static void rtl88e_dm_antenna_div_init(struct ieee80211_hw *hw)
+{
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+
+       if (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV)
+               rtl88e_dm_rx_hw_antena_div_init(hw);
+       else if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV)
+               rtl88e_dm_trx_hw_antenna_div_init(hw);
+       else if (rtlefuse->antenna_div_type == CG_TRX_SMART_ANTDIV)
+               rtl88e_dm_fast_training_init(hw);
+}
+
+void rtl88e_dm_set_tx_ant_by_tx_info(struct ieee80211_hw *hw,
+                                    u8 *pdesc, u32 mac_id)
+{
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+
+       if ((rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) ||
+           (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV)) {
+               SET_TX_DESC_ANTSEL_A(pdesc, fat_tbl->antsel_a[mac_id]);
+               SET_TX_DESC_ANTSEL_B(pdesc, fat_tbl->antsel_b[mac_id]);
+               SET_TX_DESC_ANTSEL_C(pdesc, fat_tbl->antsel_c[mac_id]);
+       }
+}
+
+void rtl88e_dm_ant_sel_statistics(struct ieee80211_hw *hw,
+                                 u8 antsel_tr_mux, u32 mac_id, u32 rx_pwdb_all)
+{
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+
+       if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) {
+               if (antsel_tr_mux == MAIN_ANT_CG_TRX) {
+                       fat_tbl->main_ant_sum[mac_id] += rx_pwdb_all;
+                       fat_tbl->main_ant_cnt[mac_id]++;
+               } else {
+                       fat_tbl->aux_ant_sum[mac_id] += rx_pwdb_all;
+                       fat_tbl->aux_ant_cnt[mac_id]++;
+               }
+       } else if (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV) {
+               if (antsel_tr_mux == MAIN_ANT_CGCS_RX) {
+                       fat_tbl->main_ant_sum[mac_id] += rx_pwdb_all;
+                       fat_tbl->main_ant_cnt[mac_id]++;
+               } else {
+                       fat_tbl->aux_ant_sum[mac_id] += rx_pwdb_all;
+                       fat_tbl->aux_ant_cnt[mac_id]++;
+               }
+       }
+}
+
+static void rtl88e_dm_hw_ant_div(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct dig_t *dm_dig = &rtlpriv->dm_digtable;
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct rtl_sta_info *drv_priv;
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+       u32 i, min_rssi = 0xff, ant_div_max_rssi = 0, max_rssi = 0;
+       u32 local_min_rssi, local_max_rssi;
+       u32 main_rssi, aux_rssi;
+       u8 rx_idle_ant = 0, target_ant = 7;
+
+       i = 0;
+       main_rssi = (fat_tbl->main_ant_cnt[i] != 0) ?
+                   (fat_tbl->main_ant_sum[i] /
+                    fat_tbl->main_ant_cnt[i]) : 0;
+       aux_rssi = (fat_tbl->aux_ant_cnt[i] != 0) ?
+               (fat_tbl->aux_ant_sum[i] / fat_tbl->aux_ant_cnt[i]) : 0;
+       target_ant = (main_rssi == aux_rssi) ?
+                    fat_tbl->rx_idle_ant : ((main_rssi >= aux_rssi) ?
+                    MAIN_ANT : AUX_ANT);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "main_ant_sum %d main_ant_cnt %d\n",
+                fat_tbl->main_ant_sum[i], fat_tbl->main_ant_cnt[i]);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "aux_ant_sum %d aux_ant_cnt %d\n",
+                fat_tbl->aux_ant_sum[i],
+                fat_tbl->aux_ant_cnt[i]);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "main_rssi %d aux_rssi%d\n", main_rssi, aux_rssi);
+       local_max_rssi = (main_rssi > aux_rssi) ? main_rssi : aux_rssi;
+       if ((local_max_rssi > ant_div_max_rssi) && (local_max_rssi < 40))
+               ant_div_max_rssi = local_max_rssi;
+       if (local_max_rssi > max_rssi)
+               max_rssi = local_max_rssi;
+
+       if ((fat_tbl->rx_idle_ant == MAIN_ANT) && (main_rssi == 0))
+               main_rssi = aux_rssi;
+       else if ((fat_tbl->rx_idle_ant == AUX_ANT) && (aux_rssi == 0))
+               aux_rssi = main_rssi;
+
+       local_min_rssi = (main_rssi > aux_rssi) ? aux_rssi : main_rssi;
+       if (local_min_rssi < min_rssi) {
+               min_rssi = local_min_rssi;
+               rx_idle_ant = target_ant;
+       }
+       if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV)
+               rtl88e_dm_update_tx_ant(hw, target_ant, i);
+
+       if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_AP ||
+           rtlpriv->mac80211.opmode == NL80211_IFTYPE_ADHOC) {
+               spin_lock_bh(&rtlpriv->locks.entry_list_lock);
+               list_for_each_entry(drv_priv, &rtlpriv->entry_list, list) {
+                       i++;
+                       main_rssi = (fat_tbl->main_ant_cnt[i] != 0) ?
+                               (fat_tbl->main_ant_sum[i] /
+                                fat_tbl->main_ant_cnt[i]) : 0;
+                       aux_rssi = (fat_tbl->aux_ant_cnt[i] != 0) ?
+                                  (fat_tbl->aux_ant_sum[i] /
+                                   fat_tbl->aux_ant_cnt[i]) : 0;
+                       target_ant = (main_rssi == aux_rssi) ?
+                                     fat_tbl->rx_idle_ant : ((main_rssi >=
+                                     aux_rssi) ? MAIN_ANT : AUX_ANT);
+
+
+                       local_max_rssi = max_t(u32, main_rssi, aux_rssi);
+                       if ((local_max_rssi > ant_div_max_rssi) &&
+                           (local_max_rssi < 40))
+                               ant_div_max_rssi = local_max_rssi;
+                       if (local_max_rssi > max_rssi)
+                               max_rssi = local_max_rssi;
+
+                       if ((fat_tbl->rx_idle_ant == MAIN_ANT) && !main_rssi)
+                               main_rssi = aux_rssi;
+                       else if ((fat_tbl->rx_idle_ant == AUX_ANT) &&
+                                (aux_rssi == 0))
+                               aux_rssi = main_rssi;
+
+                       local_min_rssi = (main_rssi > aux_rssi) ?
+                                         aux_rssi : main_rssi;
+                       if (local_min_rssi < min_rssi) {
+                               min_rssi = local_min_rssi;
+                               rx_idle_ant = target_ant;
+                       }
+                       if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV)
+                               rtl88e_dm_update_tx_ant(hw, target_ant, i);
+               }
+               spin_unlock_bh(&rtlpriv->locks.entry_list_lock);
+       }
+
+       for (i = 0; i < ASSOCIATE_ENTRY_NUM; i++) {
+               fat_tbl->main_ant_sum[i] = 0;
+               fat_tbl->aux_ant_sum[i] = 0;
+               fat_tbl->main_ant_cnt[i] = 0;
+               fat_tbl->aux_ant_cnt[i] = 0;
+       }
+
+       rtl88e_dm_update_rx_idle_ant(hw, rx_idle_ant);
+
+       dm_dig->antdiv_rssi_max = ant_div_max_rssi;
+       dm_dig->rssi_max = max_rssi;
+}
+
+static void rtl88e_set_next_mac_address_target(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct rtl_sta_info *drv_priv;
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+       u32 value32, i, j = 0;
+
+       if (mac->link_state >= MAC80211_LINKED) {
+               for (i = 0; i < ASSOCIATE_ENTRY_NUM; i++) {
+                       if ((fat_tbl->train_idx + 1) == ASSOCIATE_ENTRY_NUM)
+                               fat_tbl->train_idx = 0;
+                       else
+                               fat_tbl->train_idx++;
+
+                       if (fat_tbl->train_idx == 0) {
+                               value32 = (mac->mac_addr[5] << 8) |
+                                          mac->mac_addr[4];
+                               rtl_set_bbreg(hw, DM_REG_ANT_TRAIN_2,
+                                             MASKLWORD, value32);
+
+                               value32 = (mac->mac_addr[3] << 24) |
+                                         (mac->mac_addr[2] << 16) |
+                                         (mac->mac_addr[1] << 8) |
+                                          mac->mac_addr[0];
+                               rtl_set_bbreg(hw, DM_REG_ANT_TRAIN_1,
+                                             MASKDWORD, value32);
+                               break;
+                       }
+
+                       if (rtlpriv->mac80211.opmode !=
+                           NL80211_IFTYPE_STATION) {
+                               spin_lock_bh(&rtlpriv->locks.entry_list_lock);
+                               list_for_each_entry(drv_priv,
+                                                   &rtlpriv->entry_list,
+                                                   list) {
+                                       j++;
+                                       if (j != fat_tbl->train_idx)
+                                               continue;
+
+                                       value32 = (drv_priv->mac_addr[5] << 8) |
+                                                  drv_priv->mac_addr[4];
+                                       rtl_set_bbreg(hw, DM_REG_ANT_TRAIN_2,
+                                                     MASKLWORD, value32);
+
+                                       value32 = (drv_priv->mac_addr[3]<<24) |
+                                                 (drv_priv->mac_addr[2]<<16) |
+                                                 (drv_priv->mac_addr[1]<<8) |
+                                                  drv_priv->mac_addr[0];
+                                       rtl_set_bbreg(hw, DM_REG_ANT_TRAIN_1,
+                                                     MASKDWORD, value32);
+                                       break;
+                               }
+                               spin_unlock_bh(&rtlpriv->locks.entry_list_lock);
+                               /*find entry, break*/
+                               if (j == fat_tbl->train_idx)
+                                       break;
+                       }
+               }
+       }
+}
+
+static void rtl88e_dm_fast_ant_training(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+       u32 i, max_rssi = 0;
+       u8 target_ant = 2;
+       bool bpkt_filter_match = false;
+
+       if (fat_tbl->fat_state == FAT_TRAINING_STATE) {
+               for (i = 0; i < 7; i++) {
+                       if (fat_tbl->ant_cnt[i] == 0) {
+                               fat_tbl->ant_ave[i] = 0;
+                       } else {
+                               fat_tbl->ant_ave[i] = fat_tbl->ant_sum[i] /
+                                       fat_tbl->ant_cnt[i];
+                               bpkt_filter_match = true;
+                       }
+
+                       if (fat_tbl->ant_ave[i] > max_rssi) {
+                               max_rssi = fat_tbl->ant_ave[i];
+                               target_ant = (u8) i;
+                       }
+               }
+
+               if (bpkt_filter_match == false) {
+                       rtl_set_bbreg(hw, DM_REG_TXAGC_A_1_MCS32_11N,
+                                     BIT(16), 0);
+                       rtl_set_bbreg(hw, DM_REG_IGI_A_11N, BIT(7), 0);
+               } else {
+                       rtl_set_bbreg(hw, DM_REG_TXAGC_A_1_MCS32_11N,
+                                     BIT(16), 0);
+                       rtl_set_bbreg(hw, DM_REG_RX_ANT_CTRL_11N, BIT(8) |
+                                     BIT(7) | BIT(6), target_ant);
+                       rtl_set_bbreg(hw, DM_REG_TX_ANT_CTRL_11N, BIT(21), 1);
+
+                       fat_tbl->antsel_a[fat_tbl->train_idx] =
+                                 target_ant & BIT(0);
+                       fat_tbl->antsel_b[fat_tbl->train_idx] =
+                                (target_ant & BIT(1)) >> 1;
+                       fat_tbl->antsel_c[fat_tbl->train_idx] =
+                                (target_ant & BIT(2)) >> 2;
+
+                       if (target_ant == 0)
+                               rtl_set_bbreg(hw, DM_REG_IGI_A_11N, BIT(7), 0);
+               }
+
+               for (i = 0; i < 7; i++) {
+                       fat_tbl->ant_sum[i] = 0;
+                       fat_tbl->ant_cnt[i] = 0;
+               }
+
+               fat_tbl->fat_state = FAT_NORMAL_STATE;
+               return;
+       }
+
+       if (fat_tbl->fat_state == FAT_NORMAL_STATE) {
+               rtl88e_set_next_mac_address_target(hw);
+
+               fat_tbl->fat_state = FAT_TRAINING_STATE;
+               rtl_set_bbreg(hw, DM_REG_TXAGC_A_1_MCS32_11N, BIT(16), 1);
+               rtl_set_bbreg(hw, DM_REG_IGI_A_11N, BIT(7), 1);
+
+               mod_timer(&rtlpriv->works.fast_antenna_training_timer,
+                         jiffies + MSECS(RTL_WATCH_DOG_TIME));
+       }
+}
+
+void rtl88e_dm_fast_antenna_training_callback(unsigned long data)
+{
+       struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+
+       rtl88e_dm_fast_ant_training(hw);
+}
+
+static void rtl88e_dm_antenna_diversity(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct fast_ant_training *fat_tbl = &(rtldm->fat_table);
+
+       if (mac->link_state < MAC80211_LINKED) {
+               RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "No Link\n");
+               if (fat_tbl->becomelinked == true) {
+                       RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+                                "need to turn off HW AntDiv\n");
+                       rtl_set_bbreg(hw, DM_REG_IGI_A_11N, BIT(7), 0);
+                       rtl_set_bbreg(hw, DM_REG_CCK_ANTDIV_PARA1_11N,
+                                     BIT(15), 0);
+                       if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV)
+                               rtl_set_bbreg(hw, DM_REG_TX_ANT_CTRL_11N,
+                                             BIT(21), 0);
+                       fat_tbl->becomelinked =
+                         (mac->link_state == MAC80211_LINKED) ? true : false;
+               }
+               return;
+       } else {
+               if (fat_tbl->becomelinked == false) {
+                       RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
+                                "Need to turn on HW AntDiv\n");
+                       rtl_set_bbreg(hw, DM_REG_IGI_A_11N, BIT(7), 1);
+                       rtl_set_bbreg(hw, DM_REG_CCK_ANTDIV_PARA1_11N,
+                                     BIT(15), 1);
+                       if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV)
+                               rtl_set_bbreg(hw, DM_REG_TX_ANT_CTRL_11N,
+                                             BIT(21), 1);
+                       fat_tbl->becomelinked =
+                          (mac->link_state >= MAC80211_LINKED) ? true : false;
+               }
+       }
+
+       if ((rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) ||
+           (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV))
+               rtl88e_dm_hw_ant_div(hw);
+       else if (rtlefuse->antenna_div_type == CG_TRX_SMART_ANTDIV)
+               rtl88e_dm_fast_ant_training(hw);
+}
+
+void rtl88e_dm_init(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
+       rtl88e_dm_diginit(hw);
+       rtl88e_dm_init_dynamic_txpower(hw);
+       rtl88e_dm_init_edca_turbo(hw);
+       rtl88e_dm_init_rate_adaptive_mask(hw);
+       rtl88e_dm_init_txpower_tracking(hw);
+       rtl92c_dm_init_dynamic_bb_powersaving(hw);
+       rtl88e_dm_antenna_div_init(hw);
+}
+
+void rtl88e_dm_watchdog(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       bool fw_current_inpsmode = false;
+       bool fw_ps_awake = true;
+
+       rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+                                     (u8 *)(&fw_current_inpsmode));
+       rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FWLPS_RF_ON,
+                                     (u8 *)(&fw_ps_awake));
+       if (ppsc->p2p_ps_info.p2p_ps_mode)
+               fw_ps_awake = false;
+
+       if ((ppsc->rfpwr_state == ERFON) &&
+           ((!fw_current_inpsmode) && fw_ps_awake) &&
+           (!ppsc->rfchange_inprogress)) {
+               rtl88e_dm_pwdb_monitor(hw);
+               rtl88e_dm_dig(hw);
+               rtl88e_dm_false_alarm_counter_statistics(hw);
+               rtl92c_dm_dynamic_txpower(hw);
+               rtl88e_dm_check_txpower_tracking(hw);
+               rtl88e_dm_refresh_rate_adaptive_mask(hw);
+               rtl88e_dm_check_edca_turbo(hw);
+               rtl88e_dm_antenna_diversity(hw);
+       }
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h
new file mode 100644 (file)
index 0000000..0e07f72
--- /dev/null
@@ -0,0 +1,326 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef        __RTL88E_DM_H__
+#define __RTL88E_DM_H__
+
+#define        MAIN_ANT                                        0
+#define        AUX_ANT                                         1
+#define        MAIN_ANT_CG_TRX                                 1
+#define        AUX_ANT_CG_TRX                                  0
+#define        MAIN_ANT_CGCS_RX                                0
+#define        AUX_ANT_CGCS_RX                                 1
+
+/*RF REG LIST*/
+#define        DM_REG_RF_MODE_11N                              0x00
+#define        DM_REG_RF_0B_11N                                0x0B
+#define        DM_REG_CHNBW_11N                                0x18
+#define        DM_REG_T_METER_11N                              0x24
+#define        DM_REG_RF_25_11N                                0x25
+#define        DM_REG_RF_26_11N                                0x26
+#define        DM_REG_RF_27_11N                                0x27
+#define        DM_REG_RF_2B_11N                                0x2B
+#define        DM_REG_RF_2C_11N                                0x2C
+#define        DM_REG_RXRF_A3_11N                              0x3C
+#define        DM_REG_T_METER_92D_11N                          0x42
+#define        DM_REG_T_METER_88E_11N                          0x42
+
+/*BB REG LIST*/
+/*PAGE 8 */
+#define        DM_REG_BB_CTRL_11N                              0x800
+#define        DM_REG_RF_PIN_11N                               0x804
+#define        DM_REG_PSD_CTRL_11N                             0x808
+#define        DM_REG_TX_ANT_CTRL_11N                          0x80C
+#define        DM_REG_BB_PWR_SAV5_11N                          0x818
+#define        DM_REG_CCK_RPT_FORMAT_11N                       0x824
+#define        DM_REG_RX_DEFAULT_A_11N                         0x858
+#define        DM_REG_RX_DEFAULT_B_11N                         0x85A
+#define        DM_REG_BB_PWR_SAV3_11N                          0x85C
+#define        DM_REG_ANTSEL_CTRL_11N                          0x860
+#define        DM_REG_RX_ANT_CTRL_11N                          0x864
+#define        DM_REG_PIN_CTRL_11N                             0x870
+#define        DM_REG_BB_PWR_SAV1_11N                          0x874
+#define        DM_REG_ANTSEL_PATH_11N                          0x878
+#define        DM_REG_BB_3WIRE_11N                             0x88C
+#define        DM_REG_SC_CNT_11N                               0x8C4
+#define        DM_REG_PSD_DATA_11N                             0x8B4
+/*PAGE 9*/
+#define        DM_REG_ANT_MAPPING1_11N                         0x914
+#define        DM_REG_ANT_MAPPING2_11N                         0x918
+/*PAGE A*/
+#define        DM_REG_CCK_ANTDIV_PARA1_11N                     0xA00
+#define        DM_REG_CCK_CCA_11N                              0xA0A
+#define        DM_REG_CCK_ANTDIV_PARA2_11N                     0xA0C
+#define        DM_REG_CCK_ANTDIV_PARA3_11N                     0xA10
+#define        DM_REG_CCK_ANTDIV_PARA4_11N                     0xA14
+#define        DM_REG_CCK_FILTER_PARA1_11N                     0xA22
+#define        DM_REG_CCK_FILTER_PARA2_11N                     0xA23
+#define        DM_REG_CCK_FILTER_PARA3_11N                     0xA24
+#define        DM_REG_CCK_FILTER_PARA4_11N                     0xA25
+#define        DM_REG_CCK_FILTER_PARA5_11N                     0xA26
+#define        DM_REG_CCK_FILTER_PARA6_11N                     0xA27
+#define        DM_REG_CCK_FILTER_PARA7_11N                     0xA28
+#define        DM_REG_CCK_FILTER_PARA8_11N                     0xA29
+#define        DM_REG_CCK_FA_RST_11N                           0xA2C
+#define        DM_REG_CCK_FA_MSB_11N                           0xA58
+#define        DM_REG_CCK_FA_LSB_11N                           0xA5C
+#define        DM_REG_CCK_CCA_CNT_11N                          0xA60
+#define        DM_REG_BB_PWR_SAV4_11N                          0xA74
+/*PAGE B */
+#define        DM_REG_LNA_SWITCH_11N                           0xB2C
+#define        DM_REG_PATH_SWITCH_11N                          0xB30
+#define        DM_REG_RSSI_CTRL_11N                            0xB38
+#define        DM_REG_CONFIG_ANTA_11N                          0xB68
+#define        DM_REG_RSSI_BT_11N                              0xB9C
+/*PAGE C */
+#define        DM_REG_OFDM_FA_HOLDC_11N                        0xC00
+#define        DM_REG_RX_PATH_11N                              0xC04
+#define        DM_REG_TRMUX_11N                                0xC08
+#define        DM_REG_OFDM_FA_RSTC_11N                         0xC0C
+#define        DM_REG_RXIQI_MATRIX_11N                         0xC14
+#define        DM_REG_TXIQK_MATRIX_LSB1_11N                    0xC4C
+#define        DM_REG_IGI_A_11N                                0xC50
+#define        DM_REG_ANTDIV_PARA2_11N                         0xC54
+#define        DM_REG_IGI_B_11N                                0xC58
+#define        DM_REG_ANTDIV_PARA3_11N                         0xC5C
+#define        DM_REG_BB_PWR_SAV2_11N                          0xC70
+#define        DM_REG_RX_OFF_11N                               0xC7C
+#define        DM_REG_TXIQK_MATRIXA_11N                        0xC80
+#define        DM_REG_TXIQK_MATRIXB_11N                        0xC88
+#define        DM_REG_TXIQK_MATRIXA_LSB2_11N                   0xC94
+#define        DM_REG_TXIQK_MATRIXB_LSB2_11N                   0xC9C
+#define        DM_REG_RXIQK_MATRIX_LSB_11N                     0xCA0
+#define        DM_REG_ANTDIV_PARA1_11N                         0xCA4
+#define        DM_REG_OFDM_FA_TYPE1_11N                        0xCF0
+/*PAGE D */
+#define        DM_REG_OFDM_FA_RSTD_11N                         0xD00
+#define        DM_REG_OFDM_FA_TYPE2_11N                        0xDA0
+#define        DM_REG_OFDM_FA_TYPE3_11N                        0xDA4
+#define        DM_REG_OFDM_FA_TYPE4_11N                        0xDA8
+/*PAGE E */
+#define        DM_REG_TXAGC_A_6_18_11N                         0xE00
+#define        DM_REG_TXAGC_A_24_54_11N                        0xE04
+#define        DM_REG_TXAGC_A_1_MCS32_11N                      0xE08
+#define        DM_REG_TXAGC_A_MCS0_3_11N                       0xE10
+#define        DM_REG_TXAGC_A_MCS4_7_11N                       0xE14
+#define        DM_REG_TXAGC_A_MCS8_11_11N                      0xE18
+#define        DM_REG_TXAGC_A_MCS12_15_11N                     0xE1C
+#define        DM_REG_FPGA0_IQK_11N                            0xE28
+#define        DM_REG_TXIQK_TONE_A_11N                         0xE30
+#define        DM_REG_RXIQK_TONE_A_11N                         0xE34
+#define        DM_REG_TXIQK_PI_A_11N                           0xE38
+#define        DM_REG_RXIQK_PI_A_11N                           0xE3C
+#define        DM_REG_TXIQK_11N                                0xE40
+#define        DM_REG_RXIQK_11N                                0xE44
+#define        DM_REG_IQK_AGC_PTS_11N                          0xE48
+#define        DM_REG_IQK_AGC_RSP_11N                          0xE4C
+#define        DM_REG_BLUETOOTH_11N                            0xE6C
+#define        DM_REG_RX_WAIT_CCA_11N                          0xE70
+#define        DM_REG_TX_CCK_RFON_11N                          0xE74
+#define        DM_REG_TX_CCK_BBON_11N                          0xE78
+#define        DM_REG_OFDM_RFON_11N                            0xE7C
+#define        DM_REG_OFDM_BBON_11N                            0xE80
+#define DM_REG_TX2RX_11N                               0xE84
+#define        DM_REG_TX2TX_11N                                0xE88
+#define        DM_REG_RX_CCK_11N                               0xE8C
+#define        DM_REG_RX_OFDM_11N                              0xED0
+#define        DM_REG_RX_WAIT_RIFS_11N                         0xED4
+#define        DM_REG_RX2RX_11N                                0xED8
+#define        DM_REG_STANDBY_11N                              0xEDC
+#define        DM_REG_SLEEP_11N                                0xEE0
+#define        DM_REG_PMPD_ANAEN_11N                           0xEEC
+
+
+/*MAC REG LIST*/
+#define        DM_REG_BB_RST_11N                               0x02
+#define        DM_REG_ANTSEL_PIN_11N                           0x4C
+#define        DM_REG_EARLY_MODE_11N                           0x4D0
+#define        DM_REG_RSSI_MONITOR_11N                         0x4FE
+#define        DM_REG_EDCA_VO_11N                              0x500
+#define        DM_REG_EDCA_VI_11N                              0x504
+#define        DM_REG_EDCA_BE_11N                              0x508
+#define        DM_REG_EDCA_BK_11N                              0x50C
+#define        DM_REG_TXPAUSE_11N                              0x522
+#define        DM_REG_RESP_TX_11N                              0x6D8
+#define        DM_REG_ANT_TRAIN_1                              0x7b0
+#define        DM_REG_ANT_TRAIN_2                              0x7b4
+
+/*DIG Related*/
+#define        DM_BIT_IGI_11N                                  0x0000007F
+
+#define HAL_DM_DIG_DISABLE                             BIT(0)
+#define HAL_DM_HIPWR_DISABLE                           BIT(1)
+
+#define OFDM_TABLE_LENGTH                              43
+#define CCK_TABLE_LENGTH                               33
+
+#define OFDM_TABLE_SIZE                                        43
+#define CCK_TABLE_SIZE                                 33
+
+#define BW_AUTO_SWITCH_HIGH_LOW                                25
+#define BW_AUTO_SWITCH_LOW_HIGH                                30
+
+#define DM_DIG_THRESH_HIGH                             40
+#define DM_DIG_THRESH_LOW                              35
+
+#define DM_FALSEALARM_THRESH_LOW                       400
+#define DM_FALSEALARM_THRESH_HIGH                      1000
+
+#define DM_DIG_MAX                                     0x3e
+#define DM_DIG_MIN                                     0x1e
+
+#define DM_DIG_MAX_AP                                  0x32
+#define DM_DIG_MIN_AP                                  0x20
+
+#define DM_DIG_FA_UPPER                                        0x3e
+#define DM_DIG_FA_LOWER                                        0x1e
+#define DM_DIG_FA_TH0                                  0x200
+#define DM_DIG_FA_TH1                                  0x300
+#define DM_DIG_FA_TH2                                  0x400
+
+#define DM_DIG_BACKOFF_MAX                             12
+#define DM_DIG_BACKOFF_MIN                             -4
+#define DM_DIG_BACKOFF_DEFAULT                         10
+
+#define RXPATHSELECTION_SS_TH_LOW                      30
+#define RXPATHSELECTION_DIFF_TH                                18
+
+#define DM_RATR_STA_INIT                               0
+#define DM_RATR_STA_HIGH                               1
+#define DM_RATR_STA_MIDDLE                             2
+#define DM_RATR_STA_LOW                                        3
+
+#define CTS2SELF_THVAL                                 30
+#define REGC38_TH                                      20
+
+#define WAIOTTHVAL                                     25
+
+#define TXHIGHPWRLEVEL_NORMAL                          0
+#define TXHIGHPWRLEVEL_LEVEL1                          1
+#define TXHIGHPWRLEVEL_LEVEL2                          2
+#define TXHIGHPWRLEVEL_BT1                             3
+#define TXHIGHPWRLEVEL_BT2                             4
+
+#define DM_TYPE_BYFW                                   0
+#define DM_TYPE_BYDRIVER                               1
+
+#define TX_POWER_NEAR_FIELD_THRESH_LVL2                        74
+#define TX_POWER_NEAR_FIELD_THRESH_LVL1                        67
+#define TXPWRTRACK_MAX_IDX                             6
+
+struct swat_t {
+       u8 failure_cnt;
+       u8 try_flag;
+       u8 stop_trying;
+       long pre_rssi;
+       long trying_threshold;
+       u8 cur_antenna;
+       u8 pre_antenna;
+};
+
+enum FAT_STATE {
+       FAT_NORMAL_STATE        = 0,
+       FAT_TRAINING_STATE = 1,
+};
+
+enum tag_dynamic_init_gain_operation_type_definition {
+       DIG_TYPE_THRESH_HIGH = 0,
+       DIG_TYPE_THRESH_LOW = 1,
+       DIG_TYPE_BACKOFF = 2,
+       DIG_TYPE_RX_GAIN_MIN = 3,
+       DIG_TYPE_RX_GAIN_MAX = 4,
+       DIG_TYPE_ENABLE = 5,
+       DIG_TYPE_DISABLE = 6,
+       DIG_OP_TYPE_MAX
+};
+
+enum tag_cck_packet_detection_threshold_type_definition {
+       CCK_PD_STAGE_LOWRSSI = 0,
+       CCK_PD_STAGE_HIGHRSSI = 1,
+       CCK_FA_STAGE_LOW = 2,
+       CCK_FA_STAGE_HIGH = 3,
+       CCK_PD_STAGE_MAX = 4,
+};
+
+enum dm_1r_cca_e {
+       CCA_1R = 0,
+       CCA_2R = 1,
+       CCA_MAX = 2,
+};
+
+enum dm_rf_e {
+       RF_SAVE = 0,
+       RF_NORMAL = 1,
+       RF_MAX = 2,
+};
+
+enum dm_sw_ant_switch_e {
+       ANS_ANTENNA_B = 1,
+       ANS_ANTENNA_A = 2,
+       ANS_ANTENNA_MAX = 3,
+};
+
+enum dm_dig_ext_port_alg_e {
+       DIG_EXT_PORT_STAGE_0 = 0,
+       DIG_EXT_PORT_STAGE_1 = 1,
+       DIG_EXT_PORT_STAGE_2 = 2,
+       DIG_EXT_PORT_STAGE_3 = 3,
+       DIG_EXT_PORT_STAGE_MAX = 4,
+};
+
+enum dm_dig_connect_e {
+       DIG_STA_DISCONNECT = 0,
+       DIG_STA_CONNECT = 1,
+       DIG_STA_BEFORE_CONNECT = 2,
+       DIG_MULTISTA_DISCONNECT = 3,
+       DIG_MULTISTA_CONNECT = 4,
+       DIG_CONNECT_MAX
+};
+
+enum pwr_track_control_method {
+       BBSWING,
+       TXAGC
+};
+
+void rtl88e_dm_set_tx_ant_by_tx_info(struct ieee80211_hw *hw,
+                                    u8 *pdesc, u32 mac_id);
+void rtl88e_dm_ant_sel_statistics(struct ieee80211_hw *hw, u8 antsel_tr_mux,
+                                 u32 mac_id, u32 rx_pwdb_all);
+void rtl88e_dm_fast_antenna_training_callback(unsigned long data);
+void rtl88e_dm_init(struct ieee80211_hw *hw);
+void rtl88e_dm_watchdog(struct ieee80211_hw *hw);
+void rtl88e_dm_write_dig(struct ieee80211_hw *hw);
+void rtl88e_dm_init_edca_turbo(struct ieee80211_hw *hw);
+void rtl88e_dm_check_txpower_tracking(struct ieee80211_hw *hw);
+void rtl88e_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw);
+void rtl88e_dm_txpower_track_adjust(struct ieee80211_hw *hw,
+                                   u8 type, u8 *pdirection,
+                                   u32 *poutwrite_val);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c
new file mode 100644 (file)
index 0000000..57e4cc5
--- /dev/null
@@ -0,0 +1,830 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../base.h"
+#include "reg.h"
+#include "def.h"
+#include "fw.h"
+
+#include <linux/kmemleak.h>
+
+static void _rtl88e_enable_fw_download(struct ieee80211_hw *hw, bool enable)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 tmp;
+
+       if (enable) {
+               tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+               rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04);
+
+               tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
+               rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01);
+
+               tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2);
+               rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7);
+       } else {
+               tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
+               rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe);
+
+               rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00);
+       }
+}
+
+static void _rtl88e_fw_block_write(struct ieee80211_hw *hw,
+                                  const u8 *buffer, u32 size)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 blk_sz = sizeof(u32);
+       u8 *buf_ptr = (u8 *)buffer;
+       u32 *pu4BytePtr = (u32 *)buffer;
+       u32 i, offset, blk_cnt, remain;
+
+       blk_cnt = size / blk_sz;
+       remain = size % blk_sz;
+
+       for (i = 0; i < blk_cnt; i++) {
+               offset = i * blk_sz;
+               rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
+                               *(pu4BytePtr + i));
+       }
+
+       if (remain) {
+               offset = blk_cnt * blk_sz;
+               buf_ptr += offset;
+               for (i = 0; i < remain; i++) {
+                       rtl_write_byte(rtlpriv, (FW_8192C_START_ADDRESS +
+                                                offset + i), *(buf_ptr + i));
+               }
+       }
+}
+
+static void _rtl88e_fw_page_write(struct ieee80211_hw *hw,
+                                 u32 page, const u8 *buffer, u32 size)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 value8;
+       u8 u8page = (u8) (page & 0x07);
+
+       value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;
+
+       rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
+       _rtl88e_fw_block_write(hw, buffer, size);
+}
+
+static void _rtl88e_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
+{
+       u32 fwlen = *pfwlen;
+       u8 remain = (u8) (fwlen % 4);
+
+       remain = (remain == 0) ? 0 : (4 - remain);
+
+       while (remain > 0) {
+               pfwbuf[fwlen] = 0;
+               fwlen++;
+               remain--;
+       }
+
+       *pfwlen = fwlen;
+}
+
+static void _rtl88e_write_fw(struct ieee80211_hw *hw,
+                            enum version_8188e version, u8 *buffer, u32 size)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 *buf_ptr = (u8 *)buffer;
+       u32 page_no, remain;
+       u32 page, offset;
+
+       RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "FW size is %d bytes,\n", size);
+
+       _rtl88e_fill_dummy(buf_ptr, &size);
+
+       page_no = size / FW_8192C_PAGE_SIZE;
+       remain = size % FW_8192C_PAGE_SIZE;
+
+       if (page_no > 8) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "Page numbers should not greater then 8\n");
+       }
+
+       for (page = 0; page < page_no; page++) {
+               offset = page * FW_8192C_PAGE_SIZE;
+               _rtl88e_fw_page_write(hw, page, (buf_ptr + offset),
+                                     FW_8192C_PAGE_SIZE);
+       }
+
+       if (remain) {
+               offset = page_no * FW_8192C_PAGE_SIZE;
+               page = page_no;
+               _rtl88e_fw_page_write(hw, page, (buf_ptr + offset), remain);
+       }
+}
+
+static int _rtl88e_fw_free_to_go(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       int err = -EIO;
+       u32 counter = 0;
+       u32 value32;
+
+       do {
+               value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
+       } while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) &&
+                (!(value32 & FWDL_CHKSUM_RPT)));
+
+       if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "chksum report faill ! REG_MCUFWDL:0x%08x .\n",
+                         value32);
+               goto exit;
+       }
+
+       RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
+                "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32);
+
+       value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
+       value32 |= MCUFWDL_RDY;
+       value32 &= ~WINTINI_RDY;
+       rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);
+
+       rtl88e_firmware_selfreset(hw);
+       counter = 0;
+
+       do {
+               value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
+               if (value32 & WINTINI_RDY) {
+                       RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
+                                "Polling FW ready success!! REG_MCUFWDL:0x%08x.\n",
+                                 value32);
+                       err = 0;
+                       goto exit;
+               }
+
+               udelay(FW_8192C_POLLING_DELAY);
+
+       } while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT);
+
+       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n", value32);
+
+exit:
+       return err;
+}
+
+int rtl88e_download_fw(struct ieee80211_hw *hw, bool buse_wake_on_wlan_fw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl92c_firmware_header *pfwheader;
+       u8 *pfwdata;
+       u32 fwsize;
+       int err;
+       enum version_8188e version = rtlhal->version;
+
+       if (!rtlhal->pfirmware)
+               return 1;
+
+       pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware;
+       pfwdata = (u8 *)rtlhal->pfirmware;
+       fwsize = rtlhal->fwsize;
+       RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+                "normal Firmware SIZE %d\n", fwsize);
+
+       if (IS_FW_HEADER_EXIST(pfwheader)) {
+               RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+                        "Firmware Version(%d), Signature(%#x), Size(%d)\n",
+                        pfwheader->version, pfwheader->signature,
+                        (int)sizeof(struct rtl92c_firmware_header));
+
+               pfwdata = pfwdata + sizeof(struct rtl92c_firmware_header);
+               fwsize = fwsize - sizeof(struct rtl92c_firmware_header);
+       }
+
+       if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) {
+               rtl_write_byte(rtlpriv, REG_MCUFWDL, 0);
+               rtl88e_firmware_selfreset(hw);
+       }
+       _rtl88e_enable_fw_download(hw, true);
+       _rtl88e_write_fw(hw, version, pfwdata, fwsize);
+       _rtl88e_enable_fw_download(hw, false);
+
+       err = _rtl88e_fw_free_to_go(hw);
+
+       RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+                "Firmware is%s ready to run!\n", err ? " not" : "");
+       return 0;
+}
+
+static bool _rtl88e_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 val_hmetfr;
+
+       val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR);
+       if (((val_hmetfr >> boxnum) & BIT(0)) == 0)
+               return true;
+       return false;
+}
+
+static void _rtl88e_fill_h2c_command(struct ieee80211_hw *hw,
+                                    u8 element_id, u32 cmd_len,
+                                    u8 *cmd_b)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u8 boxnum;
+       u16 box_reg = 0, box_extreg = 0;
+       u8 u1b_tmp;
+       bool isfw_read = false;
+       u8 buf_index = 0;
+       bool write_sucess = false;
+       u8 wait_h2c_limit = 100;
+       u8 wait_writeh2c_limit = 100;
+       u8 boxc[4], boxext[2];
+       u32 h2c_waitcounter = 0;
+       unsigned long flag;
+       u8 idx;
+
+       RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "come in\n");
+
+       while (true) {
+               spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
+               if (rtlhal->h2c_setinprogress) {
+                       RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+                                "H2C set in progress! Wait to set..element_id(%d).\n",
+                                element_id);
+
+                       while (rtlhal->h2c_setinprogress) {
+                               spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock,
+                                                      flag);
+                               h2c_waitcounter++;
+                               RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+                                        "Wait 100 us (%d times)...\n",
+                                        h2c_waitcounter);
+                               udelay(100);
+
+                               if (h2c_waitcounter > 1000)
+                                       return;
+                               spin_lock_irqsave(&rtlpriv->locks.h2c_lock,
+                                                 flag);
+                       }
+                       spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+               } else {
+                       rtlhal->h2c_setinprogress = true;
+                       spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+                       break;
+               }
+       }
+
+       while (!write_sucess) {
+               wait_writeh2c_limit--;
+               if (wait_writeh2c_limit == 0) {
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "Write H2C fail because no trigger for FW INT!\n");
+                       break;
+               }
+
+               boxnum = rtlhal->last_hmeboxnum;
+               switch (boxnum) {
+               case 0:
+                       box_reg = REG_HMEBOX_0;
+                       box_extreg = REG_HMEBOX_EXT_0;
+                       break;
+               case 1:
+                       box_reg = REG_HMEBOX_1;
+                       box_extreg = REG_HMEBOX_EXT_1;
+                       break;
+               case 2:
+                       box_reg = REG_HMEBOX_2;
+                       box_extreg = REG_HMEBOX_EXT_2;
+                       break;
+               case 3:
+                       box_reg = REG_HMEBOX_3;
+                       box_extreg = REG_HMEBOX_EXT_3;
+                       break;
+               default:
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "switch case not processed\n");
+                       break;
+               }
+
+               isfw_read = _rtl88e_check_fw_read_last_h2c(hw, boxnum);
+               while (!isfw_read) {
+                       wait_h2c_limit--;
+                       if (wait_h2c_limit == 0) {
+                               RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+                                        "Wating too long for FW read "
+                                        "clear HMEBox(%d)!\n", boxnum);
+                               break;
+                       }
+
+                       udelay(10);
+
+                       isfw_read = _rtl88e_check_fw_read_last_h2c(hw, boxnum);
+                       u1b_tmp = rtl_read_byte(rtlpriv, 0x130);
+                       RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+                                "Wating for FW read clear HMEBox(%d)!!! "
+                                "0x130 = %2x\n", boxnum, u1b_tmp);
+               }
+
+               if (!isfw_read) {
+                       RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+                                "Write H2C register BOX[%d] fail!!!!! "
+                                "Fw do not read.\n", boxnum);
+                       break;
+               }
+
+               memset(boxc, 0, sizeof(boxc));
+               memset(boxext, 0, sizeof(boxext));
+               boxc[0] = element_id;
+               RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+                        "Write element_id box_reg(%4x) = %2x\n",
+                        box_reg, element_id);
+
+               switch (cmd_len) {
+               case 1:
+               case 2:
+               case 3:
+                       /*boxc[0] &= ~(BIT(7));*/
+                       memcpy((u8 *)(boxc) + 1, cmd_b + buf_index, cmd_len);
+
+                       for (idx = 0; idx < 4; idx++)
+                               rtl_write_byte(rtlpriv, box_reg+idx, boxc[idx]);
+                       break;
+               case 4:
+               case 5:
+               case 6:
+               case 7:
+                       /*boxc[0] |= (BIT(7));*/
+                       memcpy((u8 *)(boxext), cmd_b + buf_index+3, cmd_len-3);
+                       memcpy((u8 *)(boxc) + 1, cmd_b + buf_index, 3);
+
+                       for (idx = 0; idx < 2; idx++) {
+                               rtl_write_byte(rtlpriv, box_extreg + idx,
+                                              boxext[idx]);
+                       }
+
+                       for (idx = 0; idx < 4; idx++) {
+                               rtl_write_byte(rtlpriv, box_reg + idx,
+                                              boxc[idx]);
+                       }
+                       break;
+               default:
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "switch case not processed\n");
+                       break;
+               }
+
+               write_sucess = true;
+
+               rtlhal->last_hmeboxnum = boxnum + 1;
+               if (rtlhal->last_hmeboxnum == 4)
+                       rtlhal->last_hmeboxnum = 0;
+
+               RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+                        "pHalData->last_hmeboxnum  = %d\n",
+                        rtlhal->last_hmeboxnum);
+       }
+
+       spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
+       rtlhal->h2c_setinprogress = false;
+       spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+
+       RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "go out\n");
+}
+
+void rtl88e_fill_h2c_cmd(struct ieee80211_hw *hw,
+                        u8 element_id, u32 cmd_len, u8 *cmd_b)
+{
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u32 tmp_cmdbuf[2];
+
+       if (rtlhal->fw_ready == false) {
+               RT_ASSERT(false, "fail H2C cmd - Fw download fail!!!\n");
+               return;
+       }
+
+       memset(tmp_cmdbuf, 0, 8);
+       memcpy(tmp_cmdbuf, cmd_b, cmd_len);
+       _rtl88e_fill_h2c_command(hw, element_id, cmd_len, (u8 *)&tmp_cmdbuf);
+
+       return;
+}
+
+void rtl88e_firmware_selfreset(struct ieee80211_hw *hw)
+{
+       u8 u1b_tmp;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN+1);
+       rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN+1, (u1b_tmp & (~BIT(2))));
+       rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN+1, (u1b_tmp | BIT(2)));
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "8051Reset88E(): 8051 reset success.\n");
+}
+
+void rtl88e_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 u1_h2c_set_pwrmode[H2C_88E_PWEMODE_LENGTH] = { 0 };
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       u8 power_state = 0;
+
+       RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
+       SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, ((mode) ? 1 : 0));
+       SET_H2CCMD_PWRMODE_PARM_RLBM(u1_h2c_set_pwrmode, 0);
+       SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode,
+                                        (rtlpriv->mac80211.p2p) ?
+                                        ppsc->smart_ps : 1);
+       SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(u1_h2c_set_pwrmode,
+                                              ppsc->reg_max_lps_awakeintvl);
+       SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(u1_h2c_set_pwrmode, 0);
+       if (mode == FW_PS_ACTIVE_MODE)
+               power_state |= FW_PWR_STATE_ACTIVE;
+       else
+               power_state |= FW_PWR_STATE_RF_OFF;
+       SET_H2CCMD_PWRMODE_PARM_PWR_STATE(u1_h2c_set_pwrmode, power_state);
+
+       RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+                     "rtl92c_set_fw_pwrmode(): u1_h2c_set_pwrmode\n",
+                     u1_h2c_set_pwrmode, H2C_88E_PWEMODE_LENGTH);
+       rtl88e_fill_h2c_cmd(hw, H2C_88E_SETPWRMODE, H2C_88E_PWEMODE_LENGTH,
+                           u1_h2c_set_pwrmode);
+}
+
+void rtl88e_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus)
+{
+       u8 u1_joinbssrpt_parm[1] = { 0 };
+
+       SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus);
+
+       rtl88e_fill_h2c_cmd(hw, H2C_88E_JOINBSSRPT, 1, u1_joinbssrpt_parm);
+}
+
+void rtl88e_set_fw_ap_off_load_cmd(struct ieee80211_hw *hw,
+                                  u8 ap_offload_enable)
+{
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       u8 u1_apoffload_parm[H2C_88E_AP_OFFLOAD_LENGTH] = { 0 };
+
+       SET_H2CCMD_AP_OFFLOAD_ON(u1_apoffload_parm, ap_offload_enable);
+       SET_H2CCMD_AP_OFFLOAD_HIDDEN(u1_apoffload_parm, mac->hiddenssid);
+       SET_H2CCMD_AP_OFFLOAD_DENYANY(u1_apoffload_parm, 0);
+
+       rtl88e_fill_h2c_cmd(hw, H2C_88E_AP_OFFLOAD, H2C_88E_AP_OFFLOAD_LENGTH,
+                           u1_apoffload_parm);
+}
+
+static bool _rtl88e_cmd_send_packet(struct ieee80211_hw *hw,
+                                   struct sk_buff *skb)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl8192_tx_ring *ring;
+       struct rtl_tx_desc *pdesc;
+       struct sk_buff *pskb = NULL;
+       unsigned long flags;
+
+       ring = &rtlpci->tx_ring[BEACON_QUEUE];
+
+       pskb = __skb_dequeue(&ring->queue);
+       if (pskb)
+               kfree_skb(pskb);
+
+       spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
+
+       pdesc = &ring->desc[0];
+
+       rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb);
+
+       __skb_queue_tail(&ring->queue, skb);
+
+       spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
+
+       rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE);
+
+       return true;
+}
+
+#define BEACON_PG              0 /* ->1 */
+#define PSPOLL_PG              2
+#define NULL_PG                        3
+#define PROBERSP_PG            4 /* ->5 */
+
+#define TOTAL_RESERVED_PKT_LEN 768
+
+static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = {
+       /* page 0 beacon */
+       0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+       0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x50, 0x08,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
+       0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
+       0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
+       0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
+       0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
+       0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
+       0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
+       0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+       /* page 1 beacon */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x10, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x10, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+       /* page 2  ps-poll */
+       0xA4, 0x10, 0x01, 0xC0, 0x00, 0x40, 0x10, 0x10,
+       0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x18, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+       0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+       /* page 3  null */
+       0x48, 0x01, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
+       0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+       0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x72, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+       0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+       /* page 4  probe_resp */
+       0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
+       0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+       0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
+       0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00,
+       0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
+       0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
+       0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
+       0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
+       0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
+       0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
+       0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
+       0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+       /* page 5  probe_resp */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+void rtl88e_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct sk_buff *skb = NULL;
+
+       u32 totalpacketlen;
+       u8 u1RsvdPageLoc[5] = { 0 };
+
+       u8 *beacon;
+       u8 *pspoll;
+       u8 *nullfunc;
+       u8 *probersp;
+       /*---------------------------------------------------------
+        *                      (1) beacon
+        *---------------------------------------------------------
+        */
+       beacon = &reserved_page_packet[BEACON_PG * 128];
+       SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr);
+       SET_80211_HDR_ADDRESS3(beacon, mac->bssid);
+
+       /*-------------------------------------------------------
+        *                      (2) ps-poll
+        *--------------------------------------------------------
+        */
+       pspoll = &reserved_page_packet[PSPOLL_PG * 128];
+       SET_80211_PS_POLL_AID(pspoll, (mac->assoc_id | 0xc000));
+       SET_80211_PS_POLL_BSSID(pspoll, mac->bssid);
+       SET_80211_PS_POLL_TA(pspoll, mac->mac_addr);
+
+       SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1RsvdPageLoc, PSPOLL_PG);
+
+       /*--------------------------------------------------------
+        *                      (3) null data
+        *---------------------------------------------------------
+        */
+       nullfunc = &reserved_page_packet[NULL_PG * 128];
+       SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid);
+       SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr);
+       SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid);
+
+       SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1RsvdPageLoc, NULL_PG);
+
+       /*---------------------------------------------------------
+        *                      (4) probe response
+        *----------------------------------------------------------
+        */
+       probersp = &reserved_page_packet[PROBERSP_PG * 128];
+       SET_80211_HDR_ADDRESS1(probersp, mac->bssid);
+       SET_80211_HDR_ADDRESS2(probersp, mac->mac_addr);
+       SET_80211_HDR_ADDRESS3(probersp, mac->bssid);
+
+       SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1RsvdPageLoc, PROBERSP_PG);
+
+       totalpacketlen = TOTAL_RESERVED_PKT_LEN;
+
+       RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD,
+                     "rtl88e_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n",
+                     &reserved_page_packet[0], totalpacketlen);
+       RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+                     "rtl88e_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n",
+                     u1RsvdPageLoc, 3);
+
+       skb = dev_alloc_skb(totalpacketlen);
+       if (!skb)
+               return;
+       kmemleak_not_leak(skb);
+       memcpy(skb_put(skb, totalpacketlen),
+              &reserved_page_packet, totalpacketlen);
+
+       if (_rtl88e_cmd_send_packet(hw, skb)) {
+               RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                        "Set RSVD page location to Fw.\n");
+               RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+                             "H2C_RSVDPAGE:\n", u1RsvdPageLoc, 3);
+               rtl88e_fill_h2c_cmd(hw, H2C_88E_RSVDPAGE,
+                                   sizeof(u1RsvdPageLoc), u1RsvdPageLoc);
+       } else
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                        "Set RSVD page location to Fw FAIL!!!!!!.\n");
+}
+
+/*Shoud check FW support p2p or not.*/
+static void rtl88e_set_p2p_ctw_period_cmd(struct ieee80211_hw *hw, u8 ctwindow)
+{
+       u8 u1_ctwindow_period[1] = {ctwindow};
+
+       rtl88e_fill_h2c_cmd(hw, H2C_88E_P2P_PS_CTW_CMD, 1, u1_ctwindow_period);
+}
+
+void rtl88e_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_p2p_ps_info *p2pinfo = &(rtlps->p2p_ps_info);
+       struct p2p_ps_offload_t *p2p_ps_offload = &rtlhal->p2p_ps_offload;
+       u8      i;
+       u16     ctwindow;
+       u32     start_time, tsf_low;
+
+       switch (p2p_ps_state) {
+       case P2P_PS_DISABLE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_DISABLE\n");
+               memset(p2p_ps_offload, 0, sizeof(struct p2p_ps_offload_t));
+               break;
+       case P2P_PS_ENABLE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_ENABLE\n");
+               /* update CTWindow value. */
+               if (p2pinfo->ctwindow > 0) {
+                       p2p_ps_offload->ctwindow_en = 1;
+                       ctwindow = p2pinfo->ctwindow;
+                       rtl88e_set_p2p_ctw_period_cmd(hw, ctwindow);
+               }
+               /* hw only support 2 set of NoA */
+               for (i = 0; i < p2pinfo->noa_num; i++) {
+                       /* To control the register setting for which NOA*/
+                       rtl_write_byte(rtlpriv, 0x5cf, (i << 4));
+                       if (i == 0)
+                               p2p_ps_offload->noa0_en = 1;
+                       else
+                               p2p_ps_offload->noa1_en = 1;
+
+                       /* config P2P NoA Descriptor Register */
+                       rtl_write_dword(rtlpriv, 0x5E0,
+                                       p2pinfo->noa_duration[i]);
+                       rtl_write_dword(rtlpriv, 0x5E4,
+                                       p2pinfo->noa_interval[i]);
+
+                       /*Get Current TSF value */
+                       tsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
+
+                       start_time = p2pinfo->noa_start_time[i];
+                       if (p2pinfo->noa_count_type[i] != 1) {
+                               while (start_time <= (tsf_low + (50 * 1024))) {
+                                       start_time += p2pinfo->noa_interval[i];
+                                       if (p2pinfo->noa_count_type[i] != 255)
+                                               p2pinfo->noa_count_type[i]--;
+                               }
+                       }
+                       rtl_write_dword(rtlpriv, 0x5E8, start_time);
+                       rtl_write_dword(rtlpriv, 0x5EC,
+                                       p2pinfo->noa_count_type[i]);
+               }
+
+               if ((p2pinfo->opp_ps == 1) || (p2pinfo->noa_num > 0)) {
+                       /* rst p2p circuit */
+                       rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, BIT(4));
+
+                       p2p_ps_offload->offload_en = 1;
+
+                       if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) {
+                               p2p_ps_offload->role = 1;
+                               p2p_ps_offload->allstasleep = 0;
+                       } else {
+                               p2p_ps_offload->role = 0;
+                       }
+
+                       p2p_ps_offload->discovery = 0;
+               }
+               break;
+       case P2P_PS_SCAN:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN\n");
+               p2p_ps_offload->discovery = 1;
+               break;
+       case P2P_PS_SCAN_DONE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN_DONE\n");
+               p2p_ps_offload->discovery = 0;
+               p2pinfo->p2p_ps_state = P2P_PS_ENABLE;
+               break;
+       default:
+               break;
+       }
+
+       rtl88e_fill_h2c_cmd(hw, H2C_88E_P2P_PS_OFFLOAD, 1,
+                           (u8 *)p2p_ps_offload);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/fw.h b/drivers/net/wireless/rtlwifi/rtl8188ee/fw.h
new file mode 100644 (file)
index 0000000..854a987
--- /dev/null
@@ -0,0 +1,301 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92C__FW__H__
+#define __RTL92C__FW__H__
+
+#define FW_8192C_SIZE                          0x8000
+#define FW_8192C_START_ADDRESS                 0x1000
+#define FW_8192C_END_ADDRESS                   0x5FFF
+#define FW_8192C_PAGE_SIZE                     4096
+#define FW_8192C_POLLING_DELAY                 5
+#define FW_8192C_POLLING_TIMEOUT_COUNT         3000
+
+#define IS_FW_HEADER_EXIST(_pfwhdr)            \
+       ((_pfwhdr->signature&0xFFFF) == 0x88E1)
+#define USE_OLD_WOWLAN_DEBUG_FW                        0
+
+#define H2C_88E_RSVDPAGE_LOC_LEN               5
+#define H2C_88E_PWEMODE_LENGTH                 5
+#define H2C_88E_JOINBSSRPT_LENGTH              1
+#define H2C_88E_AP_OFFLOAD_LENGTH              3
+#define H2C_88E_WOWLAN_LENGTH                  3
+#define H2C_88E_KEEP_ALIVE_CTRL_LENGTH         3
+#if (USE_OLD_WOWLAN_DEBUG_FW == 0)
+#define H2C_88E_REMOTE_WAKE_CTRL_LEN           1
+#else
+#define H2C_88E_REMOTE_WAKE_CTRL_LEN           3
+#endif
+#define H2C_88E_AOAC_GLOBAL_INFO_LEN           2
+#define H2C_88E_AOAC_RSVDPAGE_LOC_LEN          7
+
+/* Fw PS state for RPWM.
+ * BIT[2:0] = HW state
+ * BIT[3] = Protocol PS state, 1: register active state, 0: register sleep state
+ * BIT[4] = sub-state
+ */
+#define        FW_PS_GO_ON                     BIT(0)
+#define        FW_PS_TX_NULL                   BIT(1)
+#define        FW_PS_RF_ON                     BIT(2)
+#define        FW_PS_REGISTER_ACTIVE           BIT(3)
+
+#define        FW_PS_DPS                       BIT(0)
+#define        FW_PS_LCLK                      (FW_PS_DPS)
+#define        FW_PS_RF_OFF                    BIT(1)
+#define        FW_PS_ALL_ON                    BIT(2)
+#define        FW_PS_ST_ACTIVE                 BIT(3)
+#define        FW_PS_ISR_ENABLE                BIT(4)
+#define        FW_PS_IMR_ENABLE                BIT(5)
+
+
+#define        FW_PS_ACK                       BIT(6)
+#define        FW_PS_TOGGLE                    BIT(7)
+
+ /* 88E RPWM value*/
+ /* BIT[0] = 1: 32k, 0: 40M*/
+#define        FW_PS_CLOCK_OFF                 BIT(0)          /* 32k*/
+#define        FW_PS_CLOCK_ON                  0               /*40M*/
+
+#define        FW_PS_STATE_MASK                (0x0F)
+#define        FW_PS_STATE_HW_MASK             (0x07)
+/*ISR_ENABLE, IMR_ENABLE, and PS mode should be inherited.*/
+#define        FW_PS_STATE_INT_MASK            (0x3F)
+
+#define        FW_PS_STATE(x)                  (FW_PS_STATE_MASK & (x))
+#define        FW_PS_STATE_HW(x)               (FW_PS_STATE_HW_MASK & (x))
+#define        FW_PS_STATE_INT(x)              (FW_PS_STATE_INT_MASK & (x))
+#define        FW_PS_ISR_VAL(x)                ((x) & 0x70)
+#define        FW_PS_IMR_MASK(x)               ((x) & 0xDF)
+#define        FW_PS_KEEP_IMR(x)               ((x) & 0x20)
+
+#define        FW_PS_STATE_S0                  (FW_PS_DPS)
+#define        FW_PS_STATE_S1                  (FW_PS_LCLK)
+#define        FW_PS_STATE_S2                  (FW_PS_RF_OFF)
+#define        FW_PS_STATE_S3                  (FW_PS_ALL_ON)
+#define        FW_PS_STATE_S4                  ((FW_PS_ST_ACTIVE) | (FW_PS_ALL_ON))
+
+#define        FW_PS_STATE_ALL_ON_88E          (FW_PS_CLOCK_ON)
+#define        FW_PS_STATE_RF_ON_88E           (FW_PS_CLOCK_ON)
+#define        FW_PS_STATE_RF_OFF_88E          (FW_PS_CLOCK_ON)
+#define        FW_PS_STATE_RF_OFF_LOW_PWR_88E  (FW_PS_CLOCK_OFF)
+
+#define        FW_PS_STATE_ALL_ON_92C          (FW_PS_STATE_S4)
+#define        FW_PS_STATE_RF_ON_92C           (FW_PS_STATE_S3)
+#define        FW_PS_STATE_RF_OFF_92C          (FW_PS_STATE_S2)
+#define        FW_PS_STATE_RF_OFF_LOW_PWR_92C  (FW_PS_STATE_S1)
+
+/* For 88E H2C PwrMode Cmd ID 5.*/
+#define        FW_PWR_STATE_ACTIVE     ((FW_PS_RF_ON) | (FW_PS_REGISTER_ACTIVE))
+#define        FW_PWR_STATE_RF_OFF             0
+
+#define        FW_PS_IS_ACK(x)                 ((x) & FW_PS_ACK)
+#define        FW_PS_IS_CLK_ON(x)              ((x) & (FW_PS_RF_OFF | FW_PS_ALL_ON))
+#define        FW_PS_IS_RF_ON(x)               ((x) & (FW_PS_ALL_ON))
+#define        FW_PS_IS_ACTIVE(x)              ((x) & (FW_PS_ST_ACTIVE))
+#define        FW_PS_IS_CPWM_INT(x)            ((x) & 0x40)
+
+#define        FW_CLR_PS_STATE(x)              ((x) = ((x) & (0xF0)))
+
+#define        IS_IN_LOW_POWER_STATE_88E(fwpsstate)            \
+       (FW_PS_STATE(fwpsstate) == FW_PS_CLOCK_OFF)
+
+#define        FW_PWR_STATE_ACTIVE     ((FW_PS_RF_ON) | (FW_PS_REGISTER_ACTIVE))
+#define        FW_PWR_STATE_RF_OFF             0
+
+struct rtl92c_firmware_header {
+       u16 signature;
+       u8 category;
+       u8 function;
+       u16 version;
+       u8 subversion;
+       u8 rsvd1;
+       u8 month;
+       u8 date;
+       u8 hour;
+       u8 minute;
+       u16 ramcodesize;
+       u16 rsvd2;
+       u32 svnindex;
+       u32 rsvd3;
+       u32 rsvd4;
+       u32 rsvd5;
+};
+
+enum rtl8192c_h2c_cmd {
+       H2C_88E_RSVDPAGE = 0,
+       H2C_88E_JOINBSSRPT = 1,
+       H2C_88E_SCAN = 2,
+       H2C_88E_KEEP_ALIVE_CTRL = 3,
+       H2C_88E_DISCONNECT_DECISION = 4,
+#if (USE_OLD_WOWLAN_DEBUG_FW == 1)
+       H2C_88E_WO_WLAN = 5,
+#endif
+       H2C_88E_INIT_OFFLOAD = 6,
+#if (USE_OLD_WOWLAN_DEBUG_FW == 1)
+       H2C_88E_REMOTE_WAKE_CTRL = 7,
+#endif
+       H2C_88E_AP_OFFLOAD = 8,
+       H2C_88E_BCN_RSVDPAGE = 9,
+       H2C_88E_PROBERSP_RSVDPAGE = 10,
+
+       H2C_88E_SETPWRMODE = 0x20,
+       H2C_88E_PS_TUNING_PARA = 0x21,
+       H2C_88E_PS_TUNING_PARA2 = 0x22,
+       H2C_88E_PS_LPS_PARA = 0x23,
+       H2C_88E_P2P_PS_OFFLOAD = 024,
+
+#if (USE_OLD_WOWLAN_DEBUG_FW == 0)
+       H2C_88E_WO_WLAN = 0x80,
+       H2C_88E_REMOTE_WAKE_CTRL = 0x81,
+       H2C_88E_AOAC_GLOBAL_INFO = 0x82,
+       H2C_88E_AOAC_RSVDPAGE = 0x83,
+#endif
+       /* Not defined in new 88E H2C CMD Format */
+       H2C_88E_RA_MASK,
+       H2C_88E_SELECTIVE_SUSPEND_ROF_CMD,
+       H2C_88E_P2P_PS_MODE,
+       H2C_88E_PSD_RESULT,
+       /*Not defined CTW CMD for P2P yet*/
+       H2C_88E_P2P_PS_CTW_CMD,
+       MAX_88E_H2CCMD
+};
+
+#define pagenum_128(_len)      (u32)(((_len)>>7) + ((_len)&0x7F ? 1 : 0))
+
+#define SET_88E_H2CCMD_WOWLAN_FUNC_ENABLE(__cmd, __value)              \
+       SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value)
+#define SET_88E_H2CCMD_WOWLAN_PATTERN_MATCH_ENABLE(__cmd, __value)     \
+       SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value)
+#define SET_88E_H2CCMD_WOWLAN_MAGIC_PKT_ENABLE(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE(__cmd, 2, 1, __value)
+#define SET_88E_H2CCMD_WOWLAN_UNICAST_PKT_ENABLE(__cmd, __value)       \
+       SET_BITS_TO_LE_1BYTE(__cmd, 3, 1, __value)
+#define SET_88E_H2CCMD_WOWLAN_ALL_PKT_DROP(__cmd, __value)             \
+       SET_BITS_TO_LE_1BYTE(__cmd, 4, 1, __value)
+#define SET_88E_H2CCMD_WOWLAN_GPIO_ACTIVE(__cmd, __value)              \
+       SET_BITS_TO_LE_1BYTE(__cmd, 5, 1, __value)
+#define SET_88E_H2CCMD_WOWLAN_REKEY_WAKE_UP(__cmd, __value)            \
+       SET_BITS_TO_LE_1BYTE(__cmd, 6, 1, __value)
+#define SET_88E_H2CCMD_WOWLAN_DISCONNECT_WAKE_UP(__cmd, __value)       \
+       SET_BITS_TO_LE_1BYTE(__cmd, 7, 1, __value)
+#define SET_88E_H2CCMD_WOWLAN_GPIONUM(__cmd, __value)          \
+       SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value)
+#define SET_88E_H2CCMD_WOWLAN_GPIO_DURATION(__cmd, __value)            \
+       SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value)
+
+
+#define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val)                 \
+       SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_PWRMODE_PARM_RLBM(__cmd, __value)           \
+       SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 4, __value)
+#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__cmd, __value)               \
+       SET_BITS_TO_LE_1BYTE((__cmd)+1, 4, 4, __value)
+#define SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value)
+#define SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(__cmd, __value)        \
+       SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value)
+#define SET_H2CCMD_PWRMODE_PARM_PWR_STATE(__cmd, __value)              \
+       SET_BITS_TO_LE_1BYTE((__cmd)+4, 0, 8, __value)
+#define GET_88E_H2CCMD_PWRMODE_PARM_MODE(__cmd)                        \
+       LE_BITS_TO_1BYTE(__cmd, 0, 8)
+
+#define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val)            \
+       SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val)            \
+       SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val)               \
+       SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val)            \
+       SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
+
+/* AP_OFFLOAD */
+#define SET_H2CCMD_AP_OFFLOAD_ON(__cmd, __value)                       \
+       SET_BITS_TO_LE_1BYTE(__cmd, 0, 8, __value)
+#define SET_H2CCMD_AP_OFFLOAD_HIDDEN(__cmd, __value)           \
+       SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value)
+#define SET_H2CCMD_AP_OFFLOAD_DENYANY(__cmd, __value)          \
+       SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value)
+#define SET_H2CCMD_AP_OFFLOAD_WAKEUP_EVT_RPT(__cmd, __value)   \
+       SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value)
+
+/* Keep Alive Control*/
+#define SET_88E_H2CCMD_KEEP_ALIVE_ENABLE(__cmd, __value)               \
+       SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value)
+#define SET_88E_H2CCMD_KEEP_ALIVE_ACCPEPT_USER_DEFINED(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value)
+#define SET_88E_H2CCMD_KEEP_ALIVE_PERIOD(__cmd, __value)               \
+       SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value)
+
+/*REMOTE_WAKE_CTRL */
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_EN(__cmd, __value)             \
+       SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value)
+#if (USE_OLD_WOWLAN_DEBUG_FW == 0)
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_ARP_OFFLOAD_EN(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value)
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_NDP_OFFLOAD_EN(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE(__cmd, 2, 1, __value)
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_GTK_OFFLOAD_EN(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE(__cmd, 3, 1, __value)
+#else
+#define SET_88E_H2_REM_WAKE_ENC_ALG(__cmd, __value)            \
+       SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value)
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_GROUP_ENC_ALG(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value)
+#endif
+
+/* GTK_OFFLOAD */
+#define SET_88E_H2CCMD_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE(__cmd, 0, 8, __value)
+#define SET_88E_H2CCMD_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value)
+
+/* AOAC_RSVDPAGE_LOC */
+#define SET_88E_H2CCMD_AOAC_RSVD_LOC_REM_WAKE_CTRL_INFO(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE((__cmd), 0, 8, __value)
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_ARP_RSP(__cmd, __value)       \
+       SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value)
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_NEIGHBOR_ADV(__cmd, __value) \
+       SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value)
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_RSP(__cmd, __value)       \
+       SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value)
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_INFO(__cmd, __value)      \
+       SET_BITS_TO_LE_1BYTE((__cmd)+4, 0, 8, __value)
+
+int rtl88e_download_fw(struct ieee80211_hw *hw,
+                      bool buse_wake_on_wlan_fw);
+void rtl88e_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id,
+                        u32 cmd_len, u8 *p_cmdbuffer);
+void rtl88e_firmware_selfreset(struct ieee80211_hw *hw);
+void rtl88e_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
+void rtl88e_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw,
+                                     u8 mstatus);
+void rtl88e_set_fw_ap_off_load_cmd(struct ieee80211_hw *hw,  u8 enable);
+void rtl88e_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
+void rtl88e_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
new file mode 100644 (file)
index 0000000..bcff497
--- /dev/null
@@ -0,0 +1,2530 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../efuse.h"
+#include "../base.h"
+#include "../regd.h"
+#include "../cam.h"
+#include "../ps.h"
+#include "../pci.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "dm.h"
+#include "fw.h"
+#include "led.h"
+#include "hw.h"
+#include "pwrseqcmd.h"
+#include "pwrseq.h"
+
+#define LLT_CONFIG             5
+
+static void _rtl88ee_set_bcn_ctrl_reg(struct ieee80211_hw *hw,
+                                     u8 set_bits, u8 clear_bits)
+{
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       rtlpci->reg_bcn_ctrl_val |= set_bits;
+       rtlpci->reg_bcn_ctrl_val &= ~clear_bits;
+
+       rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlpci->reg_bcn_ctrl_val);
+}
+
+static void _rtl88ee_stop_tx_beacon(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 tmp1byte;
+
+       tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2);
+       rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte & (~BIT(6)));
+       rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0x64);
+       tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2);
+       tmp1byte &= ~(BIT(0));
+       rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte);
+}
+
+static void _rtl88ee_resume_tx_beacon(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 tmp1byte;
+
+       tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2);
+       rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte | BIT(6));
+       rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff);
+       tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2);
+       tmp1byte |= BIT(0);
+       rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte);
+}
+
+static void _rtl88ee_enable_bcn_sub_func(struct ieee80211_hw *hw)
+{
+       _rtl88ee_set_bcn_ctrl_reg(hw, 0, BIT(1));
+}
+
+static void _rtl88ee_return_beacon_queue_skb(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[BEACON_QUEUE];
+
+       while (skb_queue_len(&ring->queue)) {
+               struct rtl_tx_desc *entry = &ring->desc[ring->idx];
+               struct sk_buff *skb = __skb_dequeue(&ring->queue);
+
+               pci_unmap_single(rtlpci->pdev,
+                                rtlpriv->cfg->ops->get_desc(
+                                (u8 *)entry, true, HW_DESC_TXBUFF_ADDR),
+                                skb->len, PCI_DMA_TODEVICE);
+               kfree_skb(skb);
+               ring->idx = (ring->idx + 1) % ring->entries;
+       }
+}
+
+static void _rtl88ee_disable_bcn_sub_func(struct ieee80211_hw *hw)
+{
+       _rtl88ee_set_bcn_ctrl_reg(hw, BIT(1), 0);
+}
+
+static void _rtl88ee_set_fw_clock_on(struct ieee80211_hw *hw,
+                                    u8 rpwm_val, bool need_turn_off_ckk)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       bool support_remote_wake_up;
+       u32 count = 0, isr_regaddr, content;
+       bool schedule_timer = need_turn_off_ckk;
+
+       rtlpriv->cfg->ops->get_hw_reg(hw, HAL_DEF_WOWLAN,
+                                     (u8 *)(&support_remote_wake_up));
+       if (!rtlhal->fw_ready)
+               return;
+       if (!rtlpriv->psc.fw_current_inpsmode)
+               return;
+
+       while (1) {
+               spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+               if (rtlhal->fw_clk_change_in_progress) {
+                       while (rtlhal->fw_clk_change_in_progress) {
+                               spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+                               udelay(100);
+                               if (++count > 1000)
+                                       return;
+                               spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+                       }
+                       spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+               } else {
+                       rtlhal->fw_clk_change_in_progress = false;
+                       spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+               }
+       }
+
+       if (IS_IN_LOW_POWER_STATE_88E(rtlhal->fw_ps_state)) {
+               rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_SET_RPWM,
+                                             (u8 *)(&rpwm_val));
+               if (FW_PS_IS_ACK(rpwm_val)) {
+                       isr_regaddr = REG_HISR;
+                       content = rtl_read_dword(rtlpriv, isr_regaddr);
+                       while (!(content & IMR_CPWM) && (count < 500)) {
+                               udelay(50);
+                               count++;
+                               content = rtl_read_dword(rtlpriv, isr_regaddr);
+                       }
+
+                       if (content & IMR_CPWM) {
+                               rtl_write_word(rtlpriv, isr_regaddr, 0x0100);
+                               rtlhal->fw_ps_state = FW_PS_STATE_RF_ON_88E;
+                               RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+                                        "Receive CPWM INT!!! Set pHalData->FwPSState = %X\n",
+                                        rtlhal->fw_ps_state);
+                       }
+               }
+
+               spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+               rtlhal->fw_clk_change_in_progress = false;
+               spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+               if (schedule_timer) {
+                       mod_timer(&rtlpriv->works.fw_clockoff_timer,
+                                 jiffies + MSECS(10));
+               }
+       } else  {
+               spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+               rtlhal->fw_clk_change_in_progress = false;
+               spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+       }
+}
+
+static void _rtl88ee_set_fw_clock_off(struct ieee80211_hw *hw,
+                                     u8 rpwm_val)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl8192_tx_ring *ring;
+       enum rf_pwrstate rtstate;
+       bool schedule_timer = false;
+       u8 queue;
+
+       if (!rtlhal->fw_ready)
+               return;
+       if (!rtlpriv->psc.fw_current_inpsmode)
+               return;
+       if (!rtlhal->allow_sw_to_change_hwclc)
+               return;
+       rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE, (u8 *)(&rtstate));
+       if (rtstate == ERFOFF || rtlpriv->psc.inactive_pwrstate == ERFOFF)
+               return;
+
+       for (queue = 0; queue < RTL_PCI_MAX_TX_QUEUE_COUNT; queue++) {
+               ring = &rtlpci->tx_ring[queue];
+               if (skb_queue_len(&ring->queue)) {
+                       schedule_timer = true;
+                       break;
+               }
+       }
+
+       if (schedule_timer) {
+               mod_timer(&rtlpriv->works.fw_clockoff_timer,
+                         jiffies + MSECS(10));
+               return;
+       }
+
+       if (FW_PS_STATE(rtlhal->fw_ps_state) !=
+           FW_PS_STATE_RF_OFF_LOW_PWR_88E) {
+               spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+               if (!rtlhal->fw_clk_change_in_progress) {
+                       rtlhal->fw_clk_change_in_progress = true;
+                       spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+                       rtlhal->fw_ps_state = FW_PS_STATE(rpwm_val);
+                       rtl_write_word(rtlpriv, REG_HISR, 0x0100);
+                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
+                                                     (u8 *)(&rpwm_val));
+                       spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+                       rtlhal->fw_clk_change_in_progress = false;
+                       spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+               } else {
+                       spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+                       mod_timer(&rtlpriv->works.fw_clockoff_timer,
+                                 jiffies + MSECS(10));
+               }
+       }
+}
+
+static void _rtl88ee_set_fw_ps_rf_on(struct ieee80211_hw *hw)
+{
+       u8 rpwm_val = 0;
+
+       rpwm_val |= (FW_PS_STATE_RF_OFF_88E | FW_PS_ACK);
+       _rtl88ee_set_fw_clock_on(hw, rpwm_val, true);
+}
+
+static void _rtl88ee_set_fw_ps_rf_off_low_power(struct ieee80211_hw *hw)
+{
+       u8 rpwm_val = 0;
+
+       rpwm_val |= FW_PS_STATE_RF_OFF_LOW_PWR_88E;
+       _rtl88ee_set_fw_clock_off(hw, rpwm_val);
+}
+
+void rtl88ee_fw_clk_off_timer_callback(unsigned long data)
+{
+       struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+
+       _rtl88ee_set_fw_ps_rf_off_low_power(hw);
+}
+
+static void _rtl88ee_fwlps_leave(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       bool fw_current_inps = false;
+       u8 rpwm_val = 0, fw_pwrmode = FW_PS_ACTIVE_MODE;
+
+       if (ppsc->low_power_enable) {
+               rpwm_val = (FW_PS_STATE_ALL_ON_88E|FW_PS_ACK);/* RF on */
+               _rtl88ee_set_fw_clock_on(hw, rpwm_val, false);
+               rtlhal->allow_sw_to_change_hwclc = false;
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+                                             (u8 *)(&fw_pwrmode));
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+                                             (u8 *)(&fw_current_inps));
+       } else {
+               rpwm_val = FW_PS_STATE_ALL_ON_88E;      /* RF on */
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
+                                             (u8 *)(&rpwm_val));
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+                                             (u8 *)(&fw_pwrmode));
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+                                             (u8 *)(&fw_current_inps));
+       }
+}
+
+static void _rtl88ee_fwlps_enter(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       bool fw_current_inps = true;
+       u8 rpwm_val;
+
+       if (ppsc->low_power_enable) {
+               rpwm_val = FW_PS_STATE_RF_OFF_LOW_PWR_88E;      /* RF off */
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+                                             (u8 *)(&fw_current_inps));
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+                                             (u8 *)(&ppsc->fwctrl_psmode));
+               rtlhal->allow_sw_to_change_hwclc = true;
+               _rtl88ee_set_fw_clock_off(hw, rpwm_val);
+       } else {
+               rpwm_val = FW_PS_STATE_RF_OFF_88E;      /* RF off */
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+                                             (u8 *)(&fw_current_inps));
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+                                             (u8 *)(&ppsc->fwctrl_psmode));
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
+                                             (u8 *)(&rpwm_val));
+       }
+}
+
+void rtl88ee_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       switch (variable) {
+       case HW_VAR_RCR:
+               *((u32 *)(val)) = rtlpci->receive_config;
+               break;
+       case HW_VAR_RF_STATE:
+               *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state;
+               break;
+       case HW_VAR_FWLPS_RF_ON:{
+                       enum rf_pwrstate rfstate;
+                       u32 val_rcr;
+
+                       rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE,
+                                                     (u8 *)(&rfstate));
+                       if (rfstate == ERFOFF) {
+                               *((bool *)(val)) = true;
+                       } else {
+                               val_rcr = rtl_read_dword(rtlpriv, REG_RCR);
+                               val_rcr &= 0x00070000;
+                               if (val_rcr)
+                                       *((bool *)(val)) = false;
+                               else
+                                       *((bool *)(val)) = true;
+                       }
+                       break;
+               }
+       case HW_VAR_FW_PSMODE_STATUS:
+               *((bool *)(val)) = ppsc->fw_current_inpsmode;
+               break;
+       case HW_VAR_CORRECT_TSF:{
+               u64 tsf;
+               u32 *ptsf_low = (u32 *)&tsf;
+               u32 *ptsf_high = ((u32 *)&tsf) + 1;
+
+               *ptsf_high = rtl_read_dword(rtlpriv, (REG_TSFTR + 4));
+               *ptsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
+
+               *((u64 *)(val)) = tsf;
+               break; }
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "switch case not process %x\n", variable);
+               break;
+       }
+}
+
+void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       u8 idx;
+
+       switch (variable) {
+       case HW_VAR_ETHER_ADDR:
+               for (idx = 0; idx < ETH_ALEN; idx++)
+                       rtl_write_byte(rtlpriv, (REG_MACID + idx), val[idx]);
+               break;
+       case HW_VAR_BASIC_RATE:{
+               u16 rate_cfg = ((u16 *)val)[0];
+               u8 rate_index = 0;
+               rate_cfg = rate_cfg & 0x15f;
+               rate_cfg |= 0x01;
+               rtl_write_byte(rtlpriv, REG_RRSR, rate_cfg & 0xff);
+               rtl_write_byte(rtlpriv, REG_RRSR + 1, (rate_cfg >> 8) & 0xff);
+               while (rate_cfg > 0x1) {
+                       rate_cfg = (rate_cfg >> 1);
+                       rate_index++;
+               }
+               rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, rate_index);
+               break; }
+       case HW_VAR_BSSID:
+               for (idx = 0; idx < ETH_ALEN; idx++)
+                       rtl_write_byte(rtlpriv, (REG_BSSID + idx), val[idx]);
+               break;
+       case HW_VAR_SIFS:
+               rtl_write_byte(rtlpriv, REG_SIFS_CTX + 1, val[0]);
+               rtl_write_byte(rtlpriv, REG_SIFS_TRX + 1, val[1]);
+
+               rtl_write_byte(rtlpriv, REG_SPEC_SIFS + 1, val[0]);
+               rtl_write_byte(rtlpriv, REG_MAC_SPEC_SIFS + 1, val[0]);
+
+               if (!mac->ht_enable)
+                       rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, 0x0e0e);
+               else
+                       rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM,
+                                      *((u16 *)val));
+               break;
+       case HW_VAR_SLOT_TIME:{
+               u8 e_aci;
+
+               RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+                        "HW_VAR_SLOT_TIME %x\n", val[0]);
+
+               rtl_write_byte(rtlpriv, REG_SLOT, val[0]);
+
+               for (e_aci = 0; e_aci < AC_MAX; e_aci++) {
+                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM,
+                                                     (u8 *)(&e_aci));
+               }
+               break; }
+       case HW_VAR_ACK_PREAMBLE:{
+               u8 reg_tmp;
+               u8 short_preamble = (bool) (*(u8 *)val);
+               reg_tmp = rtl_read_byte(rtlpriv, REG_TRXPTCL_CTL+2);
+               if (short_preamble) {
+                       reg_tmp |= 0x02;
+                       rtl_write_byte(rtlpriv, REG_TRXPTCL_CTL + 2, reg_tmp);
+               } else {
+                       reg_tmp |= 0xFD;
+                       rtl_write_byte(rtlpriv, REG_TRXPTCL_CTL + 2, reg_tmp);
+               }
+               break; }
+       case HW_VAR_WPA_CONFIG:
+               rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *)val));
+               break;
+       case HW_VAR_AMPDU_MIN_SPACE:{
+               u8 min_spacing_to_set;
+               u8 sec_min_space;
+
+               min_spacing_to_set = *((u8 *)val);
+               if (min_spacing_to_set <= 7) {
+                       sec_min_space = 0;
+
+                       if (min_spacing_to_set < sec_min_space)
+                               min_spacing_to_set = sec_min_space;
+
+                       mac->min_space_cfg = ((mac->min_space_cfg &
+                                              0xf8) | min_spacing_to_set);
+
+                       *val = min_spacing_to_set;
+
+                       RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+                                "Set HW_VAR_AMPDU_MIN_SPACE: %#x\n",
+                                 mac->min_space_cfg);
+
+                       rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE,
+                                      mac->min_space_cfg);
+               }
+               break; }
+       case HW_VAR_SHORTGI_DENSITY:{
+               u8 density_to_set;
+
+               density_to_set = *((u8 *)val);
+               mac->min_space_cfg |= (density_to_set << 3);
+
+               RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+                        "Set HW_VAR_SHORTGI_DENSITY: %#x\n",
+                         mac->min_space_cfg);
+
+               rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE,
+                              mac->min_space_cfg);
+               break; }
+       case HW_VAR_AMPDU_FACTOR:{
+               u8 regtoset_normal[4] = { 0x41, 0xa8, 0x72, 0xb9 };
+               u8 factor;
+               u8 *reg = NULL;
+               u8 id = 0;
+
+               reg = regtoset_normal;
+
+               factor = *((u8 *)val);
+               if (factor <= 3) {
+                       factor = (1 << (factor + 2));
+                       if (factor > 0xf)
+                               factor = 0xf;
+
+                       for (id = 0; id < 4; id++) {
+                               if ((reg[id] & 0xf0) > (factor << 4))
+                                       reg[id] = (reg[id] & 0x0f) |
+                                                 (factor << 4);
+
+                               if ((reg[id] & 0x0f) > factor)
+                                       reg[id] = (reg[id] & 0xf0) | (factor);
+
+                               rtl_write_byte(rtlpriv, (REG_AGGLEN_LMT + id),
+                                              reg[id]);
+                       }
+
+                       RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+                                "Set HW_VAR_AMPDU_FACTOR: %#x\n", factor);
+               }
+               break; }
+       case HW_VAR_AC_PARAM:{
+               u8 e_aci = *((u8 *)val);
+               rtl88e_dm_init_edca_turbo(hw);
+
+               if (rtlpci->acm_method != eAcmWay2_SW)
+                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL,
+                                                     (u8 *)(&e_aci));
+               break; }
+       case HW_VAR_ACM_CTRL:{
+               u8 e_aci = *((u8 *)val);
+               union aci_aifsn *p_aci_aifsn =
+                   (union aci_aifsn *)(&(mac->ac[0].aifs));
+               u8 acm = p_aci_aifsn->f.acm;
+               u8 acm_ctrl = rtl_read_byte(rtlpriv, REG_ACMHWCTRL);
+
+               acm_ctrl = acm_ctrl | ((rtlpci->acm_method == 2) ? 0x0 : 0x1);
+
+               if (acm) {
+                       switch (e_aci) {
+                       case AC0_BE:
+                               acm_ctrl |= ACMHW_BEQEN;
+                               break;
+                       case AC2_VI:
+                               acm_ctrl |= ACMHW_VIQEN;
+                               break;
+                       case AC3_VO:
+                               acm_ctrl |= ACMHW_VOQEN;
+                               break;
+                       default:
+                               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                                        "HW_VAR_ACM_CTRL acm set failed: eACI is %d\n",
+                                        acm);
+                               break;
+                       }
+               } else {
+                       switch (e_aci) {
+                       case AC0_BE:
+                               acm_ctrl &= (~ACMHW_BEQEN);
+                               break;
+                       case AC2_VI:
+                               acm_ctrl &= (~ACMHW_VIQEN);
+                               break;
+                       case AC3_VO:
+                               acm_ctrl &= (~ACMHW_BEQEN);
+                               break;
+                       default:
+                               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                        "switch case not process\n");
+                               break;
+                       }
+               }
+
+               RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE,
+                        "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] Write 0x%X\n",
+                        acm_ctrl);
+               rtl_write_byte(rtlpriv, REG_ACMHWCTRL, acm_ctrl);
+               break; }
+       case HW_VAR_RCR:
+               rtl_write_dword(rtlpriv, REG_RCR, ((u32 *)(val))[0]);
+               rtlpci->receive_config = ((u32 *)(val))[0];
+               break;
+       case HW_VAR_RETRY_LIMIT:{
+               u8 retry_limit = ((u8 *)(val))[0];
+
+               rtl_write_word(rtlpriv, REG_RL,
+                              retry_limit << RETRY_LIMIT_SHORT_SHIFT |
+                              retry_limit << RETRY_LIMIT_LONG_SHIFT);
+               break; }
+       case HW_VAR_DUAL_TSF_RST:
+               rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, (BIT(0) | BIT(1)));
+               break;
+       case HW_VAR_EFUSE_BYTES:
+               rtlefuse->efuse_usedbytes = *((u16 *)val);
+               break;
+       case HW_VAR_EFUSE_USAGE:
+               rtlefuse->efuse_usedpercentage = *((u8 *)val);
+               break;
+       case HW_VAR_IO_CMD:
+               rtl88e_phy_set_io_cmd(hw, (*(enum io_type *)val));
+               break;
+       case HW_VAR_SET_RPWM:{
+               u8 rpwm_val;
+
+               rpwm_val = rtl_read_byte(rtlpriv, REG_PCIE_HRPWM);
+               udelay(1);
+
+               if (rpwm_val & BIT(7)) {
+                       rtl_write_byte(rtlpriv, REG_PCIE_HRPWM,
+                                      (*(u8 *)val));
+               } else {
+                       rtl_write_byte(rtlpriv, REG_PCIE_HRPWM,
+                                      ((*(u8 *)val) | BIT(7)));
+               }
+               break; }
+       case HW_VAR_H2C_FW_PWRMODE:
+               rtl88e_set_fw_pwrmode_cmd(hw, (*(u8 *)val));
+               break;
+       case HW_VAR_FW_PSMODE_STATUS:
+               ppsc->fw_current_inpsmode = *((bool *)val);
+               break;
+       case HW_VAR_RESUME_CLK_ON:
+               _rtl88ee_set_fw_ps_rf_on(hw);
+               break;
+       case HW_VAR_FW_LPS_ACTION:{
+               bool enter_fwlps = *((bool *)val);
+
+               if (enter_fwlps)
+                       _rtl88ee_fwlps_enter(hw);
+                else
+                       _rtl88ee_fwlps_leave(hw);
+                break; }
+       case HW_VAR_H2C_FW_JOINBSSRPT:{
+               u8 mstatus = (*(u8 *)val);
+               u8 tmp, tmp_reg422, uval;
+               u8 count = 0, dlbcn_count = 0;
+               bool recover = false;
+
+               if (mstatus == RT_MEDIA_CONNECT) {
+                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AID, NULL);
+
+                       tmp = rtl_read_byte(rtlpriv, REG_CR + 1);
+                       rtl_write_byte(rtlpriv, REG_CR + 1, (tmp | BIT(0)));
+
+                       _rtl88ee_set_bcn_ctrl_reg(hw, 0, BIT(3));
+                       _rtl88ee_set_bcn_ctrl_reg(hw, BIT(4), 0);
+
+                       tmp_reg422 = rtl_read_byte(rtlpriv,
+                                                  REG_FWHW_TXQ_CTRL + 2);
+                       rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
+                                      tmp_reg422 & (~BIT(6)));
+                       if (tmp_reg422 & BIT(6))
+                               recover = true;
+
+                       do {
+                               uval = rtl_read_byte(rtlpriv, REG_TDECTRL+2);
+                               rtl_write_byte(rtlpriv, REG_TDECTRL+2,
+                                              (uval | BIT(0)));
+                               _rtl88ee_return_beacon_queue_skb(hw);
+
+                               rtl88e_set_fw_rsvdpagepkt(hw, 0);
+                               uval = rtl_read_byte(rtlpriv, REG_TDECTRL+2);
+                               count = 0;
+                               while (!(uval & BIT(0)) && count < 20) {
+                                       count++;
+                                       udelay(10);
+                                       uval = rtl_read_byte(rtlpriv,
+                                                            REG_TDECTRL+2);
+                               }
+                               dlbcn_count++;
+                       } while (!(uval & BIT(0)) && dlbcn_count < 5);
+
+                       if (uval & BIT(0))
+                               rtl_write_byte(rtlpriv, REG_TDECTRL+2, BIT(0));
+
+                       _rtl88ee_set_bcn_ctrl_reg(hw, BIT(3), 0);
+                       _rtl88ee_set_bcn_ctrl_reg(hw, 0, BIT(4));
+
+                       if (recover) {
+                               rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
+                                              tmp_reg422);
+                       }
+                       rtl_write_byte(rtlpriv, REG_CR + 1, (tmp & ~(BIT(0))));
+               }
+               rtl88e_set_fw_joinbss_report_cmd(hw, (*(u8 *)val));
+               break; }
+       case HW_VAR_H2C_FW_P2P_PS_OFFLOAD:
+               rtl88e_set_p2p_ps_offload_cmd(hw, (*(u8 *)val));
+               break;
+       case HW_VAR_AID:{
+               u16 u2btmp;
+               u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT);
+               u2btmp &= 0xC000;
+               rtl_write_word(rtlpriv, REG_BCN_PSR_RPT, (u2btmp |
+                              mac->assoc_id));
+               break; }
+       case HW_VAR_CORRECT_TSF:{
+               u8 btype_ibss = ((u8 *)(val))[0];
+
+               if (btype_ibss == true)
+                       _rtl88ee_stop_tx_beacon(hw);
+
+               _rtl88ee_set_bcn_ctrl_reg(hw, 0, BIT(3));
+
+               rtl_write_dword(rtlpriv, REG_TSFTR,
+                               (u32) (mac->tsf & 0xffffffff));
+               rtl_write_dword(rtlpriv, REG_TSFTR + 4,
+                               (u32) ((mac->tsf >> 32) & 0xffffffff));
+
+               _rtl88ee_set_bcn_ctrl_reg(hw, BIT(3), 0);
+
+               if (btype_ibss == true)
+                       _rtl88ee_resume_tx_beacon(hw);
+               break; }
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "switch case not process %x\n", variable);
+               break;
+       }
+}
+
+static bool _rtl88ee_llt_write(struct ieee80211_hw *hw, u32 address, u32 data)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       bool status = true;
+       long count = 0;
+       u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) |
+                   _LLT_OP(_LLT_WRITE_ACCESS);
+
+       rtl_write_dword(rtlpriv, REG_LLT_INIT, value);
+
+       do {
+               value = rtl_read_dword(rtlpriv, REG_LLT_INIT);
+               if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value))
+                       break;
+
+               if (count > POLLING_LLT_THRESHOLD) {
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "Failed to polling write LLT done at address %d!\n",
+                                address);
+                       status = false;
+                       break;
+               }
+       } while (++count);
+
+       return status;
+}
+
+static bool _rtl88ee_llt_table_init(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       unsigned short i;
+       u8 txpktbuf_bndy;
+       u8 maxpage;
+       bool status;
+
+       maxpage = 0xAF;
+       txpktbuf_bndy = 0xAB;
+
+       rtl_write_byte(rtlpriv, REG_RQPN_NPQ, 0x01);
+       rtl_write_dword(rtlpriv, REG_RQPN, 0x80730d29);
+
+
+       rtl_write_dword(rtlpriv, REG_TRXFF_BNDY, (0x25FF0000 | txpktbuf_bndy));
+       rtl_write_byte(rtlpriv, REG_TDECTRL + 1, txpktbuf_bndy);
+
+       rtl_write_byte(rtlpriv, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy);
+       rtl_write_byte(rtlpriv, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy);
+
+       rtl_write_byte(rtlpriv, 0x45D, txpktbuf_bndy);
+       rtl_write_byte(rtlpriv, REG_PBP, 0x11);
+       rtl_write_byte(rtlpriv, REG_RX_DRVINFO_SZ, 0x4);
+
+       for (i = 0; i < (txpktbuf_bndy - 1); i++) {
+               status = _rtl88ee_llt_write(hw, i, i + 1);
+               if (true != status)
+                       return status;
+       }
+
+       status = _rtl88ee_llt_write(hw, (txpktbuf_bndy - 1), 0xFF);
+       if (true != status)
+               return status;
+
+       for (i = txpktbuf_bndy; i < maxpage; i++) {
+               status = _rtl88ee_llt_write(hw, i, (i + 1));
+               if (true != status)
+                       return status;
+       }
+
+       status = _rtl88ee_llt_write(hw, maxpage, txpktbuf_bndy);
+       if (true != status)
+               return status;
+
+       return true;
+}
+
+static void _rtl88ee_gen_refresh_led_state(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+
+       if (rtlpriv->rtlhal.up_first_time)
+               return;
+
+       if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS)
+               rtl88ee_sw_led_on(hw, pLed0);
+       else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT)
+               rtl88ee_sw_led_on(hw, pLed0);
+       else
+               rtl88ee_sw_led_off(hw, pLed0);
+}
+
+static bool _rtl88ee_init_mac(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u8 bytetmp;
+       u16 wordtmp;
+
+       /*Disable XTAL OUTPUT for power saving. YJ, add, 111206. */
+       bytetmp = rtl_read_byte(rtlpriv, REG_XCK_OUT_CTRL) & (~BIT(0));
+       rtl_write_byte(rtlpriv, REG_XCK_OUT_CTRL, bytetmp);
+       /*Auto Power Down to CHIP-off State*/
+       bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1) & (~BIT(7));
+       rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, bytetmp);
+
+       rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00);
+       /* HW Power on sequence */
+       if (!rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK,
+                                       PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,
+                                       Rtl8188E_NIC_ENABLE_FLOW)) {
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                        "init MAC Fail as rtl88_hal_pwrseqcmdparsing\n");
+               return false;
+       }
+
+       bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO) | BIT(4);
+       rtl_write_byte(rtlpriv, REG_APS_FSMCO, bytetmp);
+
+       bytetmp = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG+2);
+       rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG+2, bytetmp|BIT(2));
+
+       bytetmp = rtl_read_byte(rtlpriv, REG_WATCH_DOG+1);
+       rtl_write_byte(rtlpriv, REG_WATCH_DOG+1, bytetmp|BIT(7));
+
+       bytetmp = rtl_read_byte(rtlpriv, REG_AFE_XTAL_CTRL_EXT+1);
+       rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL_EXT+1, bytetmp|BIT(1));
+
+       bytetmp = rtl_read_byte(rtlpriv, REG_TX_RPT_CTRL);
+       rtl_write_byte(rtlpriv, REG_TX_RPT_CTRL, bytetmp|BIT(1)|BIT(0));
+       rtl_write_byte(rtlpriv, REG_TX_RPT_CTRL+1, 2);
+       rtl_write_word(rtlpriv, REG_TX_RPT_TIME, 0xcdf0);
+
+       /*Add for wake up online*/
+       bytetmp = rtl_read_byte(rtlpriv, REG_SYS_CLKR);
+
+       rtl_write_byte(rtlpriv, REG_SYS_CLKR, bytetmp|BIT(3));
+       bytetmp = rtl_read_byte(rtlpriv, REG_GPIO_MUXCFG+1);
+       rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG+1, (bytetmp & (~BIT(4))));
+       rtl_write_byte(rtlpriv, 0x367, 0x80);
+
+       rtl_write_word(rtlpriv, REG_CR, 0x2ff);
+       rtl_write_byte(rtlpriv, REG_CR+1, 0x06);
+       rtl_write_byte(rtlpriv, REG_CR+2, 0x00);
+
+       if (!rtlhal->mac_func_enable) {
+               if (_rtl88ee_llt_table_init(hw) == false) {
+                       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                                "LLT table init fail\n");
+                       return false;
+               }
+       }
+
+
+       rtl_write_dword(rtlpriv, REG_HISR, 0xffffffff);
+       rtl_write_dword(rtlpriv, REG_HISRE, 0xffffffff);
+
+       wordtmp = rtl_read_word(rtlpriv, REG_TRXDMA_CTRL);
+       wordtmp &= 0xf;
+       wordtmp |= 0xE771;
+       rtl_write_word(rtlpriv, REG_TRXDMA_CTRL, wordtmp);
+
+       rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
+       rtl_write_word(rtlpriv, REG_RXFLTMAP2, 0xffff);
+       rtl_write_dword(rtlpriv, REG_TCR, rtlpci->transmit_config);
+
+       rtl_write_dword(rtlpriv, REG_BCNQ_DESA,
+                       ((u64) rtlpci->tx_ring[BEACON_QUEUE].dma) &
+                       DMA_BIT_MASK(32));
+       rtl_write_dword(rtlpriv, REG_MGQ_DESA,
+                       (u64) rtlpci->tx_ring[MGNT_QUEUE].dma &
+                       DMA_BIT_MASK(32));
+       rtl_write_dword(rtlpriv, REG_VOQ_DESA,
+                       (u64) rtlpci->tx_ring[VO_QUEUE].dma & DMA_BIT_MASK(32));
+       rtl_write_dword(rtlpriv, REG_VIQ_DESA,
+                       (u64) rtlpci->tx_ring[VI_QUEUE].dma & DMA_BIT_MASK(32));
+       rtl_write_dword(rtlpriv, REG_BEQ_DESA,
+                       (u64) rtlpci->tx_ring[BE_QUEUE].dma & DMA_BIT_MASK(32));
+       rtl_write_dword(rtlpriv, REG_BKQ_DESA,
+                       (u64) rtlpci->tx_ring[BK_QUEUE].dma & DMA_BIT_MASK(32));
+       rtl_write_dword(rtlpriv, REG_HQ_DESA,
+                       (u64) rtlpci->tx_ring[HIGH_QUEUE].dma &
+                       DMA_BIT_MASK(32));
+       rtl_write_dword(rtlpriv, REG_RX_DESA,
+                       (u64) rtlpci->rx_ring[RX_MPDU_QUEUE].dma &
+                       DMA_BIT_MASK(32));
+
+       /* if we want to support 64 bit DMA, we should set it here,
+        * but at the moment we do not support 64 bit DMA
+        */
+
+       rtl_write_dword(rtlpriv, REG_INT_MIG, 0);
+
+       rtl_write_dword(rtlpriv, REG_MCUTST_1, 0x0);
+       rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG+1, 0);/*Enable RX DMA */
+
+       if (rtlhal->earlymode_enable) {/*Early mode enable*/
+               bytetmp = rtl_read_byte(rtlpriv, REG_EARLY_MODE_CONTROL);
+               bytetmp |= 0x1f;
+               rtl_write_byte(rtlpriv, REG_EARLY_MODE_CONTROL, bytetmp);
+               rtl_write_byte(rtlpriv, REG_EARLY_MODE_CONTROL+3, 0x81);
+       }
+       _rtl88ee_gen_refresh_led_state(hw);
+       return true;
+}
+
+static void _rtl88ee_hw_configure(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 reg_prsr;
+
+       reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+
+       rtl_write_dword(rtlpriv, REG_RRSR, reg_prsr);
+       rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, 0xFF);
+}
+
+static void _rtl88ee_enable_aspm_back_door(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       u8 tmp1byte = 0;
+       u32 tmp4Byte = 0, count;
+
+       rtl_write_word(rtlpriv, 0x354, 0x8104);
+       rtl_write_word(rtlpriv, 0x358, 0x24);
+
+       rtl_write_word(rtlpriv, 0x350, 0x70c);
+       rtl_write_byte(rtlpriv, 0x352, 0x2);
+       tmp1byte = rtl_read_byte(rtlpriv, 0x352);
+       count = 0;
+       while (tmp1byte && count < 20) {
+               udelay(10);
+               tmp1byte = rtl_read_byte(rtlpriv, 0x352);
+               count++;
+       }
+       if (0 == tmp1byte) {
+               tmp4Byte = rtl_read_dword(rtlpriv, 0x34c);
+               rtl_write_dword(rtlpriv, 0x348, tmp4Byte|BIT(31));
+               rtl_write_word(rtlpriv, 0x350, 0xf70c);
+               rtl_write_byte(rtlpriv, 0x352, 0x1);
+       }
+
+       tmp1byte = rtl_read_byte(rtlpriv, 0x352);
+       count = 0;
+       while (tmp1byte && count < 20) {
+               udelay(10);
+               tmp1byte = rtl_read_byte(rtlpriv, 0x352);
+               count++;
+       }
+
+       rtl_write_word(rtlpriv, 0x350, 0x718);
+       rtl_write_byte(rtlpriv, 0x352, 0x2);
+       tmp1byte = rtl_read_byte(rtlpriv, 0x352);
+       count = 0;
+       while (tmp1byte && count < 20) {
+               udelay(10);
+               tmp1byte = rtl_read_byte(rtlpriv, 0x352);
+               count++;
+       }
+       if (ppsc->support_backdoor || (0 == tmp1byte)) {
+               tmp4Byte = rtl_read_dword(rtlpriv, 0x34c);
+               rtl_write_dword(rtlpriv, 0x348, tmp4Byte|BIT(11)|BIT(12));
+               rtl_write_word(rtlpriv, 0x350, 0xf718);
+               rtl_write_byte(rtlpriv, 0x352, 0x1);
+       }
+       tmp1byte = rtl_read_byte(rtlpriv, 0x352);
+       count = 0;
+       while (tmp1byte && count < 20) {
+               udelay(10);
+               tmp1byte = rtl_read_byte(rtlpriv, 0x352);
+               count++;
+       }
+}
+
+void rtl88ee_enable_hw_security_config(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 sec_reg_value;
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+                "PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n",
+                rtlpriv->sec.pairwise_enc_algorithm,
+                rtlpriv->sec.group_enc_algorithm);
+
+       if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) {
+               RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+                        "not open hw encryption\n");
+               return;
+       }
+       sec_reg_value = SCR_TXENCENABLE | SCR_RXDECENABLE;
+
+       if (rtlpriv->sec.use_defaultkey) {
+               sec_reg_value |= SCR_TXUSEDK;
+               sec_reg_value |= SCR_RXUSEDK;
+       }
+
+       sec_reg_value |= (SCR_RXBCUSEDK | SCR_TXBCUSEDK);
+
+       rtl_write_byte(rtlpriv, REG_CR + 1, 0x02);
+
+       RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+                "The SECR-value %x\n", sec_reg_value);
+       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value);
+}
+
+int rtl88ee_hw_init(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       bool rtstatus = true;
+       int err = 0;
+       u8 tmp_u1b, u1byte;
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Rtl8188EE hw init\n");
+       rtlpriv->rtlhal.being_init_adapter = true;
+       rtlpriv->intf_ops->disable_aspm(hw);
+
+       tmp_u1b = rtl_read_byte(rtlpriv, REG_SYS_CLKR+1);
+       u1byte = rtl_read_byte(rtlpriv, REG_CR);
+       if ((tmp_u1b & BIT(3)) && (u1byte != 0 && u1byte != 0xEA)) {
+               rtlhal->mac_func_enable = true;
+       } else {
+               rtlhal->mac_func_enable = false;
+               rtlhal->fw_ps_state = FW_PS_STATE_ALL_ON_88E;
+       }
+
+       rtstatus = _rtl88ee_init_mac(hw);
+       if (rtstatus != true) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n");
+               err = 1;
+               return err;
+       }
+
+       err = rtl88e_download_fw(hw, false);
+       if (err) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                        "Failed to download FW. Init HW without FW now..\n");
+               err = 1;
+               rtlhal->fw_ready = false;
+               return err;
+       } else {
+               rtlhal->fw_ready = true;
+       }
+       /*fw related variable initialize */
+       rtlhal->last_hmeboxnum = 0;
+       rtlhal->fw_ps_state = FW_PS_STATE_ALL_ON_88E;
+       rtlhal->fw_clk_change_in_progress = false;
+       rtlhal->allow_sw_to_change_hwclc = false;
+       ppsc->fw_current_inpsmode = false;
+
+       rtl88e_phy_mac_config(hw);
+       /* because last function modifies RCR, we update
+        * rcr var here, or TP will be unstable for receive_config
+        * is wrong, RX RCR_ACRC32 will cause TP unstable & Rx
+        * RCR_APP_ICV will cause mac80211 disassoc for cisco 1252
+        */
+       rtlpci->receive_config &= ~(RCR_ACRC32 | RCR_AICV);
+       rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
+
+       rtl88e_phy_bb_config(hw);
+       rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1);
+       rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1);
+
+       rtlphy->rf_mode = RF_OP_BY_SW_3WIRE;
+       rtl88e_phy_rf_config(hw);
+
+       rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0,
+                                                RF_CHNLBW, RFREG_OFFSET_MASK);
+       rtlphy->rfreg_chnlval[0] = rtlphy->rfreg_chnlval[0] & 0xfff00fff;
+
+       _rtl88ee_hw_configure(hw);
+       rtl_cam_reset_all_entry(hw);
+       rtl88ee_enable_hw_security_config(hw);
+
+       rtlhal->mac_func_enable = true;
+       ppsc->rfpwr_state = ERFON;
+
+       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr);
+       _rtl88ee_enable_aspm_back_door(hw);
+       rtlpriv->intf_ops->enable_aspm(hw);
+
+       if (ppsc->rfpwr_state == ERFON) {
+               if ((rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV) ||
+                   ((rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) &&
+                   (rtlhal->oem_id == RT_CID_819x_HP))) {
+                       rtl88e_phy_set_rfpath_switch(hw, true);
+                       rtlpriv->dm.fat_table.rx_idle_ant = MAIN_ANT;
+               } else {
+                       rtl88e_phy_set_rfpath_switch(hw, false);
+                       rtlpriv->dm.fat_table.rx_idle_ant = AUX_ANT;
+               }
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                        "rx idle ant %s\n",
+                        (rtlpriv->dm.fat_table.rx_idle_ant == MAIN_ANT) ?
+                        ("MAIN_ANT") : ("AUX_ANT"));
+
+               if (rtlphy->iqk_initialized) {
+                       rtl88e_phy_iq_calibrate(hw, true);
+               } else {
+                       rtl88e_phy_iq_calibrate(hw, false);
+                       rtlphy->iqk_initialized = true;
+               }
+               rtl88e_dm_check_txpower_tracking(hw);
+               rtl88e_phy_lc_calibrate(hw);
+       }
+
+       tmp_u1b = efuse_read_1byte(hw, 0x1FA);
+       if (!(tmp_u1b & BIT(0))) {
+               rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0F, 0x05);
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "PA BIAS path A\n");
+       }
+
+       if (!(tmp_u1b & BIT(4))) {
+               tmp_u1b = rtl_read_byte(rtlpriv, 0x16);
+               tmp_u1b &= 0x0F;
+               rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x80);
+               udelay(10);
+               rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x90);
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "under 1.5V\n");
+       }
+       rtl_write_byte(rtlpriv, REG_NAV_CTRL+2,  ((30000+127)/128));
+       rtl88e_dm_init(hw);
+       rtlpriv->rtlhal.being_init_adapter = false;
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "end of Rtl8188EE hw init %x\n",
+                err);
+       return 0;
+}
+
+static enum version_8188e _rtl88ee_read_chip_version(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       enum version_8188e version = VERSION_UNKNOWN;
+       u32 value32;
+
+       value32 = rtl_read_dword(rtlpriv, REG_SYS_CFG);
+       if (value32 & TRP_VAUX_EN) {
+               version = (enum version_8188e) VERSION_TEST_CHIP_88E;
+       } else {
+               version = NORMAL_CHIP;
+               version = version | ((value32 & TYPE_ID) ? RF_TYPE_2T2R : 0);
+               version = version | ((value32 & VENDOR_ID) ?
+                         CHIP_VENDOR_UMC : 0);
+       }
+
+       rtlphy->rf_type = RF_1T1R;
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "Chip RF Type: %s\n", (rtlphy->rf_type == RF_2T2R) ?
+                "RF_2T2R" : "RF_1T1R");
+
+       return version;
+}
+
+static int _rtl88ee_set_media_status(struct ieee80211_hw *hw,
+                                    enum nl80211_iftype type)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u8 bt_msr = rtl_read_byte(rtlpriv, MSR);
+       enum led_ctl_mode ledaction = LED_CTL_NO_LINK;
+       bt_msr &= 0xfc;
+
+       if (type == NL80211_IFTYPE_UNSPECIFIED ||
+           type == NL80211_IFTYPE_STATION) {
+               _rtl88ee_stop_tx_beacon(hw);
+               _rtl88ee_enable_bcn_sub_func(hw);
+       } else if (type == NL80211_IFTYPE_ADHOC ||
+               type == NL80211_IFTYPE_AP ||
+               type == NL80211_IFTYPE_MESH_POINT) {
+               _rtl88ee_resume_tx_beacon(hw);
+               _rtl88ee_disable_bcn_sub_func(hw);
+       } else {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                        "Set HW_VAR_MEDIA_STATUS: No such media status(%x).\n",
+                        type);
+       }
+
+       switch (type) {
+       case NL80211_IFTYPE_UNSPECIFIED:
+               bt_msr |= MSR_NOLINK;
+               ledaction = LED_CTL_LINK;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "Set Network type to NO LINK!\n");
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               bt_msr |= MSR_ADHOC;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "Set Network type to Ad Hoc!\n");
+               break;
+       case NL80211_IFTYPE_STATION:
+               bt_msr |= MSR_INFRA;
+               ledaction = LED_CTL_LINK;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "Set Network type to STA!\n");
+               break;
+       case NL80211_IFTYPE_AP:
+               bt_msr |= MSR_AP;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "Set Network type to AP!\n");
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               bt_msr |= MSR_ADHOC;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "Set Network type to Mesh Point!\n");
+               break;
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "Network type %d not support!\n", type);
+               return 1;
+       }
+
+       rtl_write_byte(rtlpriv, (MSR), bt_msr);
+       rtlpriv->cfg->ops->led_control(hw, ledaction);
+       if ((bt_msr & 0xfc) == MSR_AP)
+               rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
+       else
+               rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
+       return 0;
+}
+
+void rtl88ee_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       u32 reg_rcr = rtlpci->receive_config;
+
+       if (rtlpriv->psc.rfpwr_state != ERFON)
+               return;
+
+       if (check_bssid == true) {
+               reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
+                                             (u8 *)(&reg_rcr));
+               _rtl88ee_set_bcn_ctrl_reg(hw, 0, BIT(4));
+       } else if (check_bssid == false) {
+               reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN));
+               _rtl88ee_set_bcn_ctrl_reg(hw, BIT(4), 0);
+               rtlpriv->cfg->ops->set_hw_reg(hw,
+                       HW_VAR_RCR, (u8 *)(&reg_rcr));
+       }
+}
+
+int rtl88ee_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       if (_rtl88ee_set_media_status(hw, type))
+               return -EOPNOTSUPP;
+
+       if (rtlpriv->mac80211.link_state == MAC80211_LINKED) {
+               if (type != NL80211_IFTYPE_AP &&
+                   type != NL80211_IFTYPE_MESH_POINT)
+                       rtl88ee_set_check_bssid(hw, true);
+       } else {
+               rtl88ee_set_check_bssid(hw, false);
+       }
+
+       return 0;
+}
+
+/* don't set REG_EDCA_BE_PARAM here because mac80211 will send pkt when scan */
+void rtl88ee_set_qos(struct ieee80211_hw *hw, int aci)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       rtl88e_dm_init_edca_turbo(hw);
+       switch (aci) {
+       case AC1_BK:
+               rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f);
+               break;
+       case AC0_BE:
+               break;
+       case AC2_VI:
+               rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322);
+               break;
+       case AC3_VO:
+               rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222);
+               break;
+       default:
+               RT_ASSERT(false, "invalid aci: %d !\n", aci);
+               break;
+       }
+}
+
+void rtl88ee_enable_interrupt(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
+       rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
+       rtlpci->irq_enabled = true;
+       /* there are some C2H CMDs have been sent before system interrupt
+        * is enabled, e.g., C2H, CPWM.
+        * So we need to clear all C2H events that FW has notified, otherwise
+        * FW won't schedule any commands anymore.
+        */
+       rtl_write_byte(rtlpriv, REG_C2HEVT_CLEAR, 0);
+       /*enable system interrupt*/
+       rtl_write_dword(rtlpriv, REG_HSIMR, rtlpci->sys_irq_mask & 0xFFFFFFFF);
+}
+
+void rtl88ee_disable_interrupt(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       rtl_write_dword(rtlpriv, REG_HIMR, IMR_DISABLED);
+       rtl_write_dword(rtlpriv, REG_HIMRE, IMR_DISABLED);
+       rtlpci->irq_enabled = false;
+       synchronize_irq(rtlpci->pdev->irq);
+}
+
+static void _rtl88ee_poweroff_adapter(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u8 u1b_tmp;
+       u32 count = 0;
+       rtlhal->mac_func_enable = false;
+       rtlpriv->intf_ops->enable_aspm(hw);
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "POWER OFF adapter\n");
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_TX_RPT_CTRL);
+       rtl_write_byte(rtlpriv, REG_TX_RPT_CTRL, u1b_tmp & (~BIT(1)));
+
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL);
+       while (!(u1b_tmp & BIT(1)) && (count++ < 100)) {
+               udelay(10);
+               u1b_tmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL);
+               count++;
+       }
+       rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG+1, 0xFF);
+
+       rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+                                  PWR_INTF_PCI_MSK,
+                                  Rtl8188E_NIC_LPS_ENTER_FLOW);
+
+       rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00);
+
+       if ((rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) && rtlhal->fw_ready)
+               rtl88e_firmware_selfreset(hw);
+
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN+1);
+       rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2))));
+       rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00);
+
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_32K_CTRL);
+       rtl_write_byte(rtlpriv, REG_32K_CTRL, (u1b_tmp & (~BIT(0))));
+
+       rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+                                  PWR_INTF_PCI_MSK, Rtl8188E_NIC_DISABLE_FLOW);
+
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL+1);
+       rtl_write_byte(rtlpriv, REG_RSV_CTRL+1, (u1b_tmp & (~BIT(3))));
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL+1);
+       rtl_write_byte(rtlpriv, REG_RSV_CTRL+1, (u1b_tmp | BIT(3)));
+
+       rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0E);
+
+       u1b_tmp = rtl_read_byte(rtlpriv, GPIO_IN);
+       rtl_write_byte(rtlpriv, GPIO_OUT, u1b_tmp);
+       rtl_write_byte(rtlpriv, GPIO_IO_SEL, 0x7F);
+
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL);
+       rtl_write_byte(rtlpriv, REG_GPIO_IO_SEL, (u1b_tmp << 4) | u1b_tmp);
+       u1b_tmp = rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL+1);
+       rtl_write_byte(rtlpriv, REG_GPIO_IO_SEL+1, u1b_tmp | 0x0F);
+
+       rtl_write_dword(rtlpriv, REG_GPIO_IO_SEL_2+2, 0x00080808);
+}
+
+void rtl88ee_card_disable(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       enum nl80211_iftype opmode;
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "RTL8188ee card disable\n");
+
+       mac->link_state = MAC80211_NOLINK;
+       opmode = NL80211_IFTYPE_UNSPECIFIED;
+
+       _rtl88ee_set_media_status(hw, opmode);
+
+       if (rtlpriv->rtlhal.driver_is_goingto_unload ||
+           ppsc->rfoff_reason > RF_CHANGE_BY_PS)
+               rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF);
+
+       RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+       _rtl88ee_poweroff_adapter(hw);
+
+       /* after power off we should do iqk again */
+       rtlpriv->phy.iqk_initialized = false;
+}
+
+void rtl88ee_interrupt_recognized(struct ieee80211_hw *hw,
+                                 u32 *p_inta, u32 *p_intb)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       *p_inta = rtl_read_dword(rtlpriv, ISR) & rtlpci->irq_mask[0];
+       rtl_write_dword(rtlpriv, ISR, *p_inta);
+
+       *p_intb = rtl_read_dword(rtlpriv, REG_HISRE) & rtlpci->irq_mask[1];
+       rtl_write_dword(rtlpriv, REG_HISRE, *p_intb);
+}
+
+void rtl88ee_set_beacon_related_registers(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       u16 bcn_interval, atim_window;
+
+       bcn_interval = mac->beacon_interval;
+       atim_window = 2;        /*FIX MERGE */
+       rtl88ee_disable_interrupt(hw);
+       rtl_write_word(rtlpriv, REG_ATIMWND, atim_window);
+       rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval);
+       rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660f);
+       rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x18);
+       rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x18);
+       rtl_write_byte(rtlpriv, 0x606, 0x30);
+       rtlpci->reg_bcn_ctrl_val |= BIT(3);
+       rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlpci->reg_bcn_ctrl_val);
+       /*rtl88ee_enable_interrupt(hw);*/
+}
+
+void rtl88ee_set_beacon_interval(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       u16 bcn_interval = mac->beacon_interval;
+
+       RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+                "beacon_interval:%d\n", bcn_interval);
+       /*rtl88ee_disable_interrupt(hw);*/
+       rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval);
+       /*rtl88ee_enable_interrupt(hw);*/
+}
+
+void rtl88ee_update_interrupt_mask(struct ieee80211_hw *hw,
+                                  u32 add_msr, u32 rm_msr)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD,
+                "add_msr:%x, rm_msr:%x\n", add_msr, rm_msr);
+
+       rtl88ee_disable_interrupt(hw);
+       if (add_msr)
+               rtlpci->irq_mask[0] |= add_msr;
+       if (rm_msr)
+               rtlpci->irq_mask[0] &= (~rm_msr);
+       rtl88ee_enable_interrupt(hw);
+}
+
+static inline u8 get_chnl_group(u8 chnl)
+{
+       u8 group;
+
+       group = chnl / 3;
+       if (chnl == 14)
+               group = 5;
+
+       return group;
+}
+
+static void set_diff0_2g(struct txpower_info_2g *pwr2g, u8 *hwinfo, u32 path,
+                        u32 i, u32 eadr)
+{
+       pwr2g->bw40_diff[path][i] = 0;
+       if (hwinfo[eadr] == 0xFF) {
+               pwr2g->bw20_diff[path][i] = 0x02;
+       } else {
+               pwr2g->bw20_diff[path][i] = (hwinfo[eadr]&0xf0)>>4;
+               /*bit sign number to 8 bit sign number*/
+               if (pwr2g->bw20_diff[path][i] & BIT(3))
+                       pwr2g->bw20_diff[path][i] |= 0xF0;
+       }
+
+       if (hwinfo[eadr] == 0xFF) {
+               pwr2g->ofdm_diff[path][i] = 0x04;
+       } else {
+               pwr2g->ofdm_diff[path][i] = (hwinfo[eadr] & 0x0f);
+               /*bit sign number to 8 bit sign number*/
+               if (pwr2g->ofdm_diff[path][i] & BIT(3))
+                       pwr2g->ofdm_diff[path][i] |= 0xF0;
+       }
+       pwr2g->cck_diff[path][i] = 0;
+}
+
+static void set_diff0_5g(struct txpower_info_5g *pwr5g, u8 *hwinfo, u32 path,
+                        u32 i, u32 eadr)
+{
+       pwr5g->bw40_diff[path][i] = 0;
+       if (hwinfo[eadr] == 0xFF) {
+               pwr5g->bw20_diff[path][i] = 0;
+       } else {
+               pwr5g->bw20_diff[path][i] = (hwinfo[eadr]&0xf0)>>4;
+               /*bit sign number to 8 bit sign number*/
+               if (pwr5g->bw20_diff[path][i] & BIT(3))
+                       pwr5g->bw20_diff[path][i] |= 0xF0;
+       }
+
+       if (hwinfo[eadr] == 0xFF) {
+               pwr5g->ofdm_diff[path][i] = 0x04;
+       } else {
+               pwr5g->ofdm_diff[path][i] = (hwinfo[eadr] & 0x0f);
+               /*bit sign number to 8 bit sign number*/
+               if (pwr5g->ofdm_diff[path][i] & BIT(3))
+                       pwr5g->ofdm_diff[path][i] |= 0xF0;
+       }
+}
+
+static void set_diff1_2g(struct txpower_info_2g *pwr2g, u8 *hwinfo, u32 path,
+                        u32 i, u32 eadr)
+{
+       if (hwinfo[eadr] == 0xFF) {
+               pwr2g->bw40_diff[path][i] = 0xFE;
+       } else {
+               pwr2g->bw40_diff[path][i] = (hwinfo[eadr]&0xf0)>>4;
+               if (pwr2g->bw40_diff[path][i] & BIT(3))
+                       pwr2g->bw40_diff[path][i] |= 0xF0;
+       }
+
+       if (hwinfo[eadr] == 0xFF) {
+               pwr2g->bw20_diff[path][i] = 0xFE;
+       } else {
+               pwr2g->bw20_diff[path][i] = (hwinfo[eadr]&0x0f);
+               if (pwr2g->bw20_diff[path][i] & BIT(3))
+                       pwr2g->bw20_diff[path][i] |= 0xF0;
+       }
+}
+
+static void set_diff1_5g(struct txpower_info_5g *pwr5g, u8 *hwinfo, u32 path,
+                        u32 i, u32 eadr)
+{
+       if (hwinfo[eadr] == 0xFF) {
+               pwr5g->bw40_diff[path][i] = 0xFE;
+       } else {
+               pwr5g->bw40_diff[path][i] = (hwinfo[eadr]&0xf0)>>4;
+               if (pwr5g->bw40_diff[path][i] & BIT(3))
+                       pwr5g->bw40_diff[path][i] |= 0xF0;
+       }
+
+       if (hwinfo[eadr] == 0xFF) {
+               pwr5g->bw20_diff[path][i] = 0xFE;
+       } else {
+               pwr5g->bw20_diff[path][i] = (hwinfo[eadr] & 0x0f);
+               if (pwr5g->bw20_diff[path][i] & BIT(3))
+                       pwr5g->bw20_diff[path][i] |= 0xF0;
+       }
+}
+
+static void set_diff2_2g(struct txpower_info_2g *pwr2g, u8 *hwinfo, u32 path,
+                        u32 i, u32 eadr)
+{
+       if (hwinfo[eadr] == 0xFF) {
+               pwr2g->ofdm_diff[path][i] = 0xFE;
+       } else {
+               pwr2g->ofdm_diff[path][i] = (hwinfo[eadr]&0xf0)>>4;
+               if (pwr2g->ofdm_diff[path][i] & BIT(3))
+                       pwr2g->ofdm_diff[path][i] |= 0xF0;
+       }
+
+       if (hwinfo[eadr] == 0xFF) {
+               pwr2g->cck_diff[path][i] = 0xFE;
+       } else {
+               pwr2g->cck_diff[path][i] = (hwinfo[eadr]&0x0f);
+               if (pwr2g->cck_diff[path][i] & BIT(3))
+                       pwr2g->cck_diff[path][i] |= 0xF0;
+       }
+}
+
+static void _rtl8188e_read_power_value_fromprom(struct ieee80211_hw *hw,
+                                               struct txpower_info_2g *pwr2g,
+                                               struct txpower_info_5g *pwr5g,
+                                               bool autoload_fail,
+                                               u8 *hwinfo)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 path, eadr = EEPROM_TX_PWR_INX, i;
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "hal_ReadPowerValueFromPROM88E(): PROMContent[0x%x]= 0x%x\n",
+                (eadr+1), hwinfo[eadr+1]);
+       if (0xFF == hwinfo[eadr+1])
+               autoload_fail = true;
+
+       if (autoload_fail) {
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                        "auto load fail : Use Default value!\n");
+               for (path = 0; path < MAX_RF_PATH; path++) {
+                       /* 2.4G default value */
+                       for (i = 0; i < MAX_CHNL_GROUP_24G; i++) {
+                               pwr2g->index_cck_base[path][i] = 0x2D;
+                               pwr2g->index_bw40_base[path][i] = 0x2D;
+                       }
+                       for (i = 0; i < MAX_TX_COUNT; i++) {
+                               if (i == 0) {
+                                       pwr2g->bw20_diff[path][0] = 0x02;
+                                       pwr2g->ofdm_diff[path][0] = 0x04;
+                               } else {
+                                       pwr2g->bw20_diff[path][i] = 0xFE;
+                                       pwr2g->bw40_diff[path][i] = 0xFE;
+                                       pwr2g->cck_diff[path][i] = 0xFE;
+                                       pwr2g->ofdm_diff[path][i] = 0xFE;
+                               }
+                       }
+               }
+               return;
+       }
+
+       for (path = 0; path < MAX_RF_PATH; path++) {
+               /*2.4G default value*/
+               for (i = 0; i < MAX_CHNL_GROUP_24G; i++) {
+                       pwr2g->index_cck_base[path][i] = hwinfo[eadr++];
+                       if (pwr2g->index_cck_base[path][i] == 0xFF)
+                               pwr2g->index_cck_base[path][i] = 0x2D;
+               }
+               for (i = 0; i < MAX_CHNL_GROUP_24G-1; i++) {
+                       pwr2g->index_bw40_base[path][i] = hwinfo[eadr++];
+                       if (pwr2g->index_bw40_base[path][i] == 0xFF)
+                               pwr2g->index_bw40_base[path][i] = 0x2D;
+               }
+               for (i = 0; i < MAX_TX_COUNT; i++) {
+                       if (i == 0) {
+                               set_diff0_2g(pwr2g, hwinfo, path, i, eadr);
+                               eadr++;
+                       } else {
+                               set_diff1_2g(pwr2g, hwinfo, path, i, eadr);
+                               eadr++;
+
+                               set_diff2_2g(pwr2g, hwinfo, path, i, eadr);
+                               eadr++;
+                       }
+               }
+
+               /*5G default value*/
+               for (i = 0; i < MAX_CHNL_GROUP_5G; i++) {
+                       pwr5g->index_bw40_base[path][i] = hwinfo[eadr++];
+                       if (pwr5g->index_bw40_base[path][i] == 0xFF)
+                               pwr5g->index_bw40_base[path][i] = 0xFE;
+               }
+
+               for (i = 0; i < MAX_TX_COUNT; i++) {
+                       if (i == 0) {
+                               set_diff0_5g(pwr5g, hwinfo, path, i, eadr);
+                               eadr++;
+                       } else {
+                               set_diff1_5g(pwr5g, hwinfo, path, i, eadr);
+                               eadr++;
+                       }
+               }
+
+               if (hwinfo[eadr] == 0xFF) {
+                       pwr5g->ofdm_diff[path][1] = 0xFE;
+                       pwr5g->ofdm_diff[path][2] = 0xFE;
+               } else {
+                       pwr5g->ofdm_diff[path][1] = (hwinfo[eadr] & 0xf0) >> 4;
+                       pwr5g->ofdm_diff[path][2] = (hwinfo[eadr] & 0x0f);
+               }
+               eadr++;
+
+               if (hwinfo[eadr] == 0xFF)
+                       pwr5g->ofdm_diff[path][3] = 0xFE;
+               else
+                       pwr5g->ofdm_diff[path][3] = (hwinfo[eadr]&0x0f);
+               eadr++;
+
+               for (i = 1; i < MAX_TX_COUNT; i++) {
+                       if (pwr5g->ofdm_diff[path][i] == 0xFF)
+                               pwr5g->ofdm_diff[path][i] = 0xFE;
+                       else if (pwr5g->ofdm_diff[path][i] & BIT(3))
+                               pwr5g->ofdm_diff[path][i] |= 0xF0;
+               }
+       }
+}
+
+static void _rtl88ee_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
+                                                bool autoload_fail,
+                                                u8 *hwinfo)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct txpower_info_2g pwrinfo24g;
+       struct txpower_info_5g pwrinfo5g;
+       u8 rf_path, index;
+       u8 i;
+       int jj = EEPROM_RF_BOARD_OPTION_88E;
+       int kk = EEPROM_THERMAL_METER_88E;
+
+       _rtl8188e_read_power_value_fromprom(hw, &pwrinfo24g, &pwrinfo5g,
+                                           autoload_fail, hwinfo);
+
+       for (rf_path = 0; rf_path < 2; rf_path++) {
+               for (i = 0; i < 14; i++) {
+                       index = get_chnl_group(i+1);
+
+                       rtlefuse->txpwrlevel_cck[rf_path][i] =
+                                pwrinfo24g.index_cck_base[rf_path][index];
+                       if (i == 13)
+                               rtlefuse->txpwrlevel_ht40_1s[rf_path][i] =
+                                    pwrinfo24g.index_bw40_base[rf_path][4];
+                       else
+                               rtlefuse->txpwrlevel_ht40_1s[rf_path][i] =
+                                    pwrinfo24g.index_bw40_base[rf_path][index];
+                       rtlefuse->txpwr_ht20diff[rf_path][i] =
+                                pwrinfo24g.bw20_diff[rf_path][0];
+                       rtlefuse->txpwr_legacyhtdiff[rf_path][i] =
+                                pwrinfo24g.ofdm_diff[rf_path][0];
+               }
+
+               for (i = 0; i < 14; i++) {
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
+                               "RF(%d)-Ch(%d) [CCK / HT40_1S ] = "
+                               "[0x%x / 0x%x ]\n", rf_path, i,
+                               rtlefuse->txpwrlevel_cck[rf_path][i],
+                               rtlefuse->txpwrlevel_ht40_1s[rf_path][i]);
+               }
+       }
+
+       if (!autoload_fail)
+               rtlefuse->eeprom_thermalmeter = hwinfo[kk];
+       else
+               rtlefuse->eeprom_thermalmeter = EEPROM_DEFAULT_THERMALMETER;
+
+       if (rtlefuse->eeprom_thermalmeter == 0xff || autoload_fail) {
+               rtlefuse->apk_thermalmeterignore = true;
+               rtlefuse->eeprom_thermalmeter = EEPROM_DEFAULT_THERMALMETER;
+       }
+
+       rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter;
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
+               "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter);
+
+       if (!autoload_fail) {
+               rtlefuse->eeprom_regulatory = hwinfo[jj] & 0x07;/*bit0~2*/
+               if (hwinfo[jj] == 0xFF)
+                       rtlefuse->eeprom_regulatory = 0;
+       } else {
+               rtlefuse->eeprom_regulatory = 0;
+       }
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
+               "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory);
+}
+
+static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_pci_priv *rppriv = rtl_pcipriv(hw);
+       u16 i, usvalue;
+       u8 hwinfo[HWSET_MAX_SIZE];
+       u16 eeprom_id;
+       int jj = EEPROM_RF_BOARD_OPTION_88E;
+       int kk = EEPROM_RF_FEATURE_OPTION_88E;
+
+       if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) {
+               rtl_efuse_shadow_map_update(hw);
+
+               memcpy(hwinfo, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0],
+                      HWSET_MAX_SIZE);
+       } else if (rtlefuse->epromtype == EEPROM_93C46) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "RTL819X Not boot from eeprom, check it !!");
+       }
+
+       RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, ("MAP\n"),
+                     hwinfo, HWSET_MAX_SIZE);
+
+       eeprom_id = *((u16 *)&hwinfo[0]);
+       if (eeprom_id != RTL8188E_EEPROM_ID) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                        "EEPROM ID(%#x) is invalid!!\n", eeprom_id);
+               rtlefuse->autoload_failflag = true;
+       } else {
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n");
+               rtlefuse->autoload_failflag = false;
+       }
+
+       if (rtlefuse->autoload_failflag == true)
+               return;
+       /*VID DID SVID SDID*/
+       rtlefuse->eeprom_vid = *(u16 *)&hwinfo[EEPROM_VID];
+       rtlefuse->eeprom_did = *(u16 *)&hwinfo[EEPROM_DID];
+       rtlefuse->eeprom_svid = *(u16 *)&hwinfo[EEPROM_SVID];
+       rtlefuse->eeprom_smid = *(u16 *)&hwinfo[EEPROM_SMID];
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROMId = 0x%4x\n", eeprom_id);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM VID = 0x%4x\n", rtlefuse->eeprom_vid);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM DID = 0x%4x\n", rtlefuse->eeprom_did);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM SVID = 0x%4x\n", rtlefuse->eeprom_svid);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid);
+       /*customer ID*/
+       rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID];
+       if (rtlefuse->eeprom_oemid == 0xFF)
+               rtlefuse->eeprom_oemid = 0;
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid);
+       /*EEPROM version*/
+       rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION];
+       /*mac address*/
+       for (i = 0; i < 6; i += 2) {
+               usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i];
+               *((u16 *)(&rtlefuse->dev_addr[i])) = usvalue;
+       }
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+                "dev_addr: %pM\n", rtlefuse->dev_addr);
+       /*channel plan */
+       rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN];
+       /* set channel paln to world wide 13 */
+       rtlefuse->channel_plan = COUNTRY_CODE_WORLD_WIDE_13;
+       /*tx power*/
+       _rtl88ee_read_txpower_info_from_hwpg(hw, rtlefuse->autoload_failflag,
+                                            hwinfo);
+       rtlefuse->txpwr_fromeprom = true;
+
+       rtl8188ee_read_bt_coexist_info_from_hwpg(hw,
+                                                rtlefuse->autoload_failflag,
+                                                hwinfo);
+       /*board type*/
+       rtlefuse->board_type = (((*(u8 *)&hwinfo[jj]) & 0xE0) >> 5);
+       /*Wake on wlan*/
+       rtlefuse->wowlan_enable = ((hwinfo[kk] & 0x40) >> 6);
+       /*parse xtal*/
+       rtlefuse->crystalcap = hwinfo[EEPROM_XTAL_88E];
+       if (hwinfo[EEPROM_XTAL_88E])
+               rtlefuse->crystalcap = 0x20;
+       /*antenna diversity*/
+       rtlefuse->antenna_div_cfg = (hwinfo[jj] & 0x18) >> 3;
+       if (hwinfo[jj] == 0xFF)
+               rtlefuse->antenna_div_cfg = 0;
+       if (rppriv->bt_coexist.eeprom_bt_coexist != 0 &&
+           rppriv->bt_coexist.eeprom_bt_ant_num == ANT_X1)
+               rtlefuse->antenna_div_cfg = 0;
+
+       rtlefuse->antenna_div_type = hwinfo[EEPROM_RF_ANTENNA_OPT_88E];
+       if (rtlefuse->antenna_div_type == 0xFF)
+               rtlefuse->antenna_div_type = 0x01;
+       if (rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV ||
+           rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV)
+               rtlefuse->antenna_div_cfg = 1;
+
+       if (rtlhal->oem_id == RT_CID_DEFAULT) {
+               switch (rtlefuse->eeprom_oemid) {
+               case EEPROM_CID_DEFAULT:
+                       if (rtlefuse->eeprom_did == 0x8179) {
+                               if (rtlefuse->eeprom_svid == 0x1025) {
+                                       rtlhal->oem_id = RT_CID_819x_Acer;
+                               } else if ((rtlefuse->eeprom_svid == 0x10EC &&
+                                           rtlefuse->eeprom_smid == 0x0179) ||
+                                           (rtlefuse->eeprom_svid == 0x17AA &&
+                                           rtlefuse->eeprom_smid == 0x0179)) {
+                                       rtlhal->oem_id = RT_CID_819x_Lenovo;
+                               } else if (rtlefuse->eeprom_svid == 0x103c &&
+                                        rtlefuse->eeprom_smid == 0x197d) {
+                                       rtlhal->oem_id = RT_CID_819x_HP;
+                               } else {
+                                       rtlhal->oem_id = RT_CID_DEFAULT;
+                               }
+                       } else {
+                               rtlhal->oem_id = RT_CID_DEFAULT;
+                       }
+                       break;
+               case EEPROM_CID_TOSHIBA:
+                       rtlhal->oem_id = RT_CID_TOSHIBA;
+                       break;
+               case EEPROM_CID_QMI:
+                       rtlhal->oem_id = RT_CID_819x_QMI;
+                       break;
+               case EEPROM_CID_WHQL:
+               default:
+                       rtlhal->oem_id = RT_CID_DEFAULT;
+                       break;
+               }
+       }
+}
+
+static void _rtl88ee_hal_customized_behavior(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+
+       pcipriv->ledctl.led_opendrain = true;
+
+       switch (rtlhal->oem_id) {
+       case RT_CID_819x_HP:
+               pcipriv->ledctl.led_opendrain = true;
+               break;
+       case RT_CID_819x_Lenovo:
+       case RT_CID_DEFAULT:
+       case RT_CID_TOSHIBA:
+       case RT_CID_CCX:
+       case RT_CID_819x_Acer:
+       case RT_CID_WHQL:
+       default:
+               break;
+       }
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+                "RT Customized ID: 0x%02X\n", rtlhal->oem_id);
+}
+
+void rtl88ee_read_eeprom_info(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u8 tmp_u1b;
+
+       rtlhal->version = _rtl88ee_read_chip_version(hw);
+       if (get_rf_type(rtlphy) == RF_1T1R) {
+               rtlpriv->dm.rfpath_rxenable[0] = true;
+       } else {
+               rtlpriv->dm.rfpath_rxenable[0] = true;
+               rtlpriv->dm.rfpath_rxenable[1] = true;
+       }
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "VersionID = 0x%4x\n",
+                rtlhal->version);
+       tmp_u1b = rtl_read_byte(rtlpriv, REG_9346CR);
+       if (tmp_u1b & BIT(4)) {
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EEPROM\n");
+               rtlefuse->epromtype = EEPROM_93C46;
+       } else {
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EFUSE\n");
+               rtlefuse->epromtype = EEPROM_BOOT_EFUSE;
+       }
+       if (tmp_u1b & BIT(5)) {
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n");
+               rtlefuse->autoload_failflag = false;
+               _rtl88ee_read_adapter_info(hw);
+       } else {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Autoload ERR!!\n");
+       }
+       _rtl88ee_hal_customized_behavior(hw);
+}
+
+static void rtl88ee_update_hal_rate_table(struct ieee80211_hw *hw,
+                                         struct ieee80211_sta *sta)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *rppriv = rtl_pcipriv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u32 ratr_value;
+       u8 ratr_index = 0;
+       u8 nmode = mac->ht_enable;
+       u8 mimo_ps = IEEE80211_SMPS_OFF;
+       u16 shortgi_rate;
+       u32 tmp_ratr_value;
+       u8 ctx40 = mac->bw_40;
+       u16 cap = sta->ht_cap.cap;
+       u8 short40 = (cap & IEEE80211_HT_CAP_SGI_40) ?  1 : 0;
+       u8 short20 = (cap & IEEE80211_HT_CAP_SGI_20) ?  1 : 0;
+       enum wireless_mode wirelessmode = mac->mode;
+
+       if (rtlhal->current_bandtype == BAND_ON_5G)
+               ratr_value = sta->supp_rates[1] << 4;
+       else
+               ratr_value = sta->supp_rates[0];
+       if (mac->opmode == NL80211_IFTYPE_ADHOC)
+               ratr_value = 0xfff;
+       ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
+                       sta->ht_cap.mcs.rx_mask[0] << 12);
+       switch (wirelessmode) {
+       case WIRELESS_MODE_B:
+               if (ratr_value & 0x0000000c)
+                       ratr_value &= 0x0000000d;
+               else
+                       ratr_value &= 0x0000000f;
+               break;
+       case WIRELESS_MODE_G:
+               ratr_value &= 0x00000FF5;
+               break;
+       case WIRELESS_MODE_N_24G:
+       case WIRELESS_MODE_N_5G:
+               nmode = 1;
+               if (mimo_ps == IEEE80211_SMPS_STATIC) {
+                       ratr_value &= 0x0007F005;
+               } else {
+                       u32 ratr_mask;
+
+                       if (get_rf_type(rtlphy) == RF_1T2R ||
+                           get_rf_type(rtlphy) == RF_1T1R)
+                               ratr_mask = 0x000ff005;
+                       else
+                               ratr_mask = 0x0f0ff005;
+
+                       ratr_value &= ratr_mask;
+               }
+               break;
+       default:
+               if (rtlphy->rf_type == RF_1T2R)
+                       ratr_value &= 0x000ff0ff;
+               else
+                       ratr_value &= 0x0f0ff0ff;
+
+               break;
+       }
+
+       if ((rppriv->bt_coexist.bt_coexistence) &&
+           (rppriv->bt_coexist.bt_coexist_type == BT_CSR_BC4) &&
+           (rppriv->bt_coexist.bt_cur_state) &&
+           (rppriv->bt_coexist.bt_ant_isolation) &&
+           ((rppriv->bt_coexist.bt_service == BT_SCO) ||
+           (rppriv->bt_coexist.bt_service == BT_BUSY)))
+               ratr_value &= 0x0fffcfc0;
+       else
+               ratr_value &= 0x0FFFFFFF;
+
+       if (nmode && ((ctx40 && short40) ||
+                     (!ctx40 && short20))) {
+               ratr_value |= 0x10000000;
+               tmp_ratr_value = (ratr_value >> 12);
+
+               for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) {
+                       if ((1 << shortgi_rate) & tmp_ratr_value)
+                               break;
+               }
+
+               shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) |
+                   (shortgi_rate << 4) | (shortgi_rate);
+       }
+
+       rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value);
+
+       RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
+                "%x\n", rtl_read_dword(rtlpriv, REG_ARFR0));
+}
+
+static void rtl88ee_update_hal_rate_mask(struct ieee80211_hw *hw,
+                                        struct ieee80211_sta *sta, u8 rssi)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_sta_info *sta_entry = NULL;
+       u32 ratr_bitmap;
+       u8 ratr_index;
+       u16 cap = sta->ht_cap.cap;
+       u8 ctx40 = (cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0;
+       u8 short40 = (cap & IEEE80211_HT_CAP_SGI_40) ?  1 : 0;
+       u8 short20 = (cap & IEEE80211_HT_CAP_SGI_20) ?  1 : 0;
+       enum wireless_mode wirelessmode = 0;
+       bool shortgi = false;
+       u8 rate_mask[5];
+       u8 macid = 0;
+       u8 mimo_ps = IEEE80211_SMPS_OFF;
+
+       sta_entry = (struct rtl_sta_info *)sta->drv_priv;
+       wirelessmode = sta_entry->wireless_mode;
+       if (mac->opmode == NL80211_IFTYPE_STATION ||
+           mac->opmode == NL80211_IFTYPE_MESH_POINT)
+               ctx40 = mac->bw_40;
+       else if (mac->opmode == NL80211_IFTYPE_AP ||
+                mac->opmode == NL80211_IFTYPE_ADHOC)
+               macid = sta->aid + 1;
+
+       if (rtlhal->current_bandtype == BAND_ON_5G)
+               ratr_bitmap = sta->supp_rates[1] << 4;
+       else
+               ratr_bitmap = sta->supp_rates[0];
+       if (mac->opmode == NL80211_IFTYPE_ADHOC)
+               ratr_bitmap = 0xfff;
+       ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
+                       sta->ht_cap.mcs.rx_mask[0] << 12);
+       switch (wirelessmode) {
+       case WIRELESS_MODE_B:
+               ratr_index = RATR_INX_WIRELESS_B;
+               if (ratr_bitmap & 0x0000000c)
+                       ratr_bitmap &= 0x0000000d;
+               else
+                       ratr_bitmap &= 0x0000000f;
+               break;
+       case WIRELESS_MODE_G:
+               ratr_index = RATR_INX_WIRELESS_GB;
+
+               if (rssi == 1)
+                       ratr_bitmap &= 0x00000f00;
+               else if (rssi == 2)
+                       ratr_bitmap &= 0x00000ff0;
+               else
+                       ratr_bitmap &= 0x00000ff5;
+               break;
+       case WIRELESS_MODE_A:
+               ratr_index = RATR_INX_WIRELESS_A;
+               ratr_bitmap &= 0x00000ff0;
+               break;
+       case WIRELESS_MODE_N_24G:
+       case WIRELESS_MODE_N_5G:
+               ratr_index = RATR_INX_WIRELESS_NGB;
+
+               if (mimo_ps == IEEE80211_SMPS_STATIC) {
+                       if (rssi == 1)
+                               ratr_bitmap &= 0x00070000;
+                       else if (rssi == 2)
+                               ratr_bitmap &= 0x0007f000;
+                       else
+                               ratr_bitmap &= 0x0007f005;
+               } else {
+                       if (rtlphy->rf_type == RF_1T2R ||
+                           rtlphy->rf_type == RF_1T1R) {
+                               if (ctx40) {
+                                       if (rssi == 1)
+                                               ratr_bitmap &= 0x000f0000;
+                                       else if (rssi == 2)
+                                               ratr_bitmap &= 0x000ff000;
+                                       else
+                                               ratr_bitmap &= 0x000ff015;
+                               } else {
+                                       if (rssi == 1)
+                                               ratr_bitmap &= 0x000f0000;
+                                       else if (rssi == 2)
+                                               ratr_bitmap &= 0x000ff000;
+                                       else
+                                               ratr_bitmap &= 0x000ff005;
+                               }
+                       } else {
+                               if (ctx40) {
+                                       if (rssi == 1)
+                                               ratr_bitmap &= 0x0f8f0000;
+                                       else if (rssi == 2)
+                                               ratr_bitmap &= 0x0f8ff000;
+                                       else
+                                               ratr_bitmap &= 0x0f8ff015;
+                               } else {
+                                       if (rssi == 1)
+                                               ratr_bitmap &= 0x0f8f0000;
+                                       else if (rssi == 2)
+                                               ratr_bitmap &= 0x0f8ff000;
+                                       else
+                                               ratr_bitmap &= 0x0f8ff005;
+                               }
+                       }
+               }
+
+               if ((ctx40 && short40) || (!ctx40 && short20)) {
+                       if (macid == 0)
+                               shortgi = true;
+                       else if (macid == 1)
+                               shortgi = false;
+               }
+               break;
+       default:
+               ratr_index = RATR_INX_WIRELESS_NGB;
+
+               if (rtlphy->rf_type == RF_1T2R)
+                       ratr_bitmap &= 0x000ff0ff;
+               else
+                       ratr_bitmap &= 0x0f0ff0ff;
+               break;
+       }
+       sta_entry->ratr_index = ratr_index;
+
+       RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
+                "ratr_bitmap :%x\n", ratr_bitmap);
+       *(u32 *)&rate_mask = (ratr_bitmap & 0x0fffffff) |
+                            (ratr_index << 28);
+       rate_mask[4] = macid | (shortgi ? 0x20 : 0x00) | 0x80;
+       RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
+                "Rate_index:%x, ratr_val:%x, %x:%x:%x:%x:%x\n",
+                ratr_index, ratr_bitmap, rate_mask[0], rate_mask[1],
+                rate_mask[2], rate_mask[3], rate_mask[4]);
+       rtl88e_fill_h2c_cmd(hw, H2C_88E_RA_MASK, 5, rate_mask);
+       _rtl88ee_set_bcn_ctrl_reg(hw, BIT(3), 0);
+}
+
+void rtl88ee_update_hal_rate_tbl(struct ieee80211_hw *hw,
+               struct ieee80211_sta *sta, u8 rssi)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       if (rtlpriv->dm.useramask)
+               rtl88ee_update_hal_rate_mask(hw, sta, rssi);
+       else
+               rtl88ee_update_hal_rate_table(hw, sta);
+}
+
+void rtl88ee_update_channel_access_setting(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       u16 sifs_timer;
+
+       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME,
+                                     (u8 *)&mac->slot_time);
+       if (!mac->ht_enable)
+               sifs_timer = 0x0a0a;
+       else
+               sifs_timer = 0x0e0e;
+       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer);
+}
+
+bool rtl88ee_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       enum rf_pwrstate state_toset;
+       u32 u4tmp;
+       bool actuallyset = false;
+
+       if (rtlpriv->rtlhal.being_init_adapter)
+               return false;
+
+       if (ppsc->swrf_processing)
+               return false;
+
+       spin_lock(&rtlpriv->locks.rf_ps_lock);
+       if (ppsc->rfchange_inprogress) {
+               spin_unlock(&rtlpriv->locks.rf_ps_lock);
+               return false;
+       } else {
+               ppsc->rfchange_inprogress = true;
+               spin_unlock(&rtlpriv->locks.rf_ps_lock);
+       }
+
+       u4tmp = rtl_read_dword(rtlpriv, REG_GPIO_OUTPUT);
+       state_toset = (u4tmp & BIT(31)) ? ERFON : ERFOFF;
+
+
+       if ((ppsc->hwradiooff == true) && (state_toset == ERFON)) {
+               RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+                        "GPIOChangeRF  - HW Radio ON, RF ON\n");
+
+               state_toset = ERFON;
+               ppsc->hwradiooff = false;
+               actuallyset = true;
+       } else if ((ppsc->hwradiooff == false) && (state_toset == ERFOFF)) {
+               RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+                        "GPIOChangeRF  - HW Radio OFF, RF OFF\n");
+
+               state_toset = ERFOFF;
+               ppsc->hwradiooff = true;
+               actuallyset = true;
+       }
+
+       if (actuallyset) {
+               spin_lock(&rtlpriv->locks.rf_ps_lock);
+               ppsc->rfchange_inprogress = false;
+               spin_unlock(&rtlpriv->locks.rf_ps_lock);
+       } else {
+               if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC)
+                       RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+
+               spin_lock(&rtlpriv->locks.rf_ps_lock);
+               ppsc->rfchange_inprogress = false;
+               spin_unlock(&rtlpriv->locks.rf_ps_lock);
+       }
+
+       *valid = 1;
+       return !ppsc->hwradiooff;
+}
+
+static void add_one_key(struct ieee80211_hw *hw, u8 *macaddr,
+                       struct rtl_mac *mac, u32 key, u32 id,
+                       u8 enc_algo, bool is_pairwise)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+
+       RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "add one entry\n");
+       if (is_pairwise) {
+               RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set Pairwise key\n");
+
+               rtl_cam_add_one_entry(hw, macaddr, key, id, enc_algo,
+                                     CAM_CONFIG_NO_USEDK,
+                                     rtlpriv->sec.key_buf[key]);
+       } else {
+               RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set group key\n");
+
+               if (mac->opmode == NL80211_IFTYPE_ADHOC) {
+                       rtl_cam_add_one_entry(hw, rtlefuse->dev_addr,
+                                             PAIRWISE_KEYIDX,
+                                             CAM_PAIRWISE_KEY_POSITION,
+                                             enc_algo,
+                                             CAM_CONFIG_NO_USEDK,
+                                             rtlpriv->sec.key_buf[id]);
+               }
+
+               rtl_cam_add_one_entry(hw, macaddr, key, id, enc_algo,
+                                     CAM_CONFIG_NO_USEDK,
+                                     rtlpriv->sec.key_buf[id]);
+       }
+}
+
+void rtl88ee_set_key(struct ieee80211_hw *hw, u32 key,
+                    u8 *mac_ad, bool is_group, u8 enc_algo,
+                    bool is_wepkey, bool clear_all)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       u8 *macaddr = mac_ad;
+       u32 id = 0;
+       bool is_pairwise = false;
+
+       static u8 cam_const_addr[4][6] = {
+               {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+               {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+               {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+               {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
+       };
+       static u8 cam_const_broad[] = {
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+       };
+
+       if (clear_all) {
+               u8 idx = 0;
+               u8 cam_offset = 0;
+               u8 clear_number = 5;
+
+               RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "clear_all\n");
+
+               for (idx = 0; idx < clear_number; idx++) {
+                       rtl_cam_mark_invalid(hw, cam_offset + idx);
+                       rtl_cam_empty_entry(hw, cam_offset + idx);
+
+                       if (idx < 5) {
+                               memset(rtlpriv->sec.key_buf[idx], 0,
+                                      MAX_KEY_LEN);
+                               rtlpriv->sec.key_len[idx] = 0;
+                       }
+               }
+
+       } else {
+               switch (enc_algo) {
+               case WEP40_ENCRYPTION:
+                       enc_algo = CAM_WEP40;
+                       break;
+               case WEP104_ENCRYPTION:
+                       enc_algo = CAM_WEP104;
+                       break;
+               case TKIP_ENCRYPTION:
+                       enc_algo = CAM_TKIP;
+                       break;
+               case AESCCMP_ENCRYPTION:
+                       enc_algo = CAM_AES;
+                       break;
+               default:
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "switch case not processed\n");
+                       enc_algo = CAM_TKIP;
+                       break;
+               }
+
+               if (is_wepkey || rtlpriv->sec.use_defaultkey) {
+                       macaddr = cam_const_addr[key];
+                       id = key;
+               } else {
+                       if (is_group) {
+                               macaddr = cam_const_broad;
+                               id = key;
+                       } else {
+                               if (mac->opmode == NL80211_IFTYPE_AP ||
+                                   mac->opmode == NL80211_IFTYPE_MESH_POINT) {
+                                       id = rtl_cam_get_free_entry(hw, mac_ad);
+                                       if (id >=  TOTAL_CAM_ENTRY) {
+                                               RT_TRACE(rtlpriv, COMP_SEC,
+                                                        DBG_EMERG,
+                                                        "Can not find free hw security cam entry\n");
+                                               return;
+                                       }
+                               } else {
+                                       id = CAM_PAIRWISE_KEY_POSITION;
+                               }
+
+                               key = PAIRWISE_KEYIDX;
+                               is_pairwise = true;
+                       }
+               }
+
+               if (rtlpriv->sec.key_len[key] == 0) {
+                       RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+                                "delete one entry, id is %d\n", id);
+                       if (mac->opmode == NL80211_IFTYPE_AP ||
+                           mac->opmode == NL80211_IFTYPE_MESH_POINT)
+                               rtl_cam_del_entry(hw, mac_ad);
+                       rtl_cam_delete_one_entry(hw, mac_ad, id);
+               } else {
+                       add_one_key(hw, macaddr, mac, key, id, enc_algo,
+                                   is_pairwise);
+               }
+       }
+}
+
+static void rtl8188ee_bt_var_init(struct ieee80211_hw *hw)
+{
+       struct rtl_pci_priv *rppriv = rtl_pcipriv(hw);
+       struct bt_coexist_info coexist = rppriv->bt_coexist;
+
+       coexist.bt_coexistence = rppriv->bt_coexist.eeprom_bt_coexist;
+       coexist.bt_ant_num = coexist.eeprom_bt_ant_num;
+       coexist.bt_coexist_type = coexist.eeprom_bt_type;
+
+       if (coexist.reg_bt_iso == 2)
+               coexist.bt_ant_isolation = coexist.eeprom_bt_ant_isol;
+       else
+               coexist.bt_ant_isolation = coexist.reg_bt_iso;
+
+       coexist.bt_radio_shared_type = coexist.eeprom_bt_radio_shared;
+
+       if (coexist.bt_coexistence) {
+               if (coexist.reg_bt_sco == 1)
+                       coexist.bt_service = BT_OTHER_ACTION;
+               else if (coexist.reg_bt_sco == 2)
+                       coexist.bt_service = BT_SCO;
+               else if (coexist.reg_bt_sco == 4)
+                       coexist.bt_service = BT_BUSY;
+               else if (coexist.reg_bt_sco == 5)
+                       coexist.bt_service = BT_OTHERBUSY;
+               else
+                       coexist.bt_service = BT_IDLE;
+
+               coexist.bt_edca_ul = 0;
+               coexist.bt_edca_dl = 0;
+               coexist.bt_rssi_state = 0xff;
+       }
+}
+
+void rtl8188ee_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
+                                             bool auto_load_fail, u8 *hwinfo)
+{
+       rtl8188ee_bt_var_init(hw);
+}
+
+void rtl8188ee_bt_reg_init(struct ieee80211_hw *hw)
+{
+       struct rtl_pci_priv *rppriv = rtl_pcipriv(hw);
+
+       /* 0:Low, 1:High, 2:From Efuse. */
+       rppriv->bt_coexist.reg_bt_iso = 2;
+       /* 0:Idle, 1:None-SCO, 2:SCO, 3:From Counter. */
+       rppriv->bt_coexist.reg_bt_sco = 3;
+       /* 0:Disable BT control A-MPDU, 1:Enable BT control A-MPDU. */
+       rppriv->bt_coexist.reg_bt_sco = 0;
+}
+
+void rtl8188ee_bt_hw_init(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_pci_priv *rppriv = rtl_pcipriv(hw);
+       struct bt_coexist_info coexist = rppriv->bt_coexist;
+       u8 u1_tmp;
+
+       if (coexist.bt_coexistence &&
+           ((coexist.bt_coexist_type == BT_CSR_BC4) ||
+             coexist.bt_coexist_type == BT_CSR_BC8)) {
+               if (coexist.bt_ant_isolation)
+                       rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0);
+
+               u1_tmp = rtl_read_byte(rtlpriv, 0x4fd) &
+                                      BIT_OFFSET_LEN_MASK_32(0, 1);
+               u1_tmp = u1_tmp | ((coexist.bt_ant_isolation == 1) ?
+                        0 : BIT_OFFSET_LEN_MASK_32(1, 1)) |
+                        ((coexist.bt_service == BT_SCO) ?
+                        0 : BIT_OFFSET_LEN_MASK_32(2, 1));
+               rtl_write_byte(rtlpriv, 0x4fd, u1_tmp);
+
+               rtl_write_dword(rtlpriv, REG_BT_COEX_TABLE+4, 0xaaaa9aaa);
+               rtl_write_dword(rtlpriv, REG_BT_COEX_TABLE+8, 0xffbd0040);
+               rtl_write_dword(rtlpriv, REG_BT_COEX_TABLE+0xc, 0x40000010);
+
+               /* Config to 1T1R. */
+               if (rtlphy->rf_type == RF_1T1R) {
+                       u1_tmp = rtl_read_byte(rtlpriv, ROFDM0_TRXPATHENABLE);
+                       u1_tmp &= ~(BIT_OFFSET_LEN_MASK_32(1, 1));
+                       rtl_write_byte(rtlpriv, ROFDM0_TRXPATHENABLE, u1_tmp);
+
+                       u1_tmp = rtl_read_byte(rtlpriv, ROFDM1_TRXPATHENABLE);
+                       u1_tmp &= ~(BIT_OFFSET_LEN_MASK_32(1, 1));
+                       rtl_write_byte(rtlpriv, ROFDM1_TRXPATHENABLE, u1_tmp);
+               }
+       }
+}
+
+void rtl88ee_suspend(struct ieee80211_hw *hw)
+{
+}
+
+void rtl88ee_resume(struct ieee80211_hw *hw)
+{
+}
+
+/* Turn on AAP (RCR:bit 0) for promicuous mode. */
+void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw,
+                               bool allow_all_da, bool write_into_reg)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       if (allow_all_da) /* Set BIT0 */
+               rtlpci->receive_config |= RCR_AAP;
+        else /* Clear BIT0 */
+               rtlpci->receive_config &= ~RCR_AAP;
+
+       if (write_into_reg)
+               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
+
+       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
+                "receive_config = 0x%08X, write_into_reg =%d\n",
+                rtlpci->receive_config, write_into_reg);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h
new file mode 100644 (file)
index 0000000..b4460a4
--- /dev/null
@@ -0,0 +1,68 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92CE_HW_H__
+#define __RTL92CE_HW_H__
+
+void rtl88ee_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val);
+void rtl88ee_read_eeprom_info(struct ieee80211_hw *hw);
+void rtl88ee_interrupt_recognized(struct ieee80211_hw *hw,
+                                 u32 *p_inta, u32 *p_intb);
+int rtl88ee_hw_init(struct ieee80211_hw *hw);
+void rtl88ee_card_disable(struct ieee80211_hw *hw);
+void rtl88ee_enable_interrupt(struct ieee80211_hw *hw);
+void rtl88ee_disable_interrupt(struct ieee80211_hw *hw);
+int rtl88ee_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type);
+void rtl88ee_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid);
+void rtl88ee_set_qos(struct ieee80211_hw *hw, int aci);
+void rtl88ee_set_beacon_related_registers(struct ieee80211_hw *hw);
+void rtl88ee_set_beacon_interval(struct ieee80211_hw *hw);
+void rtl88ee_update_interrupt_mask(struct ieee80211_hw *hw,
+                                  u32 add_msr, u32 rm_msr);
+void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val);
+void rtl88ee_update_hal_rate_tbl(struct ieee80211_hw *hw,
+                                struct ieee80211_sta *sta, u8 rssi_level);
+void rtl88ee_update_channel_access_setting(struct ieee80211_hw *hw);
+bool rtl88ee_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid);
+void rtl88ee_enable_hw_security_config(struct ieee80211_hw *hw);
+void rtl88ee_set_key(struct ieee80211_hw *hw, u32 key_index,
+                    u8 *p_macaddr, bool is_group, u8 enc_algo,
+                    bool is_wepkey, bool clear_all);
+
+void rtl8188ee_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
+                                             bool autoload_fail, u8 *hwinfo);
+void rtl8188ee_bt_reg_init(struct ieee80211_hw *hw);
+void rtl8188ee_bt_hw_init(struct ieee80211_hw *hw);
+void rtl88ee_suspend(struct ieee80211_hw *hw);
+void rtl88ee_resume(struct ieee80211_hw *hw);
+void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw,
+                               bool allow_all_da, bool write_into_reg);
+void rtl88ee_fw_clk_off_timer_callback(unsigned long data);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/led.c b/drivers/net/wireless/rtlwifi/rtl8188ee/led.c
new file mode 100644 (file)
index 0000000..c81a9cb
--- /dev/null
@@ -0,0 +1,157 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "reg.h"
+#include "led.h"
+
+static void rtl88ee_init_led(struct ieee80211_hw *hw,
+                            struct rtl_led *pled, enum rtl_led_pin ledpin)
+{
+       pled->hw = hw;
+       pled->ledpin = ledpin;
+       pled->ledon = false;
+}
+
+void rtl88ee_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
+{
+       u8 ledcfg;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
+                "LedAddr:%X ledpin =%d\n", REG_LEDCFG2, pled->ledpin);
+
+       switch (pled->ledpin) {
+       case LED_PIN_GPIO0:
+               break;
+       case LED_PIN_LED0:
+               ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2);
+               rtl_write_byte(rtlpriv, REG_LEDCFG2,
+                              (ledcfg & 0xf0) | BIT(5) | BIT(6));
+               break;
+       case LED_PIN_LED1:
+               ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1);
+               rtl_write_byte(rtlpriv, REG_LEDCFG1, ledcfg & 0x10);
+               break;
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "switch case not processed\n");
+               break;
+       }
+       pled->ledon = true;
+}
+
+void rtl88ee_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       u8 ledcfg;
+       u8 val;
+
+       RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
+                "LedAddr:%X ledpin =%d\n", REG_LEDCFG2, pled->ledpin);
+
+       switch (pled->ledpin) {
+       case LED_PIN_GPIO0:
+               break;
+       case LED_PIN_LED0:
+               ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2);
+               ledcfg &= 0xf0;
+               val = ledcfg | BIT(3) | BIT(5) | BIT(6);
+               if (pcipriv->ledctl.led_opendrain == true) {
+                       rtl_write_byte(rtlpriv, REG_LEDCFG2, val);
+                       ledcfg = rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG);
+                       val = ledcfg & 0xFE;
+                       rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, val);
+               } else {
+                       rtl_write_byte(rtlpriv, REG_LEDCFG2, val);
+               }
+               break;
+       case LED_PIN_LED1:
+               ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1);
+               ledcfg &= 0x10;
+               rtl_write_byte(rtlpriv, REG_LEDCFG1, (ledcfg | BIT(3)));
+               break;
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "switch case not processed\n");
+               break;
+       }
+       pled->ledon = false;
+}
+
+void rtl88ee_init_sw_leds(struct ieee80211_hw *hw)
+{
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+
+       rtl88ee_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0);
+       rtl88ee_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1);
+}
+
+static void rtl88ee_sw_led_control(struct ieee80211_hw *hw,
+                                   enum led_ctl_mode ledaction)
+{
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0);
+
+       switch (ledaction) {
+       case LED_CTL_POWER_ON:
+       case LED_CTL_LINK:
+       case LED_CTL_NO_LINK:
+               rtl88ee_sw_led_on(hw, pLed0);
+               break;
+       case LED_CTL_POWER_OFF:
+               rtl88ee_sw_led_off(hw, pLed0);
+               break;
+       default:
+               break;
+       }
+}
+
+void rtl88ee_led_control(struct ieee80211_hw *hw,
+                       enum led_ctl_mode ledaction)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+
+       if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) &&
+           (ledaction == LED_CTL_TX ||
+            ledaction == LED_CTL_RX ||
+            ledaction == LED_CTL_SITE_SURVEY ||
+            ledaction == LED_CTL_LINK ||
+            ledaction == LED_CTL_NO_LINK ||
+            ledaction == LED_CTL_START_TO_LINK ||
+            ledaction == LED_CTL_POWER_ON)) {
+               return;
+       }
+       RT_TRACE(rtlpriv, COMP_LED, DBG_TRACE, "ledaction %d,\n",
+                ledaction);
+       rtl88ee_sw_led_control(hw, ledaction);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/led.h b/drivers/net/wireless/rtlwifi/rtl8188ee/led.h
new file mode 100644 (file)
index 0000000..4073f6f
--- /dev/null
@@ -0,0 +1,38 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92CE_LED_H__
+#define __RTL92CE_LED_H__
+
+void rtl88ee_init_sw_leds(struct ieee80211_hw *hw);
+void rtl88ee_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled);
+void rtl88ee_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled);
+void rtl88ee_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c
new file mode 100644 (file)
index 0000000..e655c04
--- /dev/null
@@ -0,0 +1,2202 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../ps.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "rf.h"
+#include "dm.h"
+#include "table.h"
+
+static void set_baseband_phy_config(struct ieee80211_hw *hw);
+static void set_baseband_agc_config(struct ieee80211_hw *hw);
+static void store_pwrindex_offset(struct ieee80211_hw *hw,
+                                 u32 regaddr, u32 bitmask,
+                                 u32 data);
+static bool check_cond(struct ieee80211_hw *hw, const u32  condition);
+
+static u32 rf_serial_read(struct ieee80211_hw *hw,
+                         enum radio_path rfpath, u32 offset)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct bb_reg_def *phreg = &rtlphy->phyreg_def[rfpath];
+       u32 newoffset;
+       u32 tmplong, tmplong2;
+       u8 rfpi_enable = 0;
+       u32 ret;
+       int jj = RF90_PATH_A;
+       int kk = RF90_PATH_B;
+
+       offset &= 0xff;
+       newoffset = offset;
+       if (RT_CANNOT_IO(hw)) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "return all one\n");
+               return 0xFFFFFFFF;
+       }
+       tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD);
+       if (rfpath == jj)
+               tmplong2 = tmplong;
+       else
+               tmplong2 = rtl_get_bbreg(hw, phreg->rfhssi_para2, MASKDWORD);
+       tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) |
+           (newoffset << 23) | BLSSIREADEDGE;
+       rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD,
+                     tmplong & (~BLSSIREADEDGE));
+       mdelay(1);
+       rtl_set_bbreg(hw, phreg->rfhssi_para2, MASKDWORD, tmplong2);
+       mdelay(2);
+       if (rfpath == jj)
+               rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1,
+                                                BIT(8));
+       else if (rfpath == kk)
+               rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1,
+                                                BIT(8));
+       if (rfpi_enable)
+               ret = rtl_get_bbreg(hw, phreg->rf_rbpi, BLSSIREADBACKDATA);
+       else
+               ret = rtl_get_bbreg(hw, phreg->rf_rb, BLSSIREADBACKDATA);
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFR-%d Addr[0x%x]= 0x%x\n",
+                rfpath, phreg->rf_rb, ret);
+       return ret;
+}
+
+static void rf_serial_write(struct ieee80211_hw *hw,
+                           enum radio_path rfpath, u32 offset,
+                           u32 data)
+{
+       u32 data_and_addr;
+       u32 newoffset;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct bb_reg_def *phreg = &rtlphy->phyreg_def[rfpath];
+
+       if (RT_CANNOT_IO(hw)) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "stop\n");
+               return;
+       }
+       offset &= 0xff;
+       newoffset = offset;
+       data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff;
+       rtl_set_bbreg(hw, phreg->rf3wire_offset, MASKDWORD, data_and_addr);
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]= 0x%x\n",
+                rfpath, phreg->rf3wire_offset, data_and_addr);
+}
+
+static u32 cal_bit_shift(u32 bitmask)
+{
+       u32 i;
+
+       for (i = 0; i <= 31; i++) {
+               if (((bitmask >> i) & 0x1) == 1)
+                       break;
+       }
+       return i;
+}
+
+static bool config_bb_with_header(struct ieee80211_hw *hw,
+                                 u8 configtype)
+{
+       if (configtype == BASEBAND_CONFIG_PHY_REG)
+               set_baseband_phy_config(hw);
+       else if (configtype == BASEBAND_CONFIG_AGC_TAB)
+               set_baseband_agc_config(hw);
+       return true;
+}
+
+static bool config_bb_with_pgheader(struct ieee80211_hw *hw,
+                                   u8 configtype)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       int i;
+       u32 *table_pg;
+       u16 tbl_page_len;
+       u32 v1 = 0, v2 = 0;
+
+       tbl_page_len = RTL8188EEPHY_REG_ARRAY_PGLEN;
+       table_pg = RTL8188EEPHY_REG_ARRAY_PG;
+
+       if (configtype == BASEBAND_CONFIG_PHY_REG) {
+               for (i = 0; i < tbl_page_len; i = i + 3) {
+                       v1 = table_pg[i];
+                       v2 = table_pg[i + 1];
+
+                       if (v1 < 0xcdcdcdcd) {
+                               if (table_pg[i] == 0xfe)
+                                       mdelay(50);
+                               else if (table_pg[i] == 0xfd)
+                                       mdelay(5);
+                               else if (table_pg[i] == 0xfc)
+                                       mdelay(1);
+                               else if (table_pg[i] == 0xfb)
+                                       udelay(50);
+                               else if (table_pg[i] == 0xfa)
+                                       udelay(5);
+                               else if (table_pg[i] == 0xf9)
+                                       udelay(1);
+
+                               store_pwrindex_offset(hw, table_pg[i],
+                                                     table_pg[i + 1],
+                                                     table_pg[i + 2]);
+                               continue;
+                       } else {
+                               if (!check_cond(hw, table_pg[i])) {
+                                       /*don't need the hw_body*/
+                                       i += 2; /* skip the pair of expression*/
+                                       v1 = table_pg[i];
+                                       v2 = table_pg[i + 1];
+                                       while (v2 != 0xDEAD) {
+                                               i += 3;
+                                               v1 = table_pg[i];
+                                               v2 = table_pg[i + 1];
+                                       }
+                               }
+                       }
+               }
+       } else {
+               RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+                        "configtype != BaseBand_Config_PHY_REG\n");
+       }
+       return true;
+}
+
+static bool config_parafile(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_efuse *fuse = rtl_efuse(rtl_priv(hw));
+       bool rtstatus;
+
+       rtstatus = config_bb_with_header(hw, BASEBAND_CONFIG_PHY_REG);
+       if (rtstatus != true) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Write BB Reg Fail!!");
+               return false;
+       }
+
+       if (fuse->autoload_failflag == false) {
+               rtlphy->pwrgroup_cnt = 0;
+               rtstatus = config_bb_with_pgheader(hw, BASEBAND_CONFIG_PHY_REG);
+       }
+       if (rtstatus != true) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "BB_PG Reg Fail!!");
+               return false;
+       }
+       rtstatus = config_bb_with_header(hw, BASEBAND_CONFIG_AGC_TAB);
+       if (rtstatus != true) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "AGC Table Fail\n");
+               return false;
+       }
+       rtlphy->cck_high_power = (bool) (rtl_get_bbreg(hw,
+                                RFPGA0_XA_HSSIPARAMETER2, 0x200));
+
+       return true;
+}
+
+static void rtl88e_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       int jj = RF90_PATH_A;
+       int kk = RF90_PATH_B;
+
+       rtlphy->phyreg_def[jj].rfintfs = RFPGA0_XAB_RFINTERFACESW;
+       rtlphy->phyreg_def[kk].rfintfs = RFPGA0_XAB_RFINTERFACESW;
+       rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW;
+       rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW;
+
+       rtlphy->phyreg_def[jj].rfintfi = RFPGA0_XAB_RFINTERFACERB;
+       rtlphy->phyreg_def[kk].rfintfi = RFPGA0_XAB_RFINTERFACERB;
+       rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB;
+       rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB;
+
+       rtlphy->phyreg_def[jj].rfintfo = RFPGA0_XA_RFINTERFACEOE;
+       rtlphy->phyreg_def[kk].rfintfo = RFPGA0_XB_RFINTERFACEOE;
+
+       rtlphy->phyreg_def[jj].rfintfe = RFPGA0_XA_RFINTERFACEOE;
+       rtlphy->phyreg_def[kk].rfintfe = RFPGA0_XB_RFINTERFACEOE;
+
+       rtlphy->phyreg_def[jj].rf3wire_offset = RFPGA0_XA_LSSIPARAMETER;
+       rtlphy->phyreg_def[kk].rf3wire_offset = RFPGA0_XB_LSSIPARAMETER;
+
+       rtlphy->phyreg_def[jj].rflssi_select = rFPGA0_XAB_RFPARAMETER;
+       rtlphy->phyreg_def[kk].rflssi_select = rFPGA0_XAB_RFPARAMETER;
+       rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = rFPGA0_XCD_RFPARAMETER;
+       rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = rFPGA0_XCD_RFPARAMETER;
+
+       rtlphy->phyreg_def[jj].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+       rtlphy->phyreg_def[kk].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+       rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+       rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+
+       rtlphy->phyreg_def[jj].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1;
+       rtlphy->phyreg_def[kk].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1;
+
+       rtlphy->phyreg_def[jj].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2;
+       rtlphy->phyreg_def[kk].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2;
+
+       rtlphy->phyreg_def[jj].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL;
+       rtlphy->phyreg_def[kk].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL;
+       rtlphy->phyreg_def[RF90_PATH_C].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL;
+       rtlphy->phyreg_def[RF90_PATH_D].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL;
+
+       rtlphy->phyreg_def[jj].rfagc_control1 = ROFDM0_XAAGCCORE1;
+       rtlphy->phyreg_def[kk].rfagc_control1 = ROFDM0_XBAGCCORE1;
+       rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1;
+       rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1;
+
+       rtlphy->phyreg_def[jj].rfagc_control2 = ROFDM0_XAAGCCORE2;
+       rtlphy->phyreg_def[kk].rfagc_control2 = ROFDM0_XBAGCCORE2;
+       rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2;
+       rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2;
+
+       rtlphy->phyreg_def[jj].rfrxiq_imbal = ROFDM0_XARXIQIMBAL;
+       rtlphy->phyreg_def[kk].rfrxiq_imbal = ROFDM0_XBRXIQIMBAL;
+       rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbal = ROFDM0_XCRXIQIMBAL;
+       rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbal = ROFDM0_XDRXIQIMBAL;
+
+       rtlphy->phyreg_def[jj].rfrx_afe = ROFDM0_XARXAFE;
+       rtlphy->phyreg_def[kk].rfrx_afe = ROFDM0_XBRXAFE;
+       rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE;
+       rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE;
+
+       rtlphy->phyreg_def[jj].rftxiq_imbal = ROFDM0_XATXIQIMBAL;
+       rtlphy->phyreg_def[kk].rftxiq_imbal = ROFDM0_XBTXIQIMBAL;
+       rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbal = ROFDM0_XCTXIQIMBAL;
+       rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbal = ROFDM0_XDTXIQIMBAL;
+
+       rtlphy->phyreg_def[jj].rftx_afe = ROFDM0_XATXAFE;
+       rtlphy->phyreg_def[kk].rftx_afe = ROFDM0_XBTXAFE;
+
+       rtlphy->phyreg_def[jj].rf_rb = RFPGA0_XA_LSSIREADBACK;
+       rtlphy->phyreg_def[kk].rf_rb = RFPGA0_XB_LSSIREADBACK;
+
+       rtlphy->phyreg_def[jj].rf_rbpi = TRANSCEIVEA_HSPI_READBACK;
+       rtlphy->phyreg_def[kk].rf_rbpi = TRANSCEIVEB_HSPI_READBACK;
+}
+
+static bool rtl88e_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable,
+                                           u32 cmdtableidx, u32 cmdtablesz,
+                                           enum swchnlcmd_id cmdid,
+                                           u32 para1, u32 para2, u32 msdelay)
+{
+       struct swchnlcmd *pcmd;
+
+       if (cmdtable == NULL) {
+               RT_ASSERT(false, "cmdtable cannot be NULL.\n");
+               return false;
+       }
+
+       if (cmdtableidx >= cmdtablesz)
+               return false;
+
+       pcmd = cmdtable + cmdtableidx;
+       pcmd->cmdid = cmdid;
+       pcmd->para1 = para1;
+       pcmd->para2 = para2;
+       pcmd->msdelay = msdelay;
+       return true;
+}
+
+static bool chnl_step_by_step(struct ieee80211_hw *hw,
+                             u8 channel, u8 *stage, u8 *step,
+                             u32 *delay)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct swchnlcmd precommoncmd[MAX_PRECMD_CNT];
+       u32 precommoncmdcnt;
+       struct swchnlcmd postcommoncmd[MAX_POSTCMD_CNT];
+       u32 postcommoncmdcnt;
+       struct swchnlcmd rfdependcmd[MAX_RFDEPENDCMD_CNT];
+       u32 rfdependcmdcnt;
+       struct swchnlcmd *currentcmd = NULL;
+       u8 rfpath;
+       u8 num_total_rfpath = rtlphy->num_total_rfpath;
+
+       precommoncmdcnt = 0;
+       rtl88e_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
+                                       MAX_PRECMD_CNT,
+                                       CMDID_SET_TXPOWEROWER_LEVEL, 0, 0, 0);
+       rtl88e_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
+                                       MAX_PRECMD_CNT, CMDID_END, 0, 0, 0);
+
+       postcommoncmdcnt = 0;
+
+       rtl88e_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++,
+                                       MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0);
+
+       rfdependcmdcnt = 0;
+
+       RT_ASSERT((channel >= 1 && channel <= 14),
+                 "illegal channel for Zebra: %d\n", channel);
+
+       rtl88e_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
+                                       MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG,
+                                       RF_CHNLBW, channel, 10);
+
+       rtl88e_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
+                                       MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0,
+                                        0);
+
+       do {
+               switch (*stage) {
+               case 0:
+                       currentcmd = &precommoncmd[*step];
+                       break;
+               case 1:
+                       currentcmd = &rfdependcmd[*step];
+                       break;
+               case 2:
+                       currentcmd = &postcommoncmd[*step];
+                       break;
+               }
+
+               if (currentcmd->cmdid == CMDID_END) {
+                       if ((*stage) == 2) {
+                               return true;
+                       } else {
+                               (*stage)++;
+                               (*step) = 0;
+                               continue;
+                       }
+               }
+
+               switch (currentcmd->cmdid) {
+               case CMDID_SET_TXPOWEROWER_LEVEL:
+                       rtl88e_phy_set_txpower_level(hw, channel);
+                       break;
+               case CMDID_WRITEPORT_ULONG:
+                       rtl_write_dword(rtlpriv, currentcmd->para1,
+                                       currentcmd->para2);
+                       break;
+               case CMDID_WRITEPORT_USHORT:
+                       rtl_write_word(rtlpriv, currentcmd->para1,
+                                      (u16) currentcmd->para2);
+                       break;
+               case CMDID_WRITEPORT_UCHAR:
+                       rtl_write_byte(rtlpriv, currentcmd->para1,
+                                      (u8) currentcmd->para2);
+                       break;
+               case CMDID_RF_WRITEREG:
+                       for (rfpath = 0; rfpath < num_total_rfpath; rfpath++) {
+                               rtlphy->rfreg_chnlval[rfpath] =
+                                   ((rtlphy->rfreg_chnlval[rfpath] &
+                                     0xfffffc00) | currentcmd->para2);
+
+                               rtl_set_rfreg(hw, (enum radio_path)rfpath,
+                                             currentcmd->para1,
+                                             RFREG_OFFSET_MASK,
+                                             rtlphy->rfreg_chnlval[rfpath]);
+                       }
+                       break;
+               default:
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "switch case not processed\n");
+                       break;
+               }
+
+               break;
+       } while (true);
+
+       (*delay) = currentcmd->msdelay;
+       (*step)++;
+       return false;
+}
+
+static long rtl88e_pwr_idx_dbm(struct ieee80211_hw *hw,
+                              enum wireless_mode wirelessmode,
+                              u8 txpwridx)
+{
+       long offset;
+       long pwrout_dbm;
+
+       switch (wirelessmode) {
+       case WIRELESS_MODE_B:
+               offset = -7;
+               break;
+       case WIRELESS_MODE_G:
+       case WIRELESS_MODE_N_24G:
+               offset = -8;
+               break;
+       default:
+               offset = -8;
+               break;
+       }
+       pwrout_dbm = txpwridx / 2 + offset;
+       return pwrout_dbm;
+}
+
+static void rtl88e_phy_set_io(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
+
+       RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+                "--->Cmd(%#x), set_io_inprogress(%d)\n",
+                rtlphy->current_io_type, rtlphy->set_io_inprogress);
+       switch (rtlphy->current_io_type) {
+       case IO_CMD_RESUME_DM_BY_SCAN:
+               dm_digtable->cur_igvalue = rtlphy->initgain_backup.xaagccore1;
+               /*rtl92c_dm_write_dig(hw);*/
+               rtl88e_phy_set_txpower_level(hw, rtlphy->current_channel);
+               rtl_set_bbreg(hw, RCCK0_CCA, 0xff0000, 0x83);
+               break;
+       case IO_CMD_PAUSE_DM_BY_SCAN:
+               rtlphy->initgain_backup.xaagccore1 = dm_digtable->cur_igvalue;
+               dm_digtable->cur_igvalue = 0x17;
+               rtl_set_bbreg(hw, RCCK0_CCA, 0xff0000, 0x40);
+               break;
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "switch case not processed\n");
+               break;
+       }
+       rtlphy->set_io_inprogress = false;
+       RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+                "(%#x)\n", rtlphy->current_io_type);
+}
+
+u32 rtl88e_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 returnvalue, originalvalue, bitshift;
+
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+                "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask);
+       originalvalue = rtl_read_dword(rtlpriv, regaddr);
+       bitshift = cal_bit_shift(bitmask);
+       returnvalue = (originalvalue & bitmask) >> bitshift;
+
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+                "BBR MASK = 0x%x Addr[0x%x]= 0x%x\n", bitmask,
+                regaddr, originalvalue);
+
+       return returnvalue;
+}
+
+void rtl88e_phy_set_bb_reg(struct ieee80211_hw *hw,
+                          u32 regaddr, u32 bitmask, u32 data)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 originalvalue, bitshift;
+
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+                "regaddr(%#x), bitmask(%#x),data(%#x)\n",
+                regaddr, bitmask, data);
+
+       if (bitmask != MASKDWORD) {
+               originalvalue = rtl_read_dword(rtlpriv, regaddr);
+               bitshift = cal_bit_shift(bitmask);
+               data = ((originalvalue & (~bitmask)) | (data << bitshift));
+       }
+
+       rtl_write_dword(rtlpriv, regaddr, data);
+
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+                "regaddr(%#x), bitmask(%#x), data(%#x)\n",
+                regaddr, bitmask, data);
+}
+
+u32 rtl88e_phy_query_rf_reg(struct ieee80211_hw *hw,
+                           enum radio_path rfpath, u32 regaddr, u32 bitmask)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 original_value, readback_value, bitshift;
+       unsigned long flags;
+
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+                "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n",
+                regaddr, rfpath, bitmask);
+
+       spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+
+
+       original_value = rf_serial_read(hw, rfpath, regaddr);
+       bitshift = cal_bit_shift(bitmask);
+       readback_value = (original_value & bitmask) >> bitshift;
+
+       spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+                "regaddr(%#x), rfpath(%#x), bitmask(%#x), original_value(%#x)\n",
+                 regaddr, rfpath, bitmask, original_value);
+
+       return readback_value;
+}
+
+void rtl88e_phy_set_rf_reg(struct ieee80211_hw *hw,
+                          enum radio_path rfpath,
+                          u32 regaddr, u32 bitmask, u32 data)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 original_value, bitshift;
+       unsigned long flags;
+
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+                "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n",
+                 regaddr, bitmask, data, rfpath);
+
+       spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+
+       if (bitmask != RFREG_OFFSET_MASK) {
+                       original_value = rf_serial_read(hw, rfpath, regaddr);
+                       bitshift = cal_bit_shift(bitmask);
+                       data = ((original_value & (~bitmask)) |
+                               (data << bitshift));
+               }
+
+       rf_serial_write(hw, rfpath, regaddr, data);
+
+
+       spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+
+       RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+                "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n",
+                regaddr, bitmask, data, rfpath);
+}
+
+static bool config_mac_with_header(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 i;
+       u32 arraylength;
+       u32 *ptrarray;
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read Rtl8188EMACPHY_Array\n");
+       arraylength = RTL8188EEMAC_1T_ARRAYLEN;
+       ptrarray = RTL8188EEMAC_1T_ARRAY;
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "Img:RTL8188EEMAC_1T_ARRAY LEN %d\n", arraylength);
+       for (i = 0; i < arraylength; i = i + 2)
+               rtl_write_byte(rtlpriv, ptrarray[i], (u8) ptrarray[i + 1]);
+       return true;
+}
+
+bool rtl88e_phy_mac_config(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       bool rtstatus = config_mac_with_header(hw);
+
+       rtl_write_byte(rtlpriv, 0x04CA, 0x0B);
+       return rtstatus;
+}
+
+bool rtl88e_phy_bb_config(struct ieee80211_hw *hw)
+{
+       bool rtstatus = true;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u16 regval;
+       u8 reg_hwparafile = 1;
+       u32 tmp;
+       rtl88e_phy_init_bb_rf_register_definition(hw);
+       regval = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN);
+       rtl_write_word(rtlpriv, REG_SYS_FUNC_EN,
+                      regval | BIT(13) | BIT(0) | BIT(1));
+
+       rtl_write_byte(rtlpriv, REG_RF_CTRL, RF_EN | RF_RSTB | RF_SDMRSTB);
+       rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN,
+                      FEN_PPLL | FEN_PCIEA | FEN_DIO_PCIE |
+                      FEN_BB_GLB_RSTN | FEN_BBRSTB);
+       tmp = rtl_read_dword(rtlpriv, 0x4c);
+       rtl_write_dword(rtlpriv, 0x4c, tmp | BIT(23));
+       if (reg_hwparafile == 1)
+               rtstatus = config_parafile(hw);
+       return rtstatus;
+}
+
+bool rtl88e_phy_rf_config(struct ieee80211_hw *hw)
+{
+       return rtl88e_phy_rf6052_config(hw);
+}
+
+static bool check_cond(struct ieee80211_hw *hw,
+                                   const u32  condition)
+{
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_efuse *fuse = rtl_efuse(rtl_priv(hw));
+       u32 _board = fuse->board_type; /*need efuse define*/
+       u32 _interface = rtlhal->interface;
+       u32 _platform = 0x08;/*SupportPlatform */
+       u32 cond = condition;
+
+       if (condition == 0xCDCDCDCD)
+               return true;
+
+       cond = condition & 0xFF;
+       if ((_board & cond) == 0 && cond != 0x1F)
+               return false;
+
+       cond = condition & 0xFF00;
+       cond = cond >> 8;
+       if ((_interface & cond) == 0 && cond != 0x07)
+               return false;
+
+       cond = condition & 0xFF0000;
+       cond = cond >> 16;
+       if ((_platform & cond) == 0 && cond != 0x0F)
+               return false;
+       return true;
+}
+
+static void _rtl8188e_config_rf_reg(struct ieee80211_hw *hw,
+                                   u32 addr, u32 data, enum radio_path rfpath,
+                                   u32 regaddr)
+{
+       if (addr == 0xffe) {
+               mdelay(50);
+       } else if (addr == 0xfd) {
+               mdelay(5);
+       } else if (addr == 0xfc) {
+               mdelay(1);
+       } else if (addr == 0xfb) {
+               udelay(50);
+       } else if (addr == 0xfa) {
+               udelay(5);
+       } else if (addr == 0xf9) {
+               udelay(1);
+       } else {
+               rtl_set_rfreg(hw, rfpath, regaddr,
+                             RFREG_OFFSET_MASK,
+                             data);
+               udelay(1);
+       }
+}
+
+static void rtl88_config_s(struct ieee80211_hw *hw,
+       u32 addr, u32 data)
+{
+       u32 content = 0x1000; /*RF Content: radio_a_txt*/
+       u32 maskforphyset = (u32)(content & 0xE000);
+
+       _rtl8188e_config_rf_reg(hw, addr, data, RF90_PATH_A,
+                               addr | maskforphyset);
+}
+
+static void _rtl8188e_config_bb_reg(struct ieee80211_hw *hw,
+                                   u32 addr, u32 data)
+{
+       if (addr == 0xfe) {
+               mdelay(50);
+       } else if (addr == 0xfd) {
+               mdelay(5);
+       } else if (addr == 0xfc) {
+               mdelay(1);
+       } else if (addr == 0xfb) {
+               udelay(50);
+       } else if (addr == 0xfa) {
+               udelay(5);
+       } else if (addr == 0xf9) {
+               udelay(1);
+       } else {
+               rtl_set_bbreg(hw, addr, MASKDWORD, data);
+               udelay(1);
+       }
+}
+
+
+#define NEXT_PAIR(v1, v2, i)                           \
+       do {                                            \
+               i += 2; v1 = array_table[i];            \
+               v2 = array_table[i + 1];                \
+       } while (0)
+
+static void set_baseband_agc_config(struct ieee80211_hw *hw)
+{
+       int i;
+       u32 *array_table;
+       u16 arraylen;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 v1 = 0, v2 = 0;
+
+       arraylen = RTL8188EEAGCTAB_1TARRAYLEN;
+       array_table = RTL8188EEAGCTAB_1TARRAY;
+
+       for (i = 0; i < arraylen; i += 2) {
+               v1 = array_table[i];
+               v2 = array_table[i + 1];
+               if (v1 < 0xCDCDCDCD) {
+                       rtl_set_bbreg(hw, array_table[i], MASKDWORD,
+                                     array_table[i + 1]);
+                       udelay(1);
+                       continue;
+               } else {/*This line is the start line of branch.*/
+                       if (!check_cond(hw, array_table[i])) {
+                               /*Discard the following (offset, data) pairs*/
+                               NEXT_PAIR(v1, v2, i);
+                               while (v2 != 0xDEAD && v2 != 0xCDEF &&
+                                      v2 != 0xCDCD && i < arraylen - 2) {
+                                       NEXT_PAIR(v1, v2, i);
+                               }
+                               i -= 2; /* compensate for loop's += 2*/
+                       } else {
+                               /* Configure matched pairs and skip to end */
+                               NEXT_PAIR(v1, v2, i);
+                               while (v2 != 0xDEAD && v2 != 0xCDEF &&
+                                      v2 != 0xCDCD && i < arraylen - 2) {
+                                       rtl_set_bbreg(hw, array_table[i],
+                                                     MASKDWORD,
+                                                     array_table[i + 1]);
+                                       udelay(1);
+                                       NEXT_PAIR(v1, v2, i);
+                               }
+
+                               while (v2 != 0xDEAD && i < arraylen - 2)
+                                       NEXT_PAIR(v1, v2, i);
+                       }
+               }
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "The agctab_array_table[0] is %x Rtl818EEPHY_REGArray[1] is %x\n",
+                        array_table[i],
+                        array_table[i + 1]);
+       }
+}
+
+static void set_baseband_phy_config(struct ieee80211_hw *hw)
+{
+       int i;
+       u32 *array_table;
+       u16 arraylen;
+       u32 v1 = 0, v2 = 0;
+
+       arraylen = RTL8188EEPHY_REG_1TARRAYLEN;
+       array_table = RTL8188EEPHY_REG_1TARRAY;
+
+       for (i = 0; i < arraylen; i += 2) {
+               v1 = array_table[i];
+               v2 = array_table[i + 1];
+               if (v1 < 0xcdcdcdcd) {
+                       _rtl8188e_config_bb_reg(hw, v1, v2);
+               } else {/*This line is the start line of branch.*/
+                       if (!check_cond(hw, array_table[i])) {
+                               /*Discard the following (offset, data) pairs*/
+                               NEXT_PAIR(v1, v2, i);
+                               while (v2 != 0xDEAD &&
+                                      v2 != 0xCDEF &&
+                                      v2 != 0xCDCD && i < arraylen - 2)
+                                       NEXT_PAIR(v1, v2, i);
+                               i -= 2; /* prevent from for-loop += 2*/
+                       } else {
+                               /* Configure matched pairs and skip to end */
+                               NEXT_PAIR(v1, v2, i);
+                               while (v2 != 0xDEAD &&
+                                      v2 != 0xCDEF &&
+                                      v2 != 0xCDCD && i < arraylen - 2) {
+                                       _rtl8188e_config_bb_reg(hw, v1, v2);
+                                       NEXT_PAIR(v1, v2, i);
+                               }
+
+                               while (v2 != 0xDEAD && i < arraylen - 2)
+                                       NEXT_PAIR(v1, v2, i);
+                       }
+               }
+       }
+}
+
+static void store_pwrindex_offset(struct ieee80211_hw *hw,
+                                 u32 regaddr, u32 bitmask,
+                                 u32 data)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+       if (regaddr == RTXAGC_A_RATE18_06) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][0] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][0] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][0]);
+       }
+       if (regaddr == RTXAGC_A_RATE54_24) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][1] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][1] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][1]);
+       }
+       if (regaddr == RTXAGC_A_CCK1_MCS32) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][6] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][6] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][6]);
+       }
+       if (regaddr == RTXAGC_B_CCK11_A_CCK2_11 && bitmask == 0xffffff00) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][7] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][7] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][7]);
+       }
+       if (regaddr == RTXAGC_A_MCS03_MCS00) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][2] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][2] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][2]);
+       }
+       if (regaddr == RTXAGC_A_MCS07_MCS04) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][3] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][3] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][3]);
+       }
+       if (regaddr == RTXAGC_A_MCS11_MCS08) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][4] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][4] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][4]);
+       }
+       if (regaddr == RTXAGC_A_MCS15_MCS12) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][5] = data;
+               if (get_rf_type(rtlphy) == RF_1T1R)
+                       rtlphy->pwrgroup_cnt++;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][5] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][5]);
+       }
+       if (regaddr == RTXAGC_B_RATE18_06) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][8] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][8] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][8]);
+       }
+       if (regaddr == RTXAGC_B_RATE54_24) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][9] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][9] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][9]);
+       }
+       if (regaddr == RTXAGC_B_CCK1_55_MCS32) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][14] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][14] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][14]);
+       }
+       if (regaddr == RTXAGC_B_CCK11_A_CCK2_11 && bitmask == 0x000000ff) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][15] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][15] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][15]);
+       }
+       if (regaddr == RTXAGC_B_MCS03_MCS00) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][10] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][10] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][10]);
+       }
+       if (regaddr == RTXAGC_B_MCS07_MCS04) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][11] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][11] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][11]);
+       }
+       if (regaddr == RTXAGC_B_MCS11_MCS08) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][12] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][12] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][12]);
+       }
+       if (regaddr == RTXAGC_B_MCS15_MCS12) {
+               rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][13] = data;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "MCSTxPowerLevelOriginalOffset[%d][13] = 0x%x\n",
+                        rtlphy->pwrgroup_cnt,
+                        rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][13]);
+               if (get_rf_type(rtlphy) != RF_1T1R)
+                       rtlphy->pwrgroup_cnt++;
+       }
+}
+
+#define READ_NEXT_RF_PAIR(v1, v2, i)           \
+       do {                                    \
+               i += 2; v1 = a_table[i];        \
+               v2 = a_table[i + 1];            \
+       } while (0)
+
+bool rtl88e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
+                                         enum radio_path rfpath)
+{
+       int i;
+       u32 *a_table;
+       u16 a_len;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u32 v1 = 0, v2 = 0;
+
+       a_len = RTL8188EE_RADIOA_1TARRAYLEN;
+       a_table = RTL8188EE_RADIOA_1TARRAY;
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "Radio_A:RTL8188EE_RADIOA_1TARRAY %d\n", a_len);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Radio No %x\n", rfpath);
+       switch (rfpath) {
+       case RF90_PATH_A:
+               for (i = 0; i < a_len; i = i + 2) {
+                       v1 = a_table[i];
+                       v2 = a_table[i + 1];
+                       if (v1 < 0xcdcdcdcd) {
+                               rtl88_config_s(hw, v1, v2);
+                       } else {/*This line is the start line of branch.*/
+                               if (!check_cond(hw, a_table[i])) {
+                                       /* Discard the following (offset, data)
+                                        * pairs
+                                        */
+                                       READ_NEXT_RF_PAIR(v1, v2, i);
+                                       while (v2 != 0xDEAD && v2 != 0xCDEF &&
+                                              v2 != 0xCDCD && i < a_len - 2)
+                                               READ_NEXT_RF_PAIR(v1, v2, i);
+                                       i -= 2; /* prevent from for-loop += 2*/
+                               } else {
+                                       /* Configure matched pairs and skip to
+                                        * end of if-else.
+                                        */
+                                       READ_NEXT_RF_PAIR(v1, v2, i);
+                                       while (v2 != 0xDEAD && v2 != 0xCDEF &&
+                                              v2 != 0xCDCD && i < a_len - 2) {
+                                               rtl88_config_s(hw, v1, v2);
+                                               READ_NEXT_RF_PAIR(v1, v2, i);
+                                       }
+
+                                       while (v2 != 0xDEAD && i < a_len - 2)
+                                               READ_NEXT_RF_PAIR(v1, v2, i);
+                               }
+                       }
+               }
+
+               if (rtlhal->oem_id == RT_CID_819x_HP)
+                       rtl88_config_s(hw, 0x52, 0x7E4BD);
+
+               break;
+
+       case RF90_PATH_B:
+       case RF90_PATH_C:
+       case RF90_PATH_D:
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "switch case not processed\n");
+               break;
+       }
+       return true;
+}
+
+void rtl88e_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+       rtlphy->default_initialgain[0] = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1,
+                                                      MASKBYTE0);
+       rtlphy->default_initialgain[1] = rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1,
+                                                      MASKBYTE0);
+       rtlphy->default_initialgain[2] = rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1,
+                                                      MASKBYTE0);
+       rtlphy->default_initialgain[3] = rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1,
+                                                      MASKBYTE0);
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                "Default initial gain (c50 = 0x%x, c58 = 0x%x, c60 = 0x%x, c68 = 0x%x\n",
+                 rtlphy->default_initialgain[0],
+                 rtlphy->default_initialgain[1],
+                 rtlphy->default_initialgain[2],
+                 rtlphy->default_initialgain[3]);
+
+       rtlphy->framesync = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3,
+                                         MASKBYTE0);
+       rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2,
+                                             MASKDWORD);
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                "Default framesync (0x%x) = 0x%x\n",
+                ROFDM0_RXDETECTOR3, rtlphy->framesync);
+}
+
+void rtl88e_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       u8 level;
+       long dbm;
+
+       level = rtlphy->cur_cck_txpwridx;
+       dbm = rtl88e_pwr_idx_dbm(hw, WIRELESS_MODE_B, level);
+       level = rtlphy->cur_ofdm24g_txpwridx;
+       if (rtl88e_pwr_idx_dbm(hw, WIRELESS_MODE_G, level) > dbm)
+               dbm = rtl88e_pwr_idx_dbm(hw, WIRELESS_MODE_G, level);
+       level = rtlphy->cur_ofdm24g_txpwridx;
+       if (rtl88e_pwr_idx_dbm(hw, WIRELESS_MODE_N_24G, level) > dbm)
+               dbm = rtl88e_pwr_idx_dbm(hw, WIRELESS_MODE_N_24G, level);
+       *powerlevel = dbm;
+}
+
+static void _rtl88e_get_txpower_index(struct ieee80211_hw *hw, u8 channel,
+                                     u8 *cckpower, u8 *ofdm, u8 *bw20_pwr,
+                                     u8 *bw40_pwr)
+{
+       struct rtl_efuse *fuse = rtl_efuse(rtl_priv(hw));
+       u8 i = (channel - 1);
+       u8 rf_path = 0;
+       int jj = RF90_PATH_A;
+       int kk = RF90_PATH_B;
+
+       for (rf_path = 0; rf_path < 2; rf_path++) {
+               if (rf_path == jj) {
+                       cckpower[jj] = fuse->txpwrlevel_cck[jj][i];
+                       if (fuse->txpwr_ht20diff[jj][i] > 0x0f) /*-8~7 */
+                               bw20_pwr[jj] = fuse->txpwrlevel_ht40_1s[jj][i] -
+                                       (~(fuse->txpwr_ht20diff[jj][i]) + 1);
+                       else
+                               bw20_pwr[jj] = fuse->txpwrlevel_ht40_1s[jj][i] +
+                                        fuse->txpwr_ht20diff[jj][i];
+                       if (fuse->txpwr_legacyhtdiff[jj][i] > 0xf)
+                               ofdm[jj] = fuse->txpwrlevel_ht40_1s[jj][i] -
+                                       (~(fuse->txpwr_legacyhtdiff[jj][i])+1);
+                       else
+                               ofdm[jj] = fuse->txpwrlevel_ht40_1s[jj][i] +
+                                          fuse->txpwr_legacyhtdiff[jj][i];
+                       bw40_pwr[jj] = fuse->txpwrlevel_ht40_1s[jj][i];
+
+               } else if (rf_path == kk) {
+                       cckpower[kk] = fuse->txpwrlevel_cck[kk][i];
+                       bw20_pwr[kk] = fuse->txpwrlevel_ht40_1s[kk][i] +
+                                      fuse->txpwr_ht20diff[kk][i];
+                       ofdm[kk] = fuse->txpwrlevel_ht40_1s[kk][i] +
+                                       fuse->txpwr_legacyhtdiff[kk][i];
+                       bw40_pwr[kk] = fuse->txpwrlevel_ht40_1s[kk][i];
+               }
+       }
+}
+
+static void _rtl88e_ccxpower_index_check(struct ieee80211_hw *hw,
+                                        u8 channel, u8 *cckpower,
+                                        u8 *ofdm, u8 *bw20_pwr,
+                                        u8 *bw40_pwr)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+       rtlphy->cur_cck_txpwridx = cckpower[0];
+       rtlphy->cur_ofdm24g_txpwridx = ofdm[0];
+       rtlphy->cur_bw20_txpwridx = bw20_pwr[0];
+       rtlphy->cur_bw40_txpwridx = bw40_pwr[0];
+}
+
+void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel)
+{
+       struct rtl_efuse *fuse = rtl_efuse(rtl_priv(hw));
+       u8 cckpower[MAX_TX_COUNT]  = {0}, ofdm[MAX_TX_COUNT] = {0};
+       u8 bw20_pwr[MAX_TX_COUNT] = {0}, bw40_pwr[MAX_TX_COUNT] = {0};
+
+       if (fuse->txpwr_fromeprom == false)
+               return;
+       _rtl88e_get_txpower_index(hw, channel, &cckpower[0], &ofdm[0],
+                                 &bw20_pwr[0], &bw40_pwr[0]);
+       _rtl88e_ccxpower_index_check(hw, channel, &cckpower[0], &ofdm[0],
+                                    &bw20_pwr[0], &bw40_pwr[0]);
+       rtl88e_phy_rf6052_set_cck_txpower(hw, &cckpower[0]);
+       rtl88e_phy_rf6052_set_ofdm_txpower(hw, &ofdm[0], &bw20_pwr[0],
+                                          &bw40_pwr[0], channel);
+}
+
+void rtl88e_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       enum io_type iotype;
+
+       if (!is_hal_stop(rtlhal)) {
+               switch (operation) {
+               case SCAN_OPT_BACKUP:
+                       iotype = IO_CMD_PAUSE_DM_BY_SCAN;
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                                     HW_VAR_IO_CMD,
+                                                     (u8 *)&iotype);
+                       break;
+               case SCAN_OPT_RESTORE:
+                       iotype = IO_CMD_RESUME_DM_BY_SCAN;
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                                     HW_VAR_IO_CMD,
+                                                     (u8 *)&iotype);
+                       break;
+               default:
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "Unknown Scan Backup operation.\n");
+                       break;
+               }
+       }
+}
+
+void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       u8 reg_bw_opmode;
+       u8 reg_prsr_rsc;
+
+       RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE,
+                "Switch to %s bandwidth\n",
+                rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ?
+                "20MHz" : "40MHz");
+
+       if (is_hal_stop(rtlhal)) {
+               rtlphy->set_bwmode_inprogress = false;
+               return;
+       }
+
+       reg_bw_opmode = rtl_read_byte(rtlpriv, REG_BWOPMODE);
+       reg_prsr_rsc = rtl_read_byte(rtlpriv, REG_RRSR + 2);
+
+       switch (rtlphy->current_chan_bw) {
+       case HT_CHANNEL_WIDTH_20:
+               reg_bw_opmode |= BW_OPMODE_20MHZ;
+               rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode);
+               break;
+       case HT_CHANNEL_WIDTH_20_40:
+               reg_bw_opmode &= ~BW_OPMODE_20MHZ;
+               rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode);
+               reg_prsr_rsc =
+                   (reg_prsr_rsc & 0x90) | (mac->cur_40_prime_sc << 5);
+               rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_prsr_rsc);
+               break;
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "unknown bandwidth: %#X\n", rtlphy->current_chan_bw);
+               break;
+       }
+
+       switch (rtlphy->current_chan_bw) {
+       case HT_CHANNEL_WIDTH_20:
+               rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0);
+               rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0);
+       /*      rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1);*/
+               break;
+       case HT_CHANNEL_WIDTH_20_40:
+               rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1);
+               rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1);
+
+               rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCK_SIDEBAND,
+                             (mac->cur_40_prime_sc >> 1));
+               rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc);
+               /*rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 0);*/
+
+               rtl_set_bbreg(hw, 0x818, (BIT(26) | BIT(27)),
+                             (mac->cur_40_prime_sc ==
+                              HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1);
+               break;
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "unknown bandwidth: %#X\n", rtlphy->current_chan_bw);
+               break;
+       }
+       rtl88e_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw);
+       rtlphy->set_bwmode_inprogress = false;
+       RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, "\n");
+}
+
+void rtl88e_phy_set_bw_mode(struct ieee80211_hw *hw,
+                           enum nl80211_channel_type ch_type)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u8 tmp_bw = rtlphy->current_chan_bw;
+
+       if (rtlphy->set_bwmode_inprogress)
+               return;
+       rtlphy->set_bwmode_inprogress = true;
+       if ((!is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {
+               rtl88e_phy_set_bw_mode_callback(hw);
+       } else {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                        "FALSE driver sleep or unload\n");
+               rtlphy->set_bwmode_inprogress = false;
+               rtlphy->current_chan_bw = tmp_bw;
+       }
+}
+
+void rtl88e_phy_sw_chnl_callback(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       u32 delay;
+
+       RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE,
+                "switch to channel%d\n", rtlphy->current_channel);
+       if (is_hal_stop(rtlhal))
+               return;
+       do {
+               if (!rtlphy->sw_chnl_inprogress)
+                       break;
+               if (!chnl_step_by_step(hw, rtlphy->current_channel,
+                                      &rtlphy->sw_chnl_stage,
+                                      &rtlphy->sw_chnl_step, &delay)) {
+                       if (delay > 0)
+                               mdelay(delay);
+                       else
+                               continue;
+               } else {
+                       rtlphy->sw_chnl_inprogress = false;
+               }
+               break;
+       } while (true);
+       RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "\n");
+}
+
+u8 rtl88e_phy_sw_chnl(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+
+       if (rtlphy->sw_chnl_inprogress)
+               return 0;
+       if (rtlphy->set_bwmode_inprogress)
+               return 0;
+       RT_ASSERT((rtlphy->current_channel <= 14),
+                 "WIRELESS_MODE_G but channel>14");
+       rtlphy->sw_chnl_inprogress = true;
+       rtlphy->sw_chnl_stage = 0;
+       rtlphy->sw_chnl_step = 0;
+       if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {
+               rtl88e_phy_sw_chnl_callback(hw);
+               RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
+                        "sw_chnl_inprogress false schdule workitem current channel %d\n",
+                        rtlphy->current_channel);
+               rtlphy->sw_chnl_inprogress = false;
+       } else {
+               RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
+                        "sw_chnl_inprogress false driver sleep or unload\n");
+               rtlphy->sw_chnl_inprogress = false;
+       }
+       return 1;
+}
+
+static u8 _rtl88e_phy_path_a_iqk(struct ieee80211_hw *hw, bool config_pathb)
+{
+       u32 reg_eac, reg_e94, reg_e9c;
+       u8 result = 0x00;
+
+       rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c1c);
+       rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x30008c1c);
+       rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x8214032a);
+       rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x28160000);
+
+       rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911);
+       rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000);
+       rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000);
+
+       mdelay(IQK_DELAY_TIME);
+
+       reg_eac = rtl_get_bbreg(hw, 0xeac, MASKDWORD);
+       reg_e94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD);
+       reg_e9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD);
+
+       if (!(reg_eac & BIT(28)) &&
+           (((reg_e94 & 0x03FF0000) >> 16) != 0x142) &&
+           (((reg_e9c & 0x03FF0000) >> 16) != 0x42))
+               result |= 0x01;
+       return result;
+}
+
+static u8 _rtl88e_phy_path_b_iqk(struct ieee80211_hw *hw)
+{
+       u32 reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc;
+       u8 result = 0x00;
+
+       rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000002);
+       rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000000);
+       mdelay(IQK_DELAY_TIME);
+       reg_eac = rtl_get_bbreg(hw, 0xeac, MASKDWORD);
+       reg_eb4 = rtl_get_bbreg(hw, 0xeb4, MASKDWORD);
+       reg_ebc = rtl_get_bbreg(hw, 0xebc, MASKDWORD);
+       reg_ec4 = rtl_get_bbreg(hw, 0xec4, MASKDWORD);
+       reg_ecc = rtl_get_bbreg(hw, 0xecc, MASKDWORD);
+
+       if (!(reg_eac & BIT(31)) &&
+           (((reg_eb4 & 0x03FF0000) >> 16) != 0x142) &&
+           (((reg_ebc & 0x03FF0000) >> 16) != 0x42))
+               result |= 0x01;
+       else
+               return result;
+       if (!(reg_eac & BIT(30)) &&
+           (((reg_ec4 & 0x03FF0000) >> 16) != 0x132) &&
+           (((reg_ecc & 0x03FF0000) >> 16) != 0x36))
+               result |= 0x02;
+       return result;
+}
+
+static u8 _rtl88e_phy_path_a_rx_iqk(struct ieee80211_hw *hw, bool config_pathb)
+{
+       u32 reg_eac, reg_e94, reg_e9c, reg_ea4, u32temp;
+       u8 result = 0x00;
+       int jj = RF90_PATH_A;
+
+       /*Get TXIMR Setting*/
+       /*Modify RX IQK mode table*/
+       rtl_set_bbreg(hw, RFPGA0_IQK, MASKDWORD, 0x00000000);
+       rtl_set_rfreg(hw, jj, RF_WE_LUT, RFREG_OFFSET_MASK, 0x800a0);
+       rtl_set_rfreg(hw, jj, RF_RCK_OS, RFREG_OFFSET_MASK, 0x30000);
+       rtl_set_rfreg(hw, jj, RF_TXPA_G1, RFREG_OFFSET_MASK, 0x0000f);
+       rtl_set_rfreg(hw, jj, RF_TXPA_G2, RFREG_OFFSET_MASK, 0xf117b);
+       rtl_set_bbreg(hw, RFPGA0_IQK, MASKDWORD, 0x80800000);
+
+       /*IQK Setting*/
+       rtl_set_bbreg(hw, RTX_IQK, MASKDWORD, 0x01007c00);
+       rtl_set_bbreg(hw, RRX_IQK, MASKDWORD, 0x81004800);
+
+       /*path a IQK setting*/
+       rtl_set_bbreg(hw, RTX_IQK_TONE_A, MASKDWORD, 0x10008c1c);
+       rtl_set_bbreg(hw, RRX_IQK_TONE_A, MASKDWORD, 0x30008c1c);
+       rtl_set_bbreg(hw, RTX_IQK_PI_A, MASKDWORD, 0x82160804);
+       rtl_set_bbreg(hw, RRX_IQK_PI_A, MASKDWORD, 0x28160000);
+
+       /*LO calibration Setting*/
+       rtl_set_bbreg(hw, RIQK_AGC_RSP, MASKDWORD, 0x0046a911);
+       /*one shot, path A LOK & iqk*/
+       rtl_set_bbreg(hw, RIQK_AGC_PTS, MASKDWORD, 0xf9000000);
+       rtl_set_bbreg(hw, RIQK_AGC_PTS, MASKDWORD, 0xf8000000);
+
+       mdelay(IQK_DELAY_TIME);
+
+       reg_eac = rtl_get_bbreg(hw, RRX_POWER_AFTER_IQK_A_2, MASKDWORD);
+       reg_e94 = rtl_get_bbreg(hw, RTX_POWER_BEFORE_IQK_A, MASKDWORD);
+       reg_e9c = rtl_get_bbreg(hw, RTX_POWER_AFTER_IQK_A, MASKDWORD);
+
+
+       if (!(reg_eac & BIT(28)) &&
+           (((reg_e94 & 0x03FF0000) >> 16) != 0x142) &&
+           (((reg_e9c & 0x03FF0000) >> 16) != 0x42))
+               result |= 0x01;
+       else
+               return result;
+
+       u32temp = 0x80007C00 | (reg_e94&0x3FF0000)  |
+                 ((reg_e9c&0x3FF0000) >> 16);
+       rtl_set_bbreg(hw, RTX_IQK, MASKDWORD, u32temp);
+       /*RX IQK*/
+       /*Modify RX IQK mode table*/
+       rtl_set_bbreg(hw, RFPGA0_IQK, MASKDWORD, 0x00000000);
+       rtl_set_rfreg(hw, jj, RF_WE_LUT, RFREG_OFFSET_MASK, 0x800a0);
+       rtl_set_rfreg(hw, jj, RF_RCK_OS, RFREG_OFFSET_MASK, 0x30000);
+       rtl_set_rfreg(hw, jj, RF_TXPA_G1, RFREG_OFFSET_MASK, 0x0000f);
+       rtl_set_rfreg(hw, jj, RF_TXPA_G2, RFREG_OFFSET_MASK, 0xf7ffa);
+       rtl_set_bbreg(hw, RFPGA0_IQK, MASKDWORD, 0x80800000);
+
+       /*IQK Setting*/
+       rtl_set_bbreg(hw, RRX_IQK, MASKDWORD, 0x01004800);
+
+       /*path a IQK setting*/
+       rtl_set_bbreg(hw, RTX_IQK_TONE_A, MASKDWORD, 0x30008c1c);
+       rtl_set_bbreg(hw, RRX_IQK_TONE_A, MASKDWORD, 0x10008c1c);
+       rtl_set_bbreg(hw, RTX_IQK_PI_A, MASKDWORD, 0x82160c05);
+       rtl_set_bbreg(hw, RRX_IQK_PI_A, MASKDWORD, 0x28160c05);
+
+       /*LO calibration Setting*/
+       rtl_set_bbreg(hw, RIQK_AGC_RSP, MASKDWORD, 0x0046a911);
+       /*one shot, path A LOK & iqk*/
+       rtl_set_bbreg(hw, RIQK_AGC_PTS, MASKDWORD, 0xf9000000);
+       rtl_set_bbreg(hw, RIQK_AGC_PTS, MASKDWORD, 0xf8000000);
+
+       mdelay(IQK_DELAY_TIME);
+
+       reg_eac = rtl_get_bbreg(hw, RRX_POWER_AFTER_IQK_A_2, MASKDWORD);
+       reg_e94 = rtl_get_bbreg(hw, RTX_POWER_BEFORE_IQK_A, MASKDWORD);
+       reg_e9c = rtl_get_bbreg(hw, RTX_POWER_AFTER_IQK_A, MASKDWORD);
+       reg_ea4 = rtl_get_bbreg(hw, RRX_POWER_BEFORE_IQK_A_2, MASKDWORD);
+
+       if (!(reg_eac & BIT(27)) &&
+           (((reg_ea4 & 0x03FF0000) >> 16) != 0x132) &&
+           (((reg_eac & 0x03FF0000) >> 16) != 0x36))
+               result |= 0x02;
+       return result;
+}
+
+static void fill_iqk(struct ieee80211_hw *hw, bool iqk_ok, long result[][8],
+                    u8 final, bool btxonly)
+{
+       u32 oldval_0, x, tx0_a, reg;
+       long y, tx0_c;
+
+       if (final == 0xFF) {
+               return;
+       } else if (iqk_ok) {
+               oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATXIQIMBAL,
+                                         MASKDWORD) >> 22) & 0x3FF;
+               x = result[final][0];
+               if ((x & 0x00000200) != 0)
+                       x = x | 0xFFFFFC00;
+               tx0_a = (x * oldval_0) >> 8;
+               rtl_set_bbreg(hw, ROFDM0_XATXIQIMBAL, 0x3FF, tx0_a);
+               rtl_set_bbreg(hw, ROFDM0_ECCATHRES, BIT(31),
+                             ((x * oldval_0 >> 7) & 0x1));
+               y = result[final][1];
+               if ((y & 0x00000200) != 0)
+                       y |= 0xFFFFFC00;
+               tx0_c = (y * oldval_0) >> 8;
+               rtl_set_bbreg(hw, ROFDM0_XCTXAFE, 0xF0000000,
+                             ((tx0_c & 0x3C0) >> 6));
+               rtl_set_bbreg(hw, ROFDM0_XATXIQIMBAL, 0x003F0000,
+                             (tx0_c & 0x3F));
+               rtl_set_bbreg(hw, ROFDM0_ECCATHRES, BIT(29),
+                             ((y * oldval_0 >> 7) & 0x1));
+               if (btxonly)
+                       return;
+               reg = result[final][2];
+               rtl_set_bbreg(hw, ROFDM0_XARXIQIMBAL, 0x3FF, reg);
+               reg = result[final][3] & 0x3F;
+               rtl_set_bbreg(hw, ROFDM0_XARXIQIMBAL, 0xFC00, reg);
+               reg = (result[final][3] >> 6) & 0xF;
+               rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg);
+       }
+}
+
+static void save_adda_reg(struct ieee80211_hw *hw,
+                         const u32 *addareg, u32 *backup,
+                         u32 registernum)
+{
+       u32 i;
+
+       for (i = 0; i < registernum; i++)
+               backup[i] = rtl_get_bbreg(hw, addareg[i], MASKDWORD);
+}
+
+static void save_mac_reg(struct ieee80211_hw *hw, const u32 *macreg,
+                        u32 *macbackup)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 i;
+
+       for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++)
+               macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]);
+       macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]);
+}
+
+static void reload_adda(struct ieee80211_hw *hw, const u32 *addareg,
+                       u32 *backup, u32 reg_num)
+{
+       u32 i;
+
+       for (i = 0; i < reg_num; i++)
+               rtl_set_bbreg(hw, addareg[i], MASKDWORD, backup[i]);
+}
+
+static void reload_mac(struct ieee80211_hw *hw, const u32 *macreg,
+                      u32 *macbackup)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 i;
+
+       for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++)
+               rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]);
+       rtl_write_dword(rtlpriv, macreg[i], macbackup[i]);
+}
+
+static void _rtl88e_phy_path_adda_on(struct ieee80211_hw *hw,
+                                    const u32 *addareg, bool is_patha_on,
+                                    bool is2t)
+{
+       u32 pathon;
+       u32 i;
+
+       pathon = is_patha_on ? 0x04db25a4 : 0x0b1b25a4;
+       if (false == is2t) {
+               pathon = 0x0bdb25a0;
+               rtl_set_bbreg(hw, addareg[0], MASKDWORD, 0x0b1b25a0);
+       } else {
+               rtl_set_bbreg(hw, addareg[0], MASKDWORD, pathon);
+       }
+
+       for (i = 1; i < IQK_ADDA_REG_NUM; i++)
+               rtl_set_bbreg(hw, addareg[i], MASKDWORD, pathon);
+}
+
+static void _rtl88e_phy_mac_setting_calibration(struct ieee80211_hw *hw,
+                                               const u32 *macreg,
+                                               u32 *macbackup)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u32 i = 0;
+
+       rtl_write_byte(rtlpriv, macreg[i], 0x3F);
+
+       for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++)
+               rtl_write_byte(rtlpriv, macreg[i],
+                              (u8) (macbackup[i] & (~BIT(3))));
+       rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5))));
+}
+
+static void _rtl88e_phy_path_a_standby(struct ieee80211_hw *hw)
+{
+       rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0);
+       rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000);
+       rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000);
+}
+
+static void _rtl88e_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode)
+{
+       u32 mode;
+
+       mode = pi_mode ? 0x01000100 : 0x01000000;
+       rtl_set_bbreg(hw, 0x820, MASKDWORD, mode);
+       rtl_set_bbreg(hw, 0x828, MASKDWORD, mode);
+}
+
+static bool sim_comp(struct ieee80211_hw *hw, long result[][8], u8 c1, u8 c2)
+{
+       u32 i, j, diff, bitmap, bound;
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+
+       u8 final[2] = {0xFF, 0xFF};
+       bool bresult = true, is2t = IS_92C_SERIAL(rtlhal->version);
+
+       if (is2t)
+               bound = 8;
+       else
+               bound = 4;
+
+       bitmap = 0;
+
+       for (i = 0; i < bound; i++) {
+               diff = (result[c1][i] > result[c2][i]) ?
+                      (result[c1][i] - result[c2][i]) :
+                      (result[c2][i] - result[c1][i]);
+
+               if (diff > MAX_TOLERANCE) {
+                       if ((i == 2 || i == 6) && !bitmap) {
+                               if (result[c1][i] + result[c1][i + 1] == 0)
+                                       final[(i / 4)] = c2;
+                               else if (result[c2][i] + result[c2][i + 1] == 0)
+                                       final[(i / 4)] = c1;
+                               else
+                                       bitmap = bitmap | (1 << i);
+                       } else {
+                               bitmap = bitmap | (1 << i);
+                       }
+               }
+       }
+
+       if (bitmap == 0) {
+               for (i = 0; i < (bound / 4); i++) {
+                       if (final[i] != 0xFF) {
+                               for (j = i * 4; j < (i + 1) * 4 - 2; j++)
+                                       result[3][j] = result[final[i]][j];
+                               bresult = false;
+                       }
+               }
+               return bresult;
+       } else if (!(bitmap & 0x0F)) {
+               for (i = 0; i < 4; i++)
+                       result[3][i] = result[c1][i];
+               return false;
+       } else if (!(bitmap & 0xF0) && is2t) {
+               for (i = 4; i < 8; i++)
+                       result[3][i] = result[c1][i];
+               return false;
+       } else {
+               return false;
+       }
+}
+
+static void _rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw,
+                                    long result[][8], u8 t, bool is2t)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       u32 i;
+       u8 patha_ok, pathb_ok;
+       const u32 adda_reg[IQK_ADDA_REG_NUM] = {
+               0x85c, 0xe6c, 0xe70, 0xe74,
+               0xe78, 0xe7c, 0xe80, 0xe84,
+               0xe88, 0xe8c, 0xed0, 0xed4,
+               0xed8, 0xedc, 0xee0, 0xeec
+       };
+       const u32 iqk_mac_reg[IQK_MAC_REG_NUM] = {
+               0x522, 0x550, 0x551, 0x040
+       };
+       const u32 iqk_bb_reg[IQK_BB_REG_NUM] = {
+               ROFDM0_TRXPATHENABLE, ROFDM0_TRMUXPAR, RFPGA0_XCD_RFINTERFACESW,
+               0xb68, 0xb6c, 0x870, 0x860, 0x864, 0x800
+       };
+       const u32 retrycount = 2;
+
+       if (t == 0) {
+               save_adda_reg(hw, adda_reg, rtlphy->adda_backup, 16);
+               save_mac_reg(hw, iqk_mac_reg, rtlphy->iqk_mac_backup);
+               save_adda_reg(hw, iqk_bb_reg, rtlphy->iqk_bb_backup,
+                             IQK_BB_REG_NUM);
+       }
+       _rtl88e_phy_path_adda_on(hw, adda_reg, true, is2t);
+       if (t == 0) {
+               rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw,
+                                          RFPGA0_XA_HSSIPARAMETER1, BIT(8));
+       }
+
+       if (!rtlphy->rfpi_enable)
+               _rtl88e_phy_pi_mode_switch(hw, true);
+       /*BB Setting*/
+       rtl_set_bbreg(hw, 0x800, BIT(24), 0x00);
+       rtl_set_bbreg(hw, 0xc04, MASKDWORD, 0x03a05600);
+       rtl_set_bbreg(hw, 0xc08, MASKDWORD, 0x000800e4);
+       rtl_set_bbreg(hw, 0x874, MASKDWORD, 0x22204000);
+
+       rtl_set_bbreg(hw, 0x870, BIT(10), 0x01);
+       rtl_set_bbreg(hw, 0x870, BIT(26), 0x01);
+       rtl_set_bbreg(hw, 0x860, BIT(10), 0x00);
+       rtl_set_bbreg(hw, 0x864, BIT(10), 0x00);
+
+       if (is2t) {
+               rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000);
+               rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00010000);
+       }
+       _rtl88e_phy_mac_setting_calibration(hw, iqk_mac_reg,
+                                           rtlphy->iqk_mac_backup);
+       rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x0f600000);
+       if (is2t)
+               rtl_set_bbreg(hw, 0xb6c, MASKDWORD, 0x0f600000);
+
+       rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000);
+       rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x01007c00);
+       rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x81004800);
+       for (i = 0; i < retrycount; i++) {
+               patha_ok = _rtl88e_phy_path_a_iqk(hw, is2t);
+               if (patha_ok == 0x01) {
+                       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                                "Path A Tx IQK Success!!\n");
+                       result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) &
+                                       0x3FF0000) >> 16;
+                       result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) &
+                                       0x3FF0000) >> 16;
+                       break;
+               }
+       }
+
+       for (i = 0; i < retrycount; i++) {
+               patha_ok = _rtl88e_phy_path_a_rx_iqk(hw, is2t);
+               if (patha_ok == 0x03) {
+                       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                                "Path A Rx IQK Success!!\n");
+                       result[t][2] = (rtl_get_bbreg(hw, 0xea4, MASKDWORD) &
+                                       0x3FF0000) >> 16;
+                       result[t][3] = (rtl_get_bbreg(hw, 0xeac, MASKDWORD) &
+                                       0x3FF0000) >> 16;
+                       break;
+               } else {
+                       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                                "Path a RX iqk fail!!!\n");
+               }
+       }
+
+       if (0 == patha_ok) {
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                        "Path A IQK Success!!\n");
+       }
+       if (is2t) {
+               _rtl88e_phy_path_a_standby(hw);
+               _rtl88e_phy_path_adda_on(hw, adda_reg, false, is2t);
+               for (i = 0; i < retrycount; i++) {
+                       pathb_ok = _rtl88e_phy_path_b_iqk(hw);
+                       if (pathb_ok == 0x03) {
+                               result[t][4] = (rtl_get_bbreg(hw,
+                                               0xeb4, MASKDWORD) &
+                                               0x3FF0000) >> 16;
+                               result[t][5] =
+                                   (rtl_get_bbreg(hw, 0xebc, MASKDWORD) &
+                                                  0x3FF0000) >> 16;
+                               result[t][6] =
+                                   (rtl_get_bbreg(hw, 0xec4, MASKDWORD) &
+                                                  0x3FF0000) >> 16;
+                               result[t][7] =
+                                   (rtl_get_bbreg(hw, 0xecc, MASKDWORD) &
+                                                  0x3FF0000) >> 16;
+                               break;
+                       } else if (i == (retrycount - 1) && pathb_ok == 0x01) {
+                               result[t][4] = (rtl_get_bbreg(hw,
+                                               0xeb4, MASKDWORD) &
+                                               0x3FF0000) >> 16;
+                       }
+                       result[t][5] = (rtl_get_bbreg(hw, 0xebc, MASKDWORD) &
+                                       0x3FF0000) >> 16;
+               }
+       }
+
+       rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0);
+
+       if (t != 0) {
+               if (!rtlphy->rfpi_enable)
+                       _rtl88e_phy_pi_mode_switch(hw, false);
+               reload_adda(hw, adda_reg, rtlphy->adda_backup, 16);
+               reload_mac(hw, iqk_mac_reg, rtlphy->iqk_mac_backup);
+               reload_adda(hw, iqk_bb_reg, rtlphy->iqk_bb_backup,
+                           IQK_BB_REG_NUM);
+
+               rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00032ed3);
+               if (is2t)
+                       rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00032ed3);
+               rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x01008c00);
+               rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x01008c00);
+       }
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "88ee IQK Finish!!\n");
+}
+
+static void _rtl88e_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t)
+{
+       u8 tmpreg;
+       u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       int jj = RF90_PATH_A;
+       int kk = RF90_PATH_B;
+
+       tmpreg = rtl_read_byte(rtlpriv, 0xd03);
+
+       if ((tmpreg & 0x70) != 0)
+               rtl_write_byte(rtlpriv, 0xd03, tmpreg & 0x8F);
+       else
+               rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF);
+
+       if ((tmpreg & 0x70) != 0) {
+               rf_a_mode = rtl_get_rfreg(hw, jj, 0x00, MASK12BITS);
+
+               if (is2t)
+                       rf_b_mode = rtl_get_rfreg(hw, kk, 0x00,
+                                                 MASK12BITS);
+
+               rtl_set_rfreg(hw, jj, 0x00, MASK12BITS,
+                             (rf_a_mode & 0x8FFFF) | 0x10000);
+
+               if (is2t)
+                       rtl_set_rfreg(hw, kk, 0x00, MASK12BITS,
+                                     (rf_b_mode & 0x8FFFF) | 0x10000);
+       }
+       lc_cal = rtl_get_rfreg(hw, jj, 0x18, MASK12BITS);
+
+       rtl_set_rfreg(hw, jj, 0x18, MASK12BITS, lc_cal | 0x08000);
+
+       mdelay(100);
+
+       if ((tmpreg & 0x70) != 0) {
+               rtl_write_byte(rtlpriv, 0xd03, tmpreg);
+               rtl_set_rfreg(hw, jj, 0x00, MASK12BITS, rf_a_mode);
+
+               if (is2t)
+                       rtl_set_rfreg(hw, kk, 0x00, MASK12BITS,
+                                     rf_b_mode);
+       } else {
+               rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00);
+       }
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "\n");
+}
+
+static void rfpath_switch(struct ieee80211_hw *hw,
+                         bool bmain, bool is2t)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_efuse *fuse = rtl_efuse(rtl_priv(hw));
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "\n");
+
+       if (is_hal_stop(rtlhal)) {
+               u8 u1btmp;
+               u1btmp = rtl_read_byte(rtlpriv, REG_LEDCFG0);
+               rtl_write_byte(rtlpriv, REG_LEDCFG0, u1btmp | BIT(7));
+               rtl_set_bbreg(hw, rFPGA0_XAB_RFPARAMETER, BIT(13), 0x01);
+       }
+       if (is2t) {
+               if (bmain)
+                       rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE,
+                                     BIT(5) | BIT(6), 0x1);
+               else
+                       rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE,
+                                     BIT(5) | BIT(6), 0x2);
+       } else {
+               rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BIT(8) | BIT(9), 0);
+               rtl_set_bbreg(hw, 0x914, MASKLWORD, 0x0201);
+
+               /* We use the RF definition of MAIN and AUX, left antenna and
+                * right antenna repectively.
+                * Default output at AUX.
+                */
+               if (bmain) {
+                       rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BIT(14) |
+                                     BIT(13) | BIT(12), 0);
+                       rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BIT(5) |
+                                     BIT(4) | BIT(3), 0);
+                       if (fuse->antenna_div_type == CGCS_RX_HW_ANTDIV)
+                               rtl_set_bbreg(hw, RCONFIG_RAM64X16, BIT(31), 0);
+               } else {
+                       rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BIT(14) |
+                                     BIT(13) | BIT(12), 1);
+                       rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BIT(5) |
+                                     BIT(4) | BIT(3), 1);
+                       if (fuse->antenna_div_type == CGCS_RX_HW_ANTDIV)
+                               rtl_set_bbreg(hw, RCONFIG_RAM64X16, BIT(31), 1);
+               }
+       }
+}
+
+#undef IQK_ADDA_REG_NUM
+#undef IQK_DELAY_TIME
+
+void rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       long result[4][8];
+       u8 i, final;
+       bool patha_ok;
+       long reg_e94, reg_e9c, reg_ea4, reg_eb4, reg_ebc, reg_tmp = 0;
+       bool is12simular, is13simular, is23simular;
+       u32 iqk_bb_reg[9] = {
+               ROFDM0_XARXIQIMBAL,
+               ROFDM0_XBRXIQIMBAL,
+               ROFDM0_ECCATHRES,
+               ROFDM0_AGCRSSITABLE,
+               ROFDM0_XATXIQIMBAL,
+               ROFDM0_XBTXIQIMBAL,
+               ROFDM0_XCTXAFE,
+               ROFDM0_XDTXAFE,
+               ROFDM0_RXIQEXTANTA
+       };
+
+       if (recovery) {
+               reload_adda(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 9);
+               return;
+       }
+
+       memset(result, 0, 32 * sizeof(long));
+       final = 0xff;
+       patha_ok = false;
+       is12simular = false;
+       is23simular = false;
+       is13simular = false;
+       for (i = 0; i < 3; i++) {
+               if (get_rf_type(rtlphy) == RF_2T2R)
+                       _rtl88e_phy_iq_calibrate(hw, result, i, true);
+               else
+                       _rtl88e_phy_iq_calibrate(hw, result, i, false);
+               if (i == 1) {
+                       is12simular = sim_comp(hw, result, 0, 1);
+                       if (is12simular) {
+                               final = 0;
+                               break;
+                       }
+               }
+               if (i == 2) {
+                       is13simular = sim_comp(hw, result, 0, 2);
+                       if (is13simular) {
+                               final = 0;
+                               break;
+                       }
+                       is23simular = sim_comp(hw, result, 1, 2);
+                       if (is23simular) {
+                               final = 1;
+                       } else {
+                               for (i = 0; i < 8; i++)
+                                       reg_tmp += result[3][i];
+
+                               if (reg_tmp != 0)
+                                       final = 3;
+                               else
+                                       final = 0xFF;
+                       }
+               }
+       }
+       for (i = 0; i < 4; i++) {
+               reg_e94 = result[i][0];
+               reg_e9c = result[i][1];
+               reg_ea4 = result[i][2];
+               reg_eb4 = result[i][4];
+               reg_ebc = result[i][5];
+       }
+       if (final != 0xff) {
+               reg_e94 = result[final][0];
+               rtlphy->reg_e94 = reg_e94;
+               reg_e9c = result[final][1];
+               rtlphy->reg_e9c = reg_e9c;
+               reg_ea4 = result[final][2];
+               reg_eb4 = result[final][4];
+               rtlphy->reg_eb4 = reg_eb4;
+               reg_ebc = result[final][5];
+               rtlphy->reg_ebc = reg_ebc;
+               patha_ok = true;
+       } else {
+               rtlphy->reg_e94 = 0x100;
+               rtlphy->reg_eb4 = 0x100;
+               rtlphy->reg_ebc = 0x0;
+               rtlphy->reg_e9c = 0x0;
+       }
+       if (reg_e94 != 0) /*&&(reg_ea4 != 0) */
+               fill_iqk(hw, patha_ok, result, final, (reg_ea4 == 0));
+       if (final != 0xFF) {
+               for (i = 0; i < IQK_MATRIX_REG_NUM; i++)
+                       rtlphy->iqk_matrix[0].value[0][i] = result[final][i];
+               rtlphy->iqk_matrix[0].iqk_done = true;
+       }
+       save_adda_reg(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 9);
+}
+
+void rtl88e_phy_lc_calibrate(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+       bool start_conttx = false, singletone = false;
+       u32 timeout = 2000, timecount = 0;
+
+       if (start_conttx || singletone)
+               return;
+
+       while (rtlpriv->mac80211.act_scanning && timecount < timeout) {
+               udelay(50);
+               timecount += 50;
+       }
+
+       rtlphy->lck_inprogress = true;
+       RTPRINT(rtlpriv, FINIT, INIT_IQK,
+               "LCK:Start!!! currentband %x delay %d ms\n",
+                rtlhal->current_bandtype, timecount);
+
+       _rtl88e_phy_lc_calibrate(hw, false);
+
+       rtlphy->lck_inprogress = false;
+}
+
+void rtl88e_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain)
+{
+       rfpath_switch(hw, bmain, false);
+}
+
+bool rtl88e_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       bool postprocessing = false;
+
+       RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+                "-->IO Cmd(%#x), set_io_inprogress(%d)\n",
+                iotype, rtlphy->set_io_inprogress);
+       do {
+               switch (iotype) {
+               case IO_CMD_RESUME_DM_BY_SCAN:
+                       RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+                                "[IO CMD] Resume DM after scan.\n");
+                       postprocessing = true;
+                       break;
+               case IO_CMD_PAUSE_DM_BY_SCAN:
+                       RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+                                "[IO CMD] Pause DM before scan.\n");
+                       postprocessing = true;
+                       break;
+               default:
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "switch case not processed\n");
+                       break;
+               }
+       } while (false);
+       if (postprocessing && !rtlphy->set_io_inprogress) {
+               rtlphy->set_io_inprogress = true;
+               rtlphy->current_io_type = iotype;
+       } else {
+               return false;
+       }
+       rtl88e_phy_set_io(hw);
+       RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "IO Type(%#x)\n", iotype);
+       return true;
+}
+
+static void rtl88ee_phy_set_rf_on(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b);
+       rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3);
+       /*rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00);*/
+       rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+       rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3);
+       rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00);
+}
+
+static void _rtl88ee_phy_set_rf_sleep(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       int jj = RF90_PATH_A;
+
+       rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF);
+       rtl_set_rfreg(hw, jj, 0x00, RFREG_OFFSET_MASK, 0x00);
+       rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+       rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22);
+}
+
+static bool _rtl88ee_phy_set_rf_power_state(struct ieee80211_hw *hw,
+                                           enum rf_pwrstate rfpwr_state)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       struct rtl8192_tx_ring *ring = NULL;
+       bool bresult = true;
+       u8 i, queue_id;
+
+       switch (rfpwr_state) {
+       case ERFON:{
+               if ((ppsc->rfpwr_state == ERFOFF) &&
+                   RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) {
+                       bool rtstatus;
+                       u32 init = 0;
+                       do {
+                               init++;
+                               RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+                                        "IPS Set eRf nic enable\n");
+                               rtstatus = rtl_ps_enable_nic(hw);
+                       } while ((rtstatus != true) && (init < 10));
+                       RT_CLEAR_PS_LEVEL(ppsc,
+                                         RT_RF_OFF_LEVL_HALT_NIC);
+               } else {
+                       RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+                                "Set ERFON sleeped:%d ms\n",
+                                jiffies_to_msecs(jiffies - ppsc->
+                                                 last_sleep_jiffies));
+                       ppsc->last_awake_jiffies = jiffies;
+                       rtl88ee_phy_set_rf_on(hw);
+               }
+               if (mac->link_state == MAC80211_LINKED)
+                       rtlpriv->cfg->ops->led_control(hw, LED_CTL_LINK);
+               else
+                       rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK);
+               break; }
+       case ERFOFF:{
+               for (queue_id = 0, i = 0;
+                    queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) {
+                       ring = &pcipriv->dev.tx_ring[queue_id];
+                       if (skb_queue_len(&ring->queue) == 0) {
+                               queue_id++;
+                               continue;
+                       } else {
+                               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                                        "eRf Off/Sleep: %d times TcbBusyQueue[%d] =%d before doze!\n",
+                                        (i + 1), queue_id,
+                                        skb_queue_len(&ring->queue));
+
+                               udelay(10);
+                               i++;
+                       }
+                       if (i >= MAX_DOZE_WAITING_TIMES_9x) {
+                               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                                        "\n ERFSLEEP: %d times TcbBusyQueue[%d] = %d !\n",
+                                         MAX_DOZE_WAITING_TIMES_9x,
+                                         queue_id,
+                                         skb_queue_len(&ring->queue));
+                               break;
+                       }
+               }
+               if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) {
+                       RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+                                "IPS Set eRf nic disable\n");
+                       rtl_ps_disable_nic(hw);
+                       RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+               } else {
+                       if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) {
+                               rtlpriv->cfg->ops->led_control(hw,
+                                               LED_CTL_NO_LINK);
+                       } else {
+                               rtlpriv->cfg->ops->led_control(hw,
+                                               LED_CTL_POWER_OFF);
+                       }
+               }
+               break; }
+       case ERFSLEEP:{
+               if (ppsc->rfpwr_state == ERFOFF)
+                       break;
+               for (queue_id = 0, i = 0;
+                    queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) {
+                       ring = &pcipriv->dev.tx_ring[queue_id];
+                       if (skb_queue_len(&ring->queue) == 0) {
+                               queue_id++;
+                               continue;
+                       } else {
+                               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                                        "eRf Off/Sleep: %d times TcbBusyQueue[%d] =%d before doze!\n",
+                                        (i + 1), queue_id,
+                                        skb_queue_len(&ring->queue));
+
+                               udelay(10);
+                               i++;
+                       }
+                       if (i >= MAX_DOZE_WAITING_TIMES_9x) {
+                               RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+                                        "\n ERFSLEEP: %d times TcbBusyQueue[%d] = %d !\n",
+                                        MAX_DOZE_WAITING_TIMES_9x,
+                                        queue_id,
+                                        skb_queue_len(&ring->queue));
+                               break;
+                       }
+               }
+               RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+                        "Set ERFSLEEP awaked:%d ms\n",
+                        jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies));
+               ppsc->last_sleep_jiffies = jiffies;
+               _rtl88ee_phy_set_rf_sleep(hw);
+               break; }
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "switch case not processed\n");
+               bresult = false;
+               break;
+       }
+       if (bresult)
+               ppsc->rfpwr_state = rfpwr_state;
+       return bresult;
+}
+
+bool rtl88e_phy_set_rf_power_state(struct ieee80211_hw *hw,
+                                  enum rf_pwrstate rfpwr_state)
+{
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+       bool bresult;
+
+       if (rfpwr_state == ppsc->rfpwr_state)
+               return false;
+       bresult = _rtl88ee_phy_set_rf_power_state(hw, rfpwr_state);
+       return bresult;
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h
new file mode 100644 (file)
index 0000000..f1acd6d
--- /dev/null
@@ -0,0 +1,236 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92C_PHY_H__
+#define __RTL92C_PHY_H__
+
+/*It must always set to 4, otherwise read efuse table secquence will be wrong.*/
+#define        MAX_TX_COUNT                            4
+
+#define MAX_PRECMD_CNT                         16
+#define MAX_RFDEPENDCMD_CNT                    16
+#define MAX_POSTCMD_CNT                                16
+
+#define MAX_DOZE_WAITING_TIMES_9x              64
+
+#define RT_CANNOT_IO(hw)                       false
+#define HIGHPOWER_RADIOA_ARRAYLEN              22
+
+#define IQK_ADDA_REG_NUM                       16
+#define IQK_BB_REG_NUM                         9
+#define MAX_TOLERANCE                          5
+#define        IQK_DELAY_TIME                          10
+#define        IDX_MAP                                 15
+
+#define        APK_BB_REG_NUM                          5
+#define        APK_AFE_REG_NUM                         16
+#define        APK_CURVE_REG_NUM                       4
+#define        PATH_NUM                                2
+
+#define LOOP_LIMIT                             5
+#define MAX_STALL_TIME                         50
+#define ANTENNADIVERSITYVALUE                  0x80
+#define MAX_TXPWR_IDX_NMODE_92S                        63
+#define RESET_CNT_LIMIT                                3
+
+#define IQK_ADDA_REG_NUM                       16
+#define IQK_MAC_REG_NUM                                4
+
+#define RF6052_MAX_PATH                                2
+
+#define CT_OFFSET_MAC_ADDR                     0X16
+
+#define CT_OFFSET_CCK_TX_PWR_IDX               0x5A
+#define CT_OFFSET_HT401S_TX_PWR_IDX            0x60
+#define CT_OFFSET_HT402S_TX_PWR_IDX_DIFF       0x66
+#define CT_OFFSET_HT20_TX_PWR_IDX_DIFF         0x69
+#define CT_OFFSET_OFDM_TX_PWR_IDX_DIFF         0x6C
+
+#define CT_OFFSET_HT40_MAX_PWR_OFFSET          0x6F
+#define CT_OFFSET_HT20_MAX_PWR_OFFSET          0x72
+
+#define CT_OFFSET_CHANNEL_PLAH                 0x75
+#define CT_OFFSET_THERMAL_METER                        0x78
+#define CT_OFFSET_RF_OPTION                    0x79
+#define CT_OFFSET_VERSION                      0x7E
+#define CT_OFFSET_CUSTOMER_ID                  0x7F
+
+#define RTL92C_MAX_PATH_NUM                    2
+
+enum swchnlcmd_id {
+       CMDID_END,
+       CMDID_SET_TXPOWEROWER_LEVEL,
+       CMDID_BBREGWRITE10,
+       CMDID_WRITEPORT_ULONG,
+       CMDID_WRITEPORT_USHORT,
+       CMDID_WRITEPORT_UCHAR,
+       CMDID_RF_WRITEREG,
+};
+
+struct swchnlcmd {
+       enum swchnlcmd_id cmdid;
+       u32 para1;
+       u32 para2;
+       u32 msdelay;
+};
+
+enum hw90_block_e {
+       HW90_BLOCK_MAC = 0,
+       HW90_BLOCK_PHY0 = 1,
+       HW90_BLOCK_PHY1 = 2,
+       HW90_BLOCK_RF = 3,
+       HW90_BLOCK_MAXIMUM = 4,
+};
+
+enum baseband_config_type {
+       BASEBAND_CONFIG_PHY_REG = 0,
+       BASEBAND_CONFIG_AGC_TAB = 1,
+};
+
+enum ra_offset_area {
+       RA_OFFSET_LEGACY_OFDM1,
+       RA_OFFSET_LEGACY_OFDM2,
+       RA_OFFSET_HT_OFDM1,
+       RA_OFFSET_HT_OFDM2,
+       RA_OFFSET_HT_OFDM3,
+       RA_OFFSET_HT_OFDM4,
+       RA_OFFSET_HT_CCK,
+};
+
+enum antenna_path {
+       ANTENNA_NONE,
+       ANTENNA_D,
+       ANTENNA_C,
+       ANTENNA_CD,
+       ANTENNA_B,
+       ANTENNA_BD,
+       ANTENNA_BC,
+       ANTENNA_BCD,
+       ANTENNA_A,
+       ANTENNA_AD,
+       ANTENNA_AC,
+       ANTENNA_ACD,
+       ANTENNA_AB,
+       ANTENNA_ABD,
+       ANTENNA_ABC,
+       ANTENNA_ABCD
+};
+
+struct r_antenna_select_ofdm {
+       u32 r_tx_antenna:4;
+       u32 r_ant_l:4;
+       u32 r_ant_non_ht:4;
+       u32 r_ant_ht1:4;
+       u32 r_ant_ht2:4;
+       u32 r_ant_ht_s1:4;
+       u32 r_ant_non_ht_s1:4;
+       u32 ofdm_txsc:2;
+       u32 reserved:2;
+};
+
+struct r_antenna_select_cck {
+       u8 r_cckrx_enable_2:2;
+       u8 r_cckrx_enable:2;
+       u8 r_ccktx_enable:4;
+};
+
+
+struct efuse_contents {
+       u8 mac_addr[ETH_ALEN];
+       u8 cck_tx_power_idx[6];
+       u8 ht40_1s_tx_power_idx[6];
+       u8 ht40_2s_tx_power_idx_diff[3];
+       u8 ht20_tx_power_idx_diff[3];
+       u8 ofdm_tx_power_idx_diff[3];
+       u8 ht40_max_power_offset[3];
+       u8 ht20_max_power_offset[3];
+       u8 channel_plan;
+       u8 thermal_meter;
+       u8 rf_option[5];
+       u8 version;
+       u8 oem_id;
+       u8 regulatory;
+};
+
+struct tx_power_struct {
+       u8 cck[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+       u8 ht40_1s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+       u8 ht40_2s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+       u8 ht20_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+       u8 legacy_ht_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+       u8 legacy_ht_txpowerdiff;
+       u8 groupht20[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+       u8 groupht40[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+       u8 pwrgroup_cnt;
+       u32 mcs_original_offset[4][16];
+};
+
+enum _ANT_DIV_TYPE {
+       NO_ANTDIV                       = 0xFF,
+       CG_TRX_HW_ANTDIV                = 0x01,
+       CGCS_RX_HW_ANTDIV               = 0x02,
+       FIXED_HW_ANTDIV                 = 0x03,
+       CG_TRX_SMART_ANTDIV             = 0x04,
+       CGCS_RX_SW_ANTDIV               = 0x05,
+};
+
+extern u32 rtl88e_phy_query_bb_reg(struct ieee80211_hw *hw,
+                                  u32 regaddr, u32 bitmask);
+extern void rtl88e_phy_set_bb_reg(struct ieee80211_hw *hw,
+                                 u32 regaddr, u32 bitmask, u32 data);
+extern u32 rtl88e_phy_query_rf_reg(struct ieee80211_hw *hw,
+                                  enum radio_path rfpath, u32 regaddr,
+                                  u32 bitmask);
+extern void rtl88e_phy_set_rf_reg(struct ieee80211_hw *hw,
+                                 enum radio_path rfpath, u32 regaddr,
+                                 u32 bitmask, u32 data);
+extern bool rtl88e_phy_mac_config(struct ieee80211_hw *hw);
+extern bool rtl88e_phy_bb_config(struct ieee80211_hw *hw);
+extern bool rtl88e_phy_rf_config(struct ieee80211_hw *hw);
+extern void rtl88e_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
+extern void rtl88e_phy_get_txpower_level(struct ieee80211_hw *hw,
+                                        long *powerlevel);
+extern void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel);
+extern void rtl88e_phy_scan_operation_backup(struct ieee80211_hw *hw,
+                                            u8 operation);
+extern void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
+extern void rtl88e_phy_set_bw_mode(struct ieee80211_hw *hw,
+                                  enum nl80211_channel_type ch_type);
+extern void rtl88e_phy_sw_chnl_callback(struct ieee80211_hw *hw);
+extern u8 rtl88e_phy_sw_chnl(struct ieee80211_hw *hw);
+extern void rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery);
+void rtl88e_phy_lc_calibrate(struct ieee80211_hw *hw);
+void rtl88e_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain);
+bool rtl88e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
+                                         enum radio_path rfpath);
+bool rtl88e_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype);
+extern bool rtl88e_phy_set_rf_power_state(struct ieee80211_hw *hw,
+                                         enum rf_pwrstate rfpwr_state);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.c b/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.c
new file mode 100644 (file)
index 0000000..6dc4e3a
--- /dev/null
@@ -0,0 +1,109 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "pwrseqcmd.h"
+#include "pwrseq.h"
+
+/* drivers should parse below arrays and do the corresponding actions */
+/*3 Power on  Array*/
+struct wlan_pwr_cfg rtl8188e_power_on_flow[RTL8188E_TRANS_CARDEMU_TO_ACT_STEPS +
+                                          RTL8188E_TRANS_END_STEPS] = {
+       RTL8188E_TRANS_CARDEMU_TO_ACT
+       RTL8188E_TRANS_END
+};
+
+/*3Radio off GPIO Array */
+struct wlan_pwr_cfg rtl8188e_radio_off_flow[RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS
+                                           + RTL8188E_TRANS_END_STEPS] = {
+       RTL8188E_TRANS_ACT_TO_CARDEMU
+       RTL8188E_TRANS_END
+};
+
+/*3Card Disable Array*/
+struct wlan_pwr_cfg rtl8188e_card_disable_flow
+       [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+       RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+       RTL8188E_TRANS_END_STEPS] = {
+               RTL8188E_TRANS_ACT_TO_CARDEMU
+               RTL8188E_TRANS_CARDEMU_TO_CARDDIS
+               RTL8188E_TRANS_END
+};
+
+/*3 Card Enable Array*/
+struct wlan_pwr_cfg rtl8188e_card_enable_flow
+       [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+       RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+       RTL8188E_TRANS_END_STEPS] = {
+               RTL8188E_TRANS_CARDDIS_TO_CARDEMU
+               RTL8188E_TRANS_CARDEMU_TO_ACT
+               RTL8188E_TRANS_END
+};
+
+/*3Suspend Array*/
+struct wlan_pwr_cfg rtl8188e_suspend_flow[RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS
+                                       + RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS
+                                       + RTL8188E_TRANS_END_STEPS] = {
+       RTL8188E_TRANS_ACT_TO_CARDEMU
+       RTL8188E_TRANS_CARDEMU_TO_SUS
+       RTL8188E_TRANS_END
+};
+
+/*3 Resume Array*/
+struct wlan_pwr_cfg rtl8188e_resume_flow[RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS
+                                       + RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS
+                                       + RTL8188E_TRANS_END_STEPS] = {
+       RTL8188E_TRANS_SUS_TO_CARDEMU
+       RTL8188E_TRANS_CARDEMU_TO_ACT
+       RTL8188E_TRANS_END
+};
+
+/*3HWPDN Array*/
+struct wlan_pwr_cfg rtl8188e_hwpdn_flow[RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS
+                               + RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS
+                               + RTL8188E_TRANS_END_STEPS] = {
+       RTL8188E_TRANS_ACT_TO_CARDEMU
+       RTL8188E_TRANS_CARDEMU_TO_PDN
+       RTL8188E_TRANS_END
+};
+
+/*3 Enter LPS */
+struct wlan_pwr_cfg rtl8188e_enter_lps_flow[RTL8188E_TRANS_ACT_TO_LPS_STEPS
+                                       + RTL8188E_TRANS_END_STEPS] = {
+       /*FW behavior*/
+       RTL8188E_TRANS_ACT_TO_LPS
+       RTL8188E_TRANS_END
+};
+
+/*3 Leave LPS */
+struct wlan_pwr_cfg rtl8188e_leave_lps_flow[RTL8188E_TRANS_LPS_TO_ACT_STEPS
+                                       + RTL8188E_TRANS_END_STEPS] = {
+       /*FW behavior*/
+       RTL8188E_TRANS_LPS_TO_ACT
+       RTL8188E_TRANS_END
+};
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h
new file mode 100644 (file)
index 0000000..028ec6d
--- /dev/null
@@ -0,0 +1,327 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723E_PWRSEQ_H__
+#define __RTL8723E_PWRSEQ_H__
+
+#include "pwrseqcmd.h"
+/*
+       Check document WM-20110607-Paul-RTL8188E_Power_Architecture-R02.vsd
+       There are 6 HW Power States:
+       0: POFF--Power Off
+       1: PDN--Power Down
+       2: CARDEMU--Card Emulation
+       3: ACT--Active Mode
+       4: LPS--Low Power State
+       5: SUS--Suspend
+
+       The transision from different states are defined below
+       TRANS_CARDEMU_TO_ACT
+       TRANS_ACT_TO_CARDEMU
+       TRANS_CARDEMU_TO_SUS
+       TRANS_SUS_TO_CARDEMU
+       TRANS_CARDEMU_TO_PDN
+       TRANS_ACT_TO_LPS
+       TRANS_LPS_TO_ACT
+
+       TRANS_END
+       PWR SEQ Version: rtl8188e_PwrSeq_V09.h
+*/
+
+#define        RTL8188E_TRANS_CARDEMU_TO_ACT_STEPS     10
+#define        RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS     10
+#define        RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS     10
+#define        RTL8188E_TRANS_SUS_TO_CARDEMU_STEPS     10
+#define        RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS     10
+#define        RTL8188E_TRANS_PDN_TO_CARDEMU_STEPS     10
+#define        RTL8188E_TRANS_ACT_TO_LPS_STEPS         15
+#define        RTL8188E_TRANS_LPS_TO_ACT_STEPS         15
+#define        RTL8188E_TRANS_END_STEPS                1
+
+
+#define RTL8188E_TRANS_CARDEMU_TO_ACT                                  \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value },*/\
+       {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /* wait till 0x04[17] = 1    power ready*/                      \
+       PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), BIT(1)},             \
+       {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /* 0x02[1:0] = 0        reset BB*/                              \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0)|BIT(1), 0},             \
+       {0x0026, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*0x24[23] = 2b'01 schmit trigger */                            \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)},               \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /* 0x04[15] = 0 disable HWPDN (control by DRV)*/                \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0},                    \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*0x04[12:11] = 2b'00 disable WL suspend*/                      \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4)|BIT(3), 0},             \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*0x04[8] = 1 polling until return 0*/                          \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},               \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*wait till 0x04[8] = 0*/                                       \
+       PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(0), 0},                  \
+       {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, /*LDO normal mode*/\
+       {0x0074, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, /*SDIO Driving*/\
+
+#define RTL8188E_TRANS_ACT_TO_CARDEMU                                  \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value },*/\
+       {0x001F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0},/*0x1F[7:0] = 0 turn off RF*/\
+       {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, /*LDO Sleep mode*/\
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*0x04[9] = 1 turn off MAC by HW state machine*/                \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)},               \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*wait till 0x04[9] = 0 polling until return 0 to disable*/     \
+       PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), 0},                  \
+
+
+#define RTL8188E_TRANS_CARDEMU_TO_SUS                                  \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value },*/\
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,                      \
+       PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK,                             \
+       /*0x04[12:11] = 2b'01enable WL suspend*/                        \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)},        \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,    \
+       /*0x04[12:11] = 2b'11enable WL suspend for PCIe*/               \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)|BIT(4)},\
+       {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,                      \
+       PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK,                             \
+       /*  0x04[31:30] = 2b'10 enable enable bandgap mbias in suspend */\
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, BIT(7)},                 \
+       {0x0041, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,                      \
+       PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK,                             \
+       /*Clear SIC_EN register 0x40[12] = 1'b0 */                      \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0},                    \
+       {0xfe10, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,                      \
+       PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK,                             \
+       /*Set USB suspend enable local register  0xfe10[4]= 1 */        \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)},               \
+       {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       /*Set SDIO suspend local register*/                             \
+       PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)},              \
+       {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       /*wait power state to suspend*/                                 \
+       PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), 0},
+
+#define RTL8188E_TRANS_SUS_TO_CARDEMU                                  \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\
+       {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       /*Set SDIO suspend local register*/                             \
+       PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0},                   \
+       {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       /*wait power state to suspend*/                                 \
+       PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)},            \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*0x04[12:11] = 2b'01enable WL suspend*/                        \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0},
+
+#define RTL8188E_TRANS_CARDEMU_TO_CARDDIS                              \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\
+       {0x0026, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*0x24[23] = 2b'01 schmit trigger */                            \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)},               \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,                      \
+       PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK,                             \
+       /*0x04[12:11] = 2b'01 enable WL suspend*/                       \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)},        \
+       {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,                      \
+       PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK,                             \
+       /*  0x04[31:30] = 2b'10 enable enable bandgap mbias in suspend */\
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0},                      \
+       {0x0041, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,                      \
+       PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK,                             \
+       /*Clear SIC_EN register 0x40[12] = 1'b0 */                      \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0},                    \
+       {0xfe10, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK,    \
+       /*Set USB suspend enable local register  0xfe10[4]= 1 */        \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)},               \
+       {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       /*Set SDIO suspend local register*/                             \
+       PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)},              \
+       {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       PWR_CMD_POLLING, BIT(1), 0}, /*wait power state to suspend*/
+
+#define RTL8188E_TRANS_CARDDIS_TO_CARDEMU                              \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\
+       {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       PWR_BASEADDR_SDIO,\
+       PWR_CMD_WRITE, BIT(0), 0}, /*Set SDIO suspend local register*/  \
+       {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       PWR_BASEADDR_SDIO,\
+       PWR_CMD_POLLING, BIT(1), BIT(1)}, /*wait power state to suspend*/\
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC,                                               \
+       PWR_CMD_WRITE, BIT(3)|BIT(4), 0},                               \
+       /*0x04[12:11] = 2b'01enable WL suspend*/
+
+
+#define RTL8188E_TRANS_CARDEMU_TO_PDN                                  \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\
+       {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/* 0x04[16] = 0*/   \
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)},/* 0x04[15] = 1*/
+
+
+#define RTL8188E_TRANS_PDN_TO_CARDEMU                                  \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\
+       {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0},/* 0x04[15] = 0*/
+
+
+#define RTL8188E_TRANS_ACT_TO_LPS                                      \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value },*/\
+       {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x7F},/*Tx Pause*/       \
+       {0x05F8, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*zero if no pkt is tx*/\
+       PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},                    \
+       {0x05F9, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*Should be zero if no packet is transmitting*/ \
+       PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},                    \
+       {0x05FA, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*Should be zero if no packet is transmitting*/                 \
+       PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},                    \
+       {0x05FB, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*Should be zero if no packet is transmitting*/                 \
+       PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},                    \
+       {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*CCK and OFDM are disabled, and clock are gated*/              \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},                    \
+       {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_US},/*Delay 1us*/\
+       {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x3F},/*Reset MAC TRX*/  \
+       {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*check if removed later*/                                      \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0},                    \
+       {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*Respond TxOK to scheduler*/                                   \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)},               \
+
+
+#define RTL8188E_TRANS_LPS_TO_ACT                                      \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\
+       {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,   \
+       PWR_BASEADDR_SDIO, PWR_CMD_WRITE, 0xFF, 0x84}, /*SDIO RPWM*/    \
+       {0xFE58, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, /*USB RPWM*/      \
+       {0x0361, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, /*PCIe RPWM*/     \
+       {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_MS}, /*Delay*/ \
+       {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*.     0x08[4] = 0              switch TSF to 40M*/            \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0},                    \
+       {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*Polling 0x109[7]= 0  TSF in 40M*/                             \
+       PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(7), 0},                  \
+       {0x0029, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*. 0x29[7:6] = 2b'00    enable BB clock*/                      \
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6)|BIT(7), 0},             \
+       {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*.     0x101[1] = 1*/\
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)},               \
+       {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*.     0x100[7:0] = 0xFF        enable WMAC TRX*/\
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF},                   \
+       {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,    \
+       /*. 0x02[1:0] = 2b'11  enable BB macro*/\
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1)|BIT(0), BIT(1)|BIT(0)}, \
+       {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\
+       PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, /*.  0x522 = 0*/
+
+
+#define RTL8188E_TRANS_END                                             \
+       /* format */                                                    \
+       /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value },*/\
+       {0xFFFF, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\
+       0, PWR_CMD_END, 0, 0}
+
+extern struct wlan_pwr_cfg rtl8188e_power_on_flow
+               [RTL8188E_TRANS_CARDEMU_TO_ACT_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8188e_radio_off_flow
+               [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8188e_card_disable_flow
+               [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+               RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8188e_card_enable_flow
+               [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+               RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8188e_suspend_flow
+               [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+               RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8188e_resume_flow
+               [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+               RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8188e_hwpdn_flow
+               [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+               RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8188e_enter_lps_flow
+               [RTL8188E_TRANS_ACT_TO_LPS_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8188e_leave_lps_flow
+               [RTL8188E_TRANS_LPS_TO_ACT_STEPS +
+               RTL8188E_TRANS_END_STEPS];
+
+/* RTL8723 Power Configuration CMDs for PCIe interface */
+#define Rtl8188E_NIC_PWR_ON_FLOW       rtl8188e_power_on_flow
+#define Rtl8188E_NIC_RF_OFF_FLOW       rtl8188e_radio_off_flow
+#define Rtl8188E_NIC_DISABLE_FLOW      rtl8188e_card_disable_flow
+#define Rtl8188E_NIC_ENABLE_FLOW       rtl8188e_card_enable_flow
+#define Rtl8188E_NIC_SUSPEND_FLOW      rtl8188e_suspend_flow
+#define Rtl8188E_NIC_RESUME_FLOW       rtl8188e_resume_flow
+#define Rtl8188E_NIC_PDN_FLOW          rtl8188e_hwpdn_flow
+#define Rtl8188E_NIC_LPS_ENTER_FLOW    rtl8188e_enter_lps_flow
+#define Rtl8188E_NIC_LPS_LEAVE_FLOW    rtl8188e_leave_lps_flow
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseqcmd.c b/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseqcmd.c
new file mode 100644 (file)
index 0000000..a9cfa13
--- /dev/null
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "pwrseq.h"
+
+
+/*     Description:
+ *             This routine deal with the Power Configuration CMDs
+ *              parsing for RTL8723/RTL8188E Series IC.
+ *     Assumption:
+ *             We should follow specific format which was released from HW SD.
+ *
+ *     2011.07.07, added by Roger.
+ */
+
+bool rtl88_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version,
+                               u8 fab_version, u8 interface_type,
+                               struct wlan_pwr_cfg pwrcfgcmd[])
+{
+       struct wlan_pwr_cfg cmd = {0};
+       bool polling_bit = false;
+       u32 ary_idx = 0;
+       u8 val = 0;
+       u32 offset = 0;
+       u32 polling_count = 0;
+       u32 max_polling_cnt = 5000;
+
+       do {
+               cmd = pwrcfgcmd[ary_idx];
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "rtl88_hal_pwrseqcmdparsing(): offset(%#x), cut_msk(%#x), fab_msk(%#x),"
+                        "interface_msk(%#x), base(%#x), cmd(%#x), msk(%#x), val(%#x)\n",
+                        GET_PWR_CFG_OFFSET(cmd),
+                        GET_PWR_CFG_CUT_MASK(cmd),
+                        GET_PWR_CFG_FAB_MASK(cmd),
+                        GET_PWR_CFG_INTF_MASK(cmd),
+                        GET_PWR_CFG_BASE(cmd),
+                        GET_PWR_CFG_CMD(cmd),
+                        GET_PWR_CFG_MASK(cmd),
+                        GET_PWR_CFG_VALUE(cmd));
+
+               if ((GET_PWR_CFG_FAB_MASK(cmd) & fab_version) &&
+                   (GET_PWR_CFG_CUT_MASK(cmd) & cut_version) &&
+                   (GET_PWR_CFG_INTF_MASK(cmd) & interface_type)) {
+                       switch (GET_PWR_CFG_CMD(cmd)) {
+                       case PWR_CMD_READ:
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                                        "rtl88_hal_pwrseqcmdparsing(): PWR_CMD_READ\n");
+                               break;
+                       case PWR_CMD_WRITE: {
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                                        "rtl88_hal_pwrseqcmdparsing(): PWR_CMD_WRITE\n");
+                               offset = GET_PWR_CFG_OFFSET(cmd);
+
+                                       /*Read the val from system register*/
+                                       val = rtl_read_byte(rtlpriv, offset);
+                                       val &= (~(GET_PWR_CFG_MASK(cmd)));
+                                       val |= (GET_PWR_CFG_VALUE(cmd) &
+                                               GET_PWR_CFG_MASK(cmd));
+
+                                       /*Write the val back to sytem register*/
+                                       rtl_write_byte(rtlpriv, offset, val);
+                               }
+                               break;
+                       case PWR_CMD_POLLING:
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                                        "rtl88_hal_pwrseqcmdparsing(): PWR_CMD_POLLING\n");
+                               polling_bit = false;
+                               offset = GET_PWR_CFG_OFFSET(cmd);
+
+                               do {
+                                       val = rtl_read_byte(rtlpriv, offset);
+
+                                       val = val & GET_PWR_CFG_MASK(cmd);
+                                       if (val == (GET_PWR_CFG_VALUE(cmd) &
+                                                   GET_PWR_CFG_MASK(cmd)))
+                                               polling_bit = true;
+                                       else
+                                               udelay(10);
+
+                                       if (polling_count++ > max_polling_cnt) {
+                                               RT_TRACE(rtlpriv, COMP_INIT,
+                                                        DBG_LOUD,
+                                                        "polling fail in pwrseqcmd\n");
+                                               return false;
+                                       }
+                               } while (!polling_bit);
+
+                               break;
+                       case PWR_CMD_DELAY:
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                                        "rtl88_hal_pwrseqcmdparsing(): PWR_CMD_DELAY\n");
+                               if (GET_PWR_CFG_VALUE(cmd) == PWRSEQ_DELAY_US)
+                                       udelay(GET_PWR_CFG_OFFSET(cmd));
+                               else
+                                       mdelay(GET_PWR_CFG_OFFSET(cmd));
+                               break;
+                       case PWR_CMD_END:
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                                        "rtl88_hal_pwrseqcmdparsing(): PWR_CMD_END\n");
+                               return true;
+                               break;
+                       default:
+                               RT_ASSERT(false,
+                                         "rtl88_hal_pwrseqcmdparsing(): Unknown CMD!!\n");
+                               break;
+                       }
+               }
+
+               ary_idx++;
+       } while (1);
+
+       return true;
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseqcmd.h b/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseqcmd.h
new file mode 100644 (file)
index 0000000..d9ae280
--- /dev/null
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723E_PWRSEQCMD_H__
+#define __RTL8723E_PWRSEQCMD_H__
+
+#include "../wifi.h"
+/*---------------------------------------------*/
+/* The value of cmd: 4 bits */
+/*---------------------------------------------*/
+#define PWR_CMD_READ           0x00
+#define PWR_CMD_WRITE          0x01
+#define PWR_CMD_POLLING                0x02
+#define PWR_CMD_DELAY          0x03
+#define PWR_CMD_END            0x04
+
+/* define the base address of each block */
+#define PWR_BASEADDR_MAC       0x00
+#define PWR_BASEADDR_USB       0x01
+#define PWR_BASEADDR_PCIE      0x02
+#define PWR_BASEADDR_SDIO      0x03
+
+#define PWR_INTF_SDIO_MSK      BIT(0)
+#define PWR_INTF_USB_MSK       BIT(1)
+#define PWR_INTF_PCI_MSK       BIT(2)
+#define PWR_INTF_ALL_MSK       (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+
+#define        PWR_FAB_TSMC_MSK        BIT(0)
+#define        PWR_FAB_UMC_MSK         BIT(1)
+#define        PWR_FAB_ALL_MSK         (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+
+#define        PWR_CUT_TESTCHIP_MSK    BIT(0)
+#define        PWR_CUT_A_MSK           BIT(1)
+#define        PWR_CUT_B_MSK           BIT(2)
+#define        PWR_CUT_C_MSK           BIT(3)
+#define        PWR_CUT_D_MSK           BIT(4)
+#define        PWR_CUT_E_MSK           BIT(5)
+#define        PWR_CUT_F_MSK           BIT(6)
+#define        PWR_CUT_G_MSK           BIT(7)
+#define        PWR_CUT_ALL_MSK         0xFF
+
+enum pwrseq_delay_unit {
+       PWRSEQ_DELAY_US,
+       PWRSEQ_DELAY_MS,
+};
+
+struct wlan_pwr_cfg {
+       u16 offset;
+       u8 cut_msk;
+       u8 fab_msk:4;
+       u8 interface_msk:4;
+       u8 base:4;
+       u8 cmd:4;
+       u8 msk;
+       u8 value;
+};
+
+#define        GET_PWR_CFG_OFFSET(__PWR)       (__PWR.offset)
+#define        GET_PWR_CFG_CUT_MASK(__PWR)     (__PWR.cut_msk)
+#define        GET_PWR_CFG_FAB_MASK(__PWR)     (__PWR.fab_msk)
+#define        GET_PWR_CFG_INTF_MASK(__PWR)    (__PWR.interface_msk)
+#define        GET_PWR_CFG_BASE(__PWR)         (__PWR.base)
+#define        GET_PWR_CFG_CMD(__PWR)          (__PWR.cmd)
+#define        GET_PWR_CFG_MASK(__PWR)         (__PWR.msk)
+#define        GET_PWR_CFG_VALUE(__PWR)        (__PWR.value)
+
+bool rtl88_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version,
+                               u8 fab_version, u8 interface_type,
+                               struct wlan_pwr_cfg pwrcfgcmd[]);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h
new file mode 100644 (file)
index 0000000..d849abf
--- /dev/null
@@ -0,0 +1,2258 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92C_REG_H__
+#define __RTL92C_REG_H__
+
+#define TXPKT_BUF_SELECT                       0x69
+#define RXPKT_BUF_SELECT                       0xA5
+#define DISABLE_TRXPKT_BUF_ACCESS              0x0
+
+#define REG_SYS_ISO_CTRL                       0x0000
+#define REG_SYS_FUNC_EN                                0x0002
+#define REG_APS_FSMCO                          0x0004
+#define REG_SYS_CLKR                           0x0008
+#define REG_9346CR                             0x000A
+#define REG_EE_VPD                             0x000C
+#define REG_AFE_MISC                           0x0010
+#define REG_SPS0_CTRL                          0x0011
+#define REG_SPS_OCP_CFG                                0x0018
+#define REG_RSV_CTRL                           0x001C
+#define REG_RF_CTRL                            0x001F
+#define REG_LDOA15_CTRL                                0x0020
+#define REG_LDOV12D_CTRL                       0x0021
+#define REG_LDOHCI12_CTRL                      0x0022
+#define REG_LPLDO_CTRL                         0x0023
+#define REG_AFE_XTAL_CTRL                      0x0024
+#define REG_AFE_LDO_CTRL                       0x0027 /* 1.5v for 8188EE test
+                                                       * chip, 1.4v for MP chip
+                                                       */
+#define REG_AFE_PLL_CTRL                       0x0028
+#define REG_EFUSE_CTRL                         0x0030
+#define REG_EFUSE_TEST                         0x0034
+#define REG_PWR_DATA                           0x0038
+#define REG_CAL_TIMER                          0x003C
+#define REG_ACLK_MON                           0x003E
+#define REG_GPIO_MUXCFG                                0x0040
+#define REG_GPIO_IO_SEL                                0x0042
+#define REG_MAC_PINMUX_CFG                     0x0043
+#define REG_GPIO_PIN_CTRL                      0x0044
+#define REG_GPIO_INTM                          0x0048
+#define REG_LEDCFG0                            0x004C
+#define REG_LEDCFG1                            0x004D
+#define REG_LEDCFG2                            0x004E
+#define REG_LEDCFG3                            0x004F
+#define REG_FSIMR                              0x0050
+#define REG_FSISR                              0x0054
+#define REG_HSIMR                              0x0058
+#define REG_HSISR                              0x005c
+#define REG_GPIO_PIN_CTRL_2                    0x0060
+#define REG_GPIO_IO_SEL_2                      0x0062
+#define REG_GPIO_OUTPUT                                0x006c
+#define REG_AFE_XTAL_CTRL_EXT                  0x0078
+#define REG_XCK_OUT_CTRL                       0x007c
+#define REG_MCUFWDL                            0x0080
+#define REG_WOL_EVENT                          0x0081
+#define REG_MCUTSTCFG                          0x0084
+
+
+#define REG_HIMR                               0x00B0
+#define REG_HISR                               0x00B4
+#define REG_HIMRE                              0x00B8
+#define REG_HISRE                              0x00BC
+
+#define REG_EFUSE_ACCESS                       0x00CF
+
+#define REG_BIST_SCAN                          0x00D0
+#define REG_BIST_RPT                           0x00D4
+#define REG_BIST_ROM_RPT                       0x00D8
+#define REG_USB_SIE_INTF                       0x00E0
+#define REG_PCIE_MIO_INTF                      0x00E4
+#define REG_PCIE_MIO_INTD                      0x00E8
+#define REG_HPON_FSM                           0x00EC
+#define REG_SYS_CFG                            0x00F0
+
+#define REG_CR                                 0x0100
+#define REG_PBP                                        0x0104
+#define REG_PKT_BUFF_ACCESS_CTRL               0x0106
+#define REG_TRXDMA_CTRL                                0x010C
+#define REG_TRXFF_BNDY                         0x0114
+#define REG_TRXFF_STATUS                       0x0118
+#define REG_RXFF_PTR                           0x011C
+
+#define REG_CPWM                               0x012F
+#define REG_FWIMR                              0x0130
+#define REG_FWISR                              0x0134
+#define REG_PKTBUF_DBG_CTRL                    0x0140
+#define REG_PKTBUF_DBG_DATA_L                  0x0144
+#define REG_PKTBUF_DBG_DATA_H                  0x0148
+#define REG_RXPKTBUF_CTRL                      (REG_PKTBUF_DBG_CTRL+2)
+
+#define REG_TC0_CTRL                           0x0150
+#define REG_TC1_CTRL                           0x0154
+#define REG_TC2_CTRL                           0x0158
+#define REG_TC3_CTRL                           0x015C
+#define REG_TC4_CTRL                           0x0160
+#define REG_TCUNIT_BASE                                0x0164
+#define REG_MBIST_START                                0x0174
+#define REG_MBIST_DONE                         0x0178
+#define REG_MBIST_FAIL                         0x017C
+#define REG_32K_CTRL                           0x0194
+#define REG_C2HEVT_MSG_NORMAL                  0x01A0
+#define REG_C2HEVT_CLEAR                       0x01AF
+#define REG_C2HEVT_MSG_TEST                    0x01B8
+#define REG_MCUTST_1                           0x01c0
+#define REG_FMETHR                             0x01C8
+#define REG_HMETFR                             0x01CC
+#define REG_HMEBOX_0                           0x01D0
+#define REG_HMEBOX_1                           0x01D4
+#define REG_HMEBOX_2                           0x01D8
+#define REG_HMEBOX_3                           0x01DC
+
+#define REG_LLT_INIT                           0x01E0
+#define REG_BB_ACCEESS_CTRL                    0x01E8
+#define REG_BB_ACCESS_DATA                     0x01EC
+
+#define REG_HMEBOX_EXT_0                       0x01F0
+#define REG_HMEBOX_EXT_1                       0x01F4
+#define REG_HMEBOX_EXT_2                       0x01F8
+#define REG_HMEBOX_EXT_3                       0x01FC
+
+#define REG_RQPN                               0x0200
+#define REG_FIFOPAGE                           0x0204
+#define REG_TDECTRL                            0x0208
+#define REG_TXDMA_OFFSET_CHK                   0x020C
+#define REG_TXDMA_STATUS                       0x0210
+#define REG_RQPN_NPQ                           0x0214
+
+#define REG_RXDMA_AGG_PG_TH                    0x0280
+#define REG_FW_UPD_RDPTR                       0x0284 /* FW shall update this
+                                               * register before FW * write
+                                               * RXPKT_RELEASE_POLL to 1
+                                               */
+#define REG_RXDMA_CONTROL                      0x0286 /* Control the RX DMA.*/
+#define REG_RXPKT_NUM                          0x0287 /* The number of packets
+                                                       * in RXPKTBUF.
+                                                        */
+#define        REG_PCIE_CTRL_REG                       0x0300
+#define        REG_INT_MIG                             0x0304
+#define        REG_BCNQ_DESA                           0x0308
+#define        REG_HQ_DESA                             0x0310
+#define        REG_MGQ_DESA                            0x0318
+#define        REG_VOQ_DESA                            0x0320
+#define        REG_VIQ_DESA                            0x0328
+#define        REG_BEQ_DESA                            0x0330
+#define        REG_BKQ_DESA                            0x0338
+#define        REG_RX_DESA                             0x0340
+
+#define        REG_DBI                                 0x0348
+#define        REG_MDIO                                0x0354
+#define        REG_DBG_SEL                             0x0360
+#define        REG_PCIE_HRPWM                          0x0361
+#define        REG_PCIE_HCPWM                          0x0363
+#define        REG_UART_CTRL                           0x0364
+#define        REG_WATCH_DOG                           0x0368
+#define        REG_UART_TX_DESA                        0x0370
+#define        REG_UART_RX_DESA                        0x0378
+
+
+#define        REG_HDAQ_DESA_NODEF                     0x0000
+#define        REG_CMDQ_DESA_NODEF                     0x0000
+
+#define REG_VOQ_INFORMATION                    0x0400
+#define REG_VIQ_INFORMATION                    0x0404
+#define REG_BEQ_INFORMATION                    0x0408
+#define REG_BKQ_INFORMATION                    0x040C
+#define REG_MGQ_INFORMATION                    0x0410
+#define REG_HGQ_INFORMATION                    0x0414
+#define REG_BCNQ_INFORMATION                   0x0418
+#define REG_TXPKT_EMPTY                                0x041A
+
+
+#define REG_CPU_MGQ_INFORMATION                        0x041C
+#define REG_FWHW_TXQ_CTRL                      0x0420
+#define REG_HWSEQ_CTRL                         0x0423
+#define REG_TXPKTBUF_BCNQ_BDNY                 0x0424
+#define REG_TXPKTBUF_MGQ_BDNY                  0x0425
+#define REG_MULTI_BCNQ_EN                      0x0426
+#define REG_MULTI_BCNQ_OFFSET                  0x0427
+#define REG_SPEC_SIFS                          0x0428
+#define REG_RL                                 0x042A
+#define REG_DARFRC                             0x0430
+#define REG_RARFRC                             0x0438
+#define REG_RRSR                               0x0440
+#define REG_ARFR0                              0x0444
+#define REG_ARFR1                              0x0448
+#define REG_ARFR2                              0x044C
+#define REG_ARFR3                              0x0450
+#define REG_AGGLEN_LMT                         0x0458
+#define REG_AMPDU_MIN_SPACE                    0x045C
+#define REG_TXPKTBUF_WMAC_LBK_BF_HD            0x045D
+#define REG_FAST_EDCA_CTRL                     0x0460
+#define REG_RD_RESP_PKT_TH                     0x0463
+#define REG_INIRTS_RATE_SEL                    0x0480
+#define REG_INIDATA_RATE_SEL                   0x0484
+#define REG_POWER_STATUS                       0x04A4
+#define REG_POWER_STAGE1                       0x04B4
+#define REG_POWER_STAGE2                       0x04B8
+#define REG_PKT_LIFE_TIME                      0x04C0
+#define REG_STBC_SETTING                       0x04C4
+#define REG_PROT_MODE_CTRL                     0x04C8
+#define REG_BAR_MODE_CTRL                      0x04CC
+#define REG_RA_TRY_RATE_AGG_LMT                        0x04CF
+#define REG_EARLY_MODE_CONTROL                 0x04D0
+#define REG_NQOS_SEQ                           0x04DC
+#define REG_QOS_SEQ                            0x04DE
+#define REG_NEED_CPU_HANDLE                    0x04E0
+#define REG_PKT_LOSE_RPT                       0x04E1
+#define REG_PTCL_ERR_STATUS                    0x04E2
+#define REG_TX_RPT_CTRL                                0x04EC
+#define REG_TX_RPT_TIME                                0x04F0
+#define REG_DUMMY                              0x04FC
+
+#define REG_EDCA_VO_PARAM                      0x0500
+#define REG_EDCA_VI_PARAM                      0x0504
+#define REG_EDCA_BE_PARAM                      0x0508
+#define REG_EDCA_BK_PARAM                      0x050C
+#define REG_BCNTCFG                            0x0510
+#define REG_PIFS                               0x0512
+#define REG_RDG_PIFS                           0x0513
+#define REG_SIFS_CTX                           0x0514
+#define REG_SIFS_TRX                           0x0516
+#define REG_AGGR_BREAK_TIME                    0x051A
+#define REG_SLOT                               0x051B
+#define REG_TX_PTCL_CTRL                       0x0520
+#define REG_TXPAUSE                            0x0522
+#define REG_DIS_TXREQ_CLR                      0x0523
+#define REG_RD_CTRL                            0x0524
+#define REG_TBTT_PROHIBIT                      0x0540
+#define REG_RD_NAV_NXT                         0x0544
+#define REG_NAV_PROT_LEN                       0x0546
+#define REG_BCN_CTRL                           0x0550
+#define REG_USTIME_TSF                         0x0551
+#define REG_MBID_NUM                           0x0552
+#define REG_DUAL_TSF_RST                       0x0553
+#define REG_BCN_INTERVAL                       0x0554
+#define REG_MBSSID_BCN_SPACE                   0x0554
+#define REG_DRVERLYINT                         0x0558
+#define REG_BCNDMATIM                          0x0559
+#define REG_ATIMWND                            0x055A
+#define REG_BCN_MAX_ERR                                0x055D
+#define REG_RXTSF_OFFSET_CCK                   0x055E
+#define REG_RXTSF_OFFSET_OFDM                  0x055F
+#define REG_TSFTR                              0x0560
+#define REG_INIT_TSFTR                         0x0564
+#define REG_PSTIMER                            0x0580
+#define REG_TIMER0                             0x0584
+#define REG_TIMER1                             0x0588
+#define REG_ACMHWCTRL                          0x05C0
+#define REG_ACMRSTCTRL                         0x05C1
+#define REG_ACMAVG                             0x05C2
+#define REG_VO_ADMTIME                         0x05C4
+#define REG_VI_ADMTIME                         0x05C6
+#define REG_BE_ADMTIME                         0x05C8
+#define REG_EDCA_RANDOM_GEN                    0x05CC
+#define REG_SCH_TXCMD                          0x05D0
+
+#define REG_APSD_CTRL                          0x0600
+#define REG_BWOPMODE                           0x0603
+#define REG_TCR                                        0x0604
+#define REG_RCR                                        0x0608
+#define REG_RX_PKT_LIMIT                       0x060C
+#define REG_RX_DLK_TIME                                0x060D
+#define REG_RX_DRVINFO_SZ                      0x060F
+
+#define REG_MACID                              0x0610
+#define REG_BSSID                              0x0618
+#define REG_MAR                                        0x0620
+#define REG_MBIDCAMCFG                         0x0628
+
+#define REG_USTIME_EDCA                                0x0638
+#define REG_MAC_SPEC_SIFS                      0x063A
+#define REG_RESP_SIFS_CCK                      0x063C
+#define REG_RESP_SIFS_OFDM                     0x063E
+#define REG_ACKTO                              0x0640
+#define REG_CTS2TO                             0x0641
+#define REG_EIFS                               0x0642
+
+#define REG_NAV_CTRL                           0x0650
+#define REG_BACAMCMD                           0x0654
+#define REG_BACAMCONTENT                       0x0658
+#define REG_LBDLY                              0x0660
+#define REG_FWDLY                              0x0661
+#define REG_RXERR_RPT                          0x0664
+#define REG_TRXPTCL_CTL                                0x0668
+
+#define REG_CAMCMD                             0x0670
+#define REG_CAMWRITE                           0x0674
+#define REG_CAMREAD                            0x0678
+#define REG_CAMDBG                             0x067C
+#define REG_SECCFG                             0x0680
+
+#define REG_WOW_CTRL                           0x0690
+#define REG_PSSTATUS                           0x0691
+#define REG_PS_RX_INFO                         0x0692
+#define REG_UAPSD_TID                          0x0693
+#define REG_LPNAV_CTRL                         0x0694
+#define REG_WKFMCAM_NUM                                0x0698
+#define REG_WKFMCAM_RWD                                0x069C
+#define REG_RXFLTMAP0                          0x06A0
+#define REG_RXFLTMAP1                          0x06A2
+#define REG_RXFLTMAP2                          0x06A4
+#define REG_BCN_PSR_RPT                                0x06A8
+#define REG_CALB32K_CTRL                       0x06AC
+#define REG_PKT_MON_CTRL                       0x06B4
+#define REG_BT_COEX_TABLE                      0x06C0
+#define REG_WMAC_RESP_TXINFO                   0x06D8
+
+#define REG_USB_INFO                           0xFE17
+#define REG_USB_SPECIAL_OPTION                 0xFE55
+#define REG_USB_DMA_AGG_TO                     0xFE5B
+#define REG_USB_AGG_TO                         0xFE5C
+#define REG_USB_AGG_TH                         0xFE5D
+
+#define REG_TEST_USB_TXQS                      0xFE48
+#define REG_TEST_SIE_VID                       0xFE60
+#define REG_TEST_SIE_PID                       0xFE62
+#define REG_TEST_SIE_OPTIONAL                  0xFE64
+#define REG_TEST_SIE_CHIRP_K                   0xFE65
+#define REG_TEST_SIE_PHY                       0xFE66
+#define REG_TEST_SIE_MAC_ADDR                  0xFE70
+#define REG_TEST_SIE_STRING                    0xFE80
+
+#define REG_NORMAL_SIE_VID                     0xFE60
+#define REG_NORMAL_SIE_PID                     0xFE62
+#define REG_NORMAL_SIE_OPTIONAL                        0xFE64
+#define REG_NORMAL_SIE_EP                      0xFE65
+#define REG_NORMAL_SIE_PHY                     0xFE68
+#define REG_NORMAL_SIE_MAC_ADDR                        0xFE70
+#define REG_NORMAL_SIE_STRING                  0xFE80
+
+#define        CR9346                                  REG_9346CR
+#define        MSR                                     (REG_CR + 2)
+#define        ISR                                     REG_HISR
+#define        TSFR                                    REG_TSFTR
+
+#define        MACIDR0                                 REG_MACID
+#define        MACIDR4                                 (REG_MACID + 4)
+
+#define PBP                                    REG_PBP
+
+#define        IDR0                                    MACIDR0
+#define        IDR4                                    MACIDR4
+
+#define        UNUSED_REGISTER                         0x1BF
+#define        DCAM                                    UNUSED_REGISTER
+#define        PSR                                     UNUSED_REGISTER
+#define BBADDR                                 UNUSED_REGISTER
+#define        PHYDATAR                                UNUSED_REGISTER
+
+#define        INVALID_BBRF_VALUE                      0x12345678
+
+#define        MAX_MSS_DENSITY_2T                      0x13
+#define        MAX_MSS_DENSITY_1T                      0x0A
+
+#define        CMDEEPROM_EN                            BIT(5)
+#define        CMDEEPROM_SEL                           BIT(4)
+#define        CMD9346CR_9356SEL                       BIT(4)
+#define        AUTOLOAD_EEPROM                         (CMDEEPROM_EN|CMDEEPROM_SEL)
+#define        AUTOLOAD_EFUSE                          CMDEEPROM_EN
+
+#define        GPIOSEL_GPIO                            0
+#define        GPIOSEL_ENBT                            BIT(5)
+
+#define        GPIO_IN                                 REG_GPIO_PIN_CTRL
+#define        GPIO_OUT                                (REG_GPIO_PIN_CTRL+1)
+#define        GPIO_IO_SEL                             (REG_GPIO_PIN_CTRL+2)
+#define        GPIO_MOD                                (REG_GPIO_PIN_CTRL+3)
+
+/* 8723/8188E Host System Interrupt Mask Register (offset 0x58, 32 byte) */
+#define        HSIMR_GPIO12_0_INT_EN                   BIT(0)
+#define        HSIMR_SPS_OCP_INT_EN                    BIT(5)
+#define        HSIMR_RON_INT_EN                        BIT(6)
+#define        HSIMR_PDN_INT_EN                        BIT(7)
+#define        HSIMR_GPIO9_INT_EN                      BIT(25)
+
+
+/* 8723/8188E Host System Interrupt Status Register (offset 0x5C, 32 byte) */
+#define        HSISR_GPIO12_0_INT                      BIT(0)
+#define        HSISR_SPS_OCP_INT                       BIT(5)
+#define        HSISR_RON_INT_EN                        BIT(6)
+#define        HSISR_PDNINT                            BIT(7)
+#define        HSISR_GPIO9_INT                         BIT(25)
+
+#define        MSR_NOLINK                              0x00
+#define        MSR_ADHOC                               0x01
+#define        MSR_INFRA                               0x02
+#define        MSR_AP                                  0x03
+
+#define        RRSR_RSC_OFFSET                         21
+#define        RRSR_SHORT_OFFSET                       23
+#define        RRSR_RSC_BW_40M                         0x600000
+#define        RRSR_RSC_UPSUBCHNL                      0x400000
+#define        RRSR_RSC_LOWSUBCHNL                     0x200000
+#define        RRSR_SHORT                              0x800000
+#define        RRSR_1M                                 BIT(0)
+#define        RRSR_2M                                 BIT(1)
+#define        RRSR_5_5M                               BIT(2)
+#define        RRSR_11M                                BIT(3)
+#define        RRSR_6M                                 BIT(4)
+#define        RRSR_9M                                 BIT(5)
+#define        RRSR_12M                                BIT(6)
+#define        RRSR_18M                                BIT(7)
+#define        RRSR_24M                                BIT(8)
+#define        RRSR_36M                                BIT(9)
+#define        RRSR_48M                                BIT(10)
+#define        RRSR_54M                                BIT(11)
+#define        RRSR_MCS0                               BIT(12)
+#define        RRSR_MCS1                               BIT(13)
+#define        RRSR_MCS2                               BIT(14)
+#define        RRSR_MCS3                               BIT(15)
+#define        RRSR_MCS4                               BIT(16)
+#define        RRSR_MCS5                               BIT(17)
+#define        RRSR_MCS6                               BIT(18)
+#define        RRSR_MCS7                               BIT(19)
+#define        BRSR_ACKSHORTPMB                        BIT(23)
+
+#define        RATR_1M                                 0x00000001
+#define        RATR_2M                                 0x00000002
+#define        RATR_55M                                0x00000004
+#define        RATR_11M                                0x00000008
+#define        RATR_6M                                 0x00000010
+#define        RATR_9M                                 0x00000020
+#define        RATR_12M                                0x00000040
+#define        RATR_18M                                0x00000080
+#define        RATR_24M                                0x00000100
+#define        RATR_36M                                0x00000200
+#define        RATR_48M                                0x00000400
+#define        RATR_54M                                0x00000800
+#define        RATR_MCS0                               0x00001000
+#define        RATR_MCS1                               0x00002000
+#define        RATR_MCS2                               0x00004000
+#define        RATR_MCS3                               0x00008000
+#define        RATR_MCS4                               0x00010000
+#define        RATR_MCS5                               0x00020000
+#define        RATR_MCS6                               0x00040000
+#define        RATR_MCS7                               0x00080000
+#define        RATR_MCS8                               0x00100000
+#define        RATR_MCS9                               0x00200000
+#define        RATR_MCS10                              0x00400000
+#define        RATR_MCS11                              0x00800000
+#define        RATR_MCS12                              0x01000000
+#define        RATR_MCS13                              0x02000000
+#define        RATR_MCS14                              0x04000000
+#define        RATR_MCS15                              0x08000000
+
+#define RATE_1M                                        BIT(0)
+#define RATE_2M                                        BIT(1)
+#define RATE_5_5M                              BIT(2)
+#define RATE_11M                               BIT(3)
+#define RATE_6M                                        BIT(4)
+#define RATE_9M                                        BIT(5)
+#define RATE_12M                               BIT(6)
+#define RATE_18M                               BIT(7)
+#define RATE_24M                               BIT(8)
+#define RATE_36M                               BIT(9)
+#define RATE_48M                               BIT(10)
+#define RATE_54M                               BIT(11)
+#define RATE_MCS0                              BIT(12)
+#define RATE_MCS1                              BIT(13)
+#define RATE_MCS2                              BIT(14)
+#define RATE_MCS3                              BIT(15)
+#define RATE_MCS4                              BIT(16)
+#define RATE_MCS5                              BIT(17)
+#define RATE_MCS6                              BIT(18)
+#define RATE_MCS7                              BIT(19)
+#define RATE_MCS8                              BIT(20)
+#define RATE_MCS9                              BIT(21)
+#define RATE_MCS10                             BIT(22)
+#define RATE_MCS11                             BIT(23)
+#define RATE_MCS12                             BIT(24)
+#define RATE_MCS13                             BIT(25)
+#define RATE_MCS14                             BIT(26)
+#define RATE_MCS15                             BIT(27)
+
+#define        RATE_ALL_CCK            (RATR_1M | RATR_2M | RATR_55M | RATR_11M)
+#define        RATE_ALL_OFDM_AG        (RATR_6M | RATR_9M | RATR_12M | RATR_18M | \
+                               RATR_24M | RATR_36M | RATR_48M | RATR_54M)
+#define        RATE_ALL_OFDM_1SS       (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 | \
+                               RATR_MCS3 | RATR_MCS4 | RATR_MCS5 | \
+                               RATR_MCS6 | RATR_MCS7)
+#define        RATE_ALL_OFDM_2SS       (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 | \
+                               RATR_MCS11 | RATR_MCS12 | RATR_MCS13 | \
+                               RATR_MCS14 | RATR_MCS15)
+
+#define        BW_OPMODE_20MHZ                         BIT(2)
+#define        BW_OPMODE_5G                            BIT(1)
+#define        BW_OPMODE_11J                           BIT(0)
+
+#define        CAM_VALID                               BIT(15)
+#define        CAM_NOTVALID                            0x0000
+#define        CAM_USEDK                               BIT(5)
+
+#define        CAM_NONE                                0x0
+#define        CAM_WEP40                               0x01
+#define        CAM_TKIP                                0x02
+#define        CAM_AES                                 0x04
+#define        CAM_WEP104                              0x05
+
+#define        TOTAL_CAM_ENTRY                         32
+#define        HALF_CAM_ENTRY                          16
+
+#define        CAM_WRITE                               BIT(16)
+#define        CAM_READ                                0x00000000
+#define        CAM_POLLINIG                            BIT(31)
+
+#define        SCR_USEDK                               0x01
+#define        SCR_TXSEC_ENABLE                        0x02
+#define        SCR_RXSEC_ENABLE                        0x04
+
+#define        WOW_PMEN                                BIT(0)
+#define        WOW_WOMEN                               BIT(1)
+#define        WOW_MAGIC                               BIT(2)
+#define        WOW_UWF                                 BIT(3)
+
+/*********************************************
+*       8188 IMR/ISR bits
+**********************************************/
+#define        IMR_DISABLED                            0x0
+/* IMR DW0(0x0060-0063) Bit 0-31 */
+#define        IMR_TXCCK               BIT(30) /* TXRPT interrupt when CCX bit of
+                                        * the packet is set
+                                        */
+#define        IMR_PSTIMEOUT           BIT(29) /* Power Save Time Out Interrupt */
+#define        IMR_GTINT4              BIT(28) /* When GTIMER4 expires,
+                                        * this bit is set to 1
+                                        */
+#define        IMR_GTINT3              BIT(27) /* When GTIMER3 expires,
+                                        * this bit is set to 1
+                                        */
+#define        IMR_TBDER               BIT(26) /* Transmit Beacon0 Error */
+#define        IMR_TBDOK               BIT(25) /* Transmit Beacon0 OK  */
+#define        IMR_TSF_BIT32_TOGGLE    BIT(24) /* TSF Timer BIT32 toggle ind int */
+#define        IMR_BCNDMAINT0          BIT(20) /* Beacon DMA Interrupt 0 */
+#define        IMR_BCNDOK0             BIT(16) /* Beacon Queue DMA OK0 */
+#define        IMR_HSISR_IND_ON_INT    BIT(15) /* HSISR Indicator (HSIMR & HSISR is
+                                        * true, this bit is set to 1)
+                                        */
+#define        IMR_BCNDMAINT_E         BIT(14) /* Beacon DMA Int Extension for Win7 */
+#define        IMR_ATIMEND             BIT(12) /* CTWidnow End or ATIM Window End */
+#define        IMR_HISR1_IND_INT       BIT(11) /* HISR1 Indicator (HISR1 & HIMR1 is
+                                        * true, this bit is set to 1)
+                                        */
+#define        IMR_C2HCMD              BIT(10) /* CPU to Host Command INT Status,
+                                        * Write 1 clear
+                                        */
+#define        IMR_CPWM2               BIT(9)  /* CPU power Mode exchange INT Status,
+                                        * Write 1 clear
+                                        */
+#define        IMR_CPWM                BIT(8)  /* CPU power Mode exchange INT Status,
+                                        * Write 1 clear
+                                        */
+#define        IMR_HIGHDOK             BIT(7)  /* High Queue DMA OK    */
+#define        IMR_MGNTDOK             BIT(6)  /* Management Queue DMA OK */
+#define        IMR_BKDOK               BIT(5)  /* AC_BK DMA OK         */
+#define        IMR_BEDOK               BIT(4)  /* AC_BE DMA OK */
+#define        IMR_VIDOK               BIT(3)  /* AC_VI DMA OK */
+#define        IMR_VODOK               BIT(2)  /* AC_VO DMA OK */
+#define        IMR_RDU                 BIT(1)  /* Rx Descriptor Unavailable */
+#define        IMR_ROK                 BIT(0)  /* Receive DMA OK */
+
+/* IMR DW1(0x00B4-00B7) Bit 0-31 */
+#define        IMR_BCNDMAINT7          BIT(27) /* Beacon DMA Interrupt 7 */
+#define        IMR_BCNDMAINT6          BIT(26) /* Beacon DMA Interrupt 6 */
+#define        IMR_BCNDMAINT5          BIT(25) /* Beacon DMA Interrupt 5 */
+#define        IMR_BCNDMAINT4          BIT(24) /* Beacon DMA Interrupt 4 */
+#define        IMR_BCNDMAINT3          BIT(23) /* Beacon DMA Interrupt 3 */
+#define        IMR_BCNDMAINT2          BIT(22) /* Beacon DMA Interrupt 2 */
+#define        IMR_BCNDMAINT1          BIT(21) /* Beacon DMA Interrupt 1 */
+#define        IMR_BCNDOK7             BIT(20) /* Beacon Queue DMA OK Interrup 7 */
+#define        IMR_BCNDOK6             BIT(19) /* Beacon Queue DMA OK Interrup 6 */
+#define        IMR_BCNDOK5             BIT(18) /* Beacon Queue DMA OK Interrup 5 */
+#define        IMR_BCNDOK4             BIT(17) /* Beacon Queue DMA OK Interrup 4 */
+#define        IMR_BCNDOK3             BIT(16) /* Beacon Queue DMA OK Interrup 3 */
+#define        IMR_BCNDOK2             BIT(15) /* Beacon Queue DMA OK Interrup 2 */
+#define        IMR_BCNDOK1             BIT(14) /* Beacon Queue DMA OK Interrup 1 */
+#define        IMR_ATIMEND_E           BIT(13) /* ATIM Window End Extension for Win7 */
+#define        IMR_TXERR               BIT(11) /* Tx Err Flag Int Status,
+                                        * write 1 clear.
+                                        */
+#define        IMR_RXERR               BIT(10) /* Rx Err Flag INT Status,
+                                        * Write 1 clear
+                                        */
+#define        IMR_TXFOVW              BIT(9)  /* Transmit FIFO Overflow */
+#define        IMR_RXFOVW              BIT(8)  /* Receive FIFO Overflow */
+
+
+#define        HWSET_MAX_SIZE                          512
+#define   EFUSE_MAX_SECTION                    64
+#define   EFUSE_REAL_CONTENT_LEN               256
+#define        EFUSE_OOB_PROTECT_BYTES                 18 /* PG data exclude header,
+                                                   * dummy 7 bytes frome CP
+                                                   * test and reserved 1byte.
+                                                   */
+
+#define        EEPROM_DEFAULT_TSSI                     0x0
+#define EEPROM_DEFAULT_TXPOWERDIFF             0x0
+#define EEPROM_DEFAULT_CRYSTALCAP              0x5
+#define EEPROM_DEFAULT_BOARDTYPE               0x02
+#define EEPROM_DEFAULT_TXPOWER                 0x1010
+#define        EEPROM_DEFAULT_HT2T_TXPWR               0x10
+
+#define        EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF      0x3
+#define        EEPROM_DEFAULT_THERMALMETER             0x18
+#define        EEPROM_DEFAULT_ANTTXPOWERDIFF           0x0
+#define        EEPROM_DEFAULT_TXPWDIFF_CRYSTALCAP      0x5
+#define        EEPROM_DEFAULT_TXPOWERLEVEL             0x22
+#define        EEPROM_DEFAULT_HT40_2SDIFF              0x0
+#define EEPROM_DEFAULT_HT20_DIFF               2
+#define        EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF      0x3
+#define EEPROM_DEFAULT_HT40_PWRMAXOFFSET       0
+#define EEPROM_DEFAULT_HT20_PWRMAXOFFSET       0
+
+#define RF_OPTION1                             0x79
+#define RF_OPTION2                             0x7A
+#define RF_OPTION3                             0x7B
+#define RF_OPTION4                             0x7C
+
+#define EEPROM_DEFAULT_PID                     0x1234
+#define EEPROM_DEFAULT_VID                     0x5678
+#define EEPROM_DEFAULT_CUSTOMERID              0xAB
+#define EEPROM_DEFAULT_SUBCUSTOMERID           0xCD
+#define EEPROM_DEFAULT_VERSION                 0
+
+#define        EEPROM_CHANNEL_PLAN_FCC                 0x0
+#define        EEPROM_CHANNEL_PLAN_IC                  0x1
+#define        EEPROM_CHANNEL_PLAN_ETSI                0x2
+#define        EEPROM_CHANNEL_PLAN_SPAIN               0x3
+#define        EEPROM_CHANNEL_PLAN_FRANCE              0x4
+#define        EEPROM_CHANNEL_PLAN_MKK                 0x5
+#define        EEPROM_CHANNEL_PLAN_MKK1                0x6
+#define        EEPROM_CHANNEL_PLAN_ISRAEL              0x7
+#define        EEPROM_CHANNEL_PLAN_TELEC               0x8
+#define        EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN       0x9
+#define        EEPROM_CHANNEL_PLAN_WORLD_WIDE_13       0xA
+#define        EEPROM_CHANNEL_PLAN_NCC                 0xB
+#define        EEPROM_CHANNEL_PLAN_BY_HW_MASK          0x80
+
+#define EEPROM_CID_DEFAULT                     0x0
+#define EEPROM_CID_TOSHIBA                     0x4
+#define        EEPROM_CID_CCX                          0x10
+#define        EEPROM_CID_QMI                          0x0D
+#define EEPROM_CID_WHQL                                0xFE
+
+#define        RTL8188E_EEPROM_ID                      0x8129
+
+#define EEPROM_HPON                            0x02
+#define EEPROM_CLK                             0x06
+#define EEPROM_TESTR                           0x08
+
+#define EEPROM_TXPOWERCCK                      0x10
+#define        EEPROM_TXPOWERHT40_1S                   0x16
+#define EEPROM_TXPOWERHT20DIFF                 0x1B
+#define EEPROM_TXPOWER_OFDMDIFF                        0x1B
+
+#define        EEPROM_TX_PWR_INX                       0x10
+
+#define        EEPROM_CHANNELPLAN                      0xB8
+#define        EEPROM_XTAL_88E                         0xB9
+#define        EEPROM_THERMAL_METER_88E                0xBA
+#define        EEPROM_IQK_LCK_88E                      0xBB
+
+#define        EEPROM_RF_BOARD_OPTION_88E              0xC1
+#define        EEPROM_RF_FEATURE_OPTION_88E            0xC2
+#define        EEPROM_RF_BT_SETTING_88E                0xC3
+#define        EEPROM_VERSION                          0xC4
+#define        EEPROM_CUSTOMER_ID                      0xC5
+#define        EEPROM_RF_ANTENNA_OPT_88E               0xC9
+
+#define        EEPROM_MAC_ADDR                         0xD0
+#define EEPROM_VID                             0xD6
+#define EEPROM_DID                             0xD8
+#define EEPROM_SVID                            0xDA
+#define EEPROM_SMID                            0xDC
+
+#define        STOPBECON                               BIT(6)
+#define        STOPHIGHT                               BIT(5)
+#define        STOPMGT                                 BIT(4)
+#define        STOPVO                                  BIT(3)
+#define        STOPVI                                  BIT(2)
+#define        STOPBE                                  BIT(1)
+#define        STOPBK                                  BIT(0)
+
+#define        RCR_APPFCS                              BIT(31)
+#define        RCR_APP_MIC                             BIT(30)
+#define        RCR_APP_ICV                             BIT(29)
+#define        RCR_APP_PHYST_RXFF                      BIT(28)
+#define        RCR_APP_BA_SSN                          BIT(27)
+#define        RCR_ENMBID                              BIT(24)
+#define        RCR_LSIGEN                              BIT(23)
+#define        RCR_MFBEN                               BIT(22)
+#define        RCR_HTC_LOC_CTRL                        BIT(14)
+#define        RCR_AMF                                 BIT(13)
+#define        RCR_ACF                                 BIT(12)
+#define        RCR_ADF                                 BIT(11)
+#define        RCR_AICV                                BIT(9)
+#define        RCR_ACRC32                              BIT(8)
+#define        RCR_CBSSID_BCN                          BIT(7)
+#define        RCR_CBSSID_DATA                         BIT(6)
+#define        RCR_CBSSID                              RCR_CBSSID_DATA
+#define        RCR_APWRMGT                             BIT(5)
+#define        RCR_ADD3                                BIT(4)
+#define        RCR_AB                                  BIT(3)
+#define        RCR_AM                                  BIT(2)
+#define        RCR_APM                                 BIT(1)
+#define        RCR_AAP                                 BIT(0)
+#define        RCR_MXDMA_OFFSET                        8
+#define        RCR_FIFO_OFFSET                         13
+
+#define RSV_CTRL                               0x001C
+#define RD_CTRL                                        0x0524
+
+#define REG_USB_INFO                           0xFE17
+#define REG_USB_SPECIAL_OPTION                 0xFE55
+#define REG_USB_DMA_AGG_TO                     0xFE5B
+#define REG_USB_AGG_TO                         0xFE5C
+#define REG_USB_AGG_TH                         0xFE5D
+
+#define REG_USB_VID                            0xFE60
+#define REG_USB_PID                            0xFE62
+#define REG_USB_OPTIONAL                       0xFE64
+#define REG_USB_CHIRP_K                                0xFE65
+#define REG_USB_PHY                            0xFE66
+#define REG_USB_MAC_ADDR                       0xFE70
+#define REG_USB_HRPWM                          0xFE58
+#define REG_USB_HCPWM                          0xFE57
+
+#define SW18_FPWM                              BIT(3)
+
+#define ISO_MD2PP                              BIT(0)
+#define ISO_UA2USB                             BIT(1)
+#define ISO_UD2CORE                            BIT(2)
+#define ISO_PA2PCIE                            BIT(3)
+#define ISO_PD2CORE                            BIT(4)
+#define ISO_IP2MAC                             BIT(5)
+#define ISO_DIOP                               BIT(6)
+#define ISO_DIOE                               BIT(7)
+#define ISO_EB2CORE                            BIT(8)
+#define ISO_DIOR                               BIT(9)
+
+#define PWC_EV25V                              BIT(14)
+#define PWC_EV12V                              BIT(15)
+
+#define FEN_BBRSTB                             BIT(0)
+#define FEN_BB_GLB_RSTN                                BIT(1)
+#define FEN_USBA                               BIT(2)
+#define FEN_UPLL                               BIT(3)
+#define FEN_USBD                               BIT(4)
+#define FEN_DIO_PCIE                           BIT(5)
+#define FEN_PCIEA                              BIT(6)
+#define FEN_PPLL                               BIT(7)
+#define FEN_PCIED                              BIT(8)
+#define FEN_DIOE                               BIT(9)
+#define FEN_CPUEN                              BIT(10)
+#define FEN_DCORE                              BIT(11)
+#define FEN_ELDR                               BIT(12)
+#define FEN_DIO_RF                             BIT(13)
+#define FEN_HWPDN                              BIT(14)
+#define FEN_MREGEN                             BIT(15)
+
+#define PFM_LDALL                              BIT(0)
+#define PFM_ALDN                               BIT(1)
+#define PFM_LDKP                               BIT(2)
+#define PFM_WOWL                               BIT(3)
+#define ENPDN                                  BIT(4)
+#define PDN_PL                                 BIT(5)
+#define APFM_ONMAC                             BIT(8)
+#define APFM_OFF                               BIT(9)
+#define APFM_RSM                               BIT(10)
+#define AFSM_HSUS                              BIT(11)
+#define AFSM_PCIE                              BIT(12)
+#define APDM_MAC                               BIT(13)
+#define APDM_HOST                              BIT(14)
+#define APDM_HPDN                              BIT(15)
+#define RDY_MACON                              BIT(16)
+#define SUS_HOST                               BIT(17)
+#define ROP_ALD                                        BIT(20)
+#define ROP_PWR                                        BIT(21)
+#define ROP_SPS                                        BIT(22)
+#define SOP_MRST                               BIT(25)
+#define SOP_FUSE                               BIT(26)
+#define SOP_ABG                                        BIT(27)
+#define SOP_AMB                                        BIT(28)
+#define SOP_RCK                                        BIT(29)
+#define SOP_A8M                                        BIT(30)
+#define XOP_BTCK                               BIT(31)
+
+#define ANAD16V_EN                             BIT(0)
+#define ANA8M                                  BIT(1)
+#define MACSLP                                 BIT(4)
+#define LOADER_CLK_EN                          BIT(5)
+#define _80M_SSC_DIS                           BIT(7)
+#define _80M_SSC_EN_HO                         BIT(8)
+#define PHY_SSC_RSTB                           BIT(9)
+#define SEC_CLK_EN                             BIT(10)
+#define MAC_CLK_EN                             BIT(11)
+#define SYS_CLK_EN                             BIT(12)
+#define RING_CLK_EN                            BIT(13)
+
+#define        BOOT_FROM_EEPROM                        BIT(4)
+#define        EEPROM_EN                               BIT(5)
+
+#define AFE_BGEN                               BIT(0)
+#define AFE_MBEN                               BIT(1)
+#define MAC_ID_EN                              BIT(7)
+
+#define WLOCK_ALL                              BIT(0)
+#define WLOCK_00                               BIT(1)
+#define WLOCK_04                               BIT(2)
+#define WLOCK_08                               BIT(3)
+#define WLOCK_40                               BIT(4)
+#define R_DIS_PRST_0                           BIT(5)
+#define R_DIS_PRST_1                           BIT(6)
+#define LOCK_ALL_EN                            BIT(7)
+
+#define RF_EN                                  BIT(0)
+#define RF_RSTB                                        BIT(1)
+#define RF_SDMRSTB                             BIT(2)
+
+#define LDA15_EN                               BIT(0)
+#define LDA15_STBY                             BIT(1)
+#define LDA15_OBUF                             BIT(2)
+#define LDA15_REG_VOS                          BIT(3)
+#define _LDA15_VOADJ(x)                                (((x) & 0x7) << 4)
+
+#define LDV12_EN                               BIT(0)
+#define LDV12_SDBY                             BIT(1)
+#define LPLDO_HSM                              BIT(2)
+#define LPLDO_LSM_DIS                          BIT(3)
+#define _LDV12_VADJ(x)                         (((x) & 0xF) << 4)
+
+#define XTAL_EN                                        BIT(0)
+#define XTAL_BSEL                              BIT(1)
+#define _XTAL_BOSC(x)                          (((x) & 0x3) << 2)
+#define _XTAL_CADJ(x)                          (((x) & 0xF) << 4)
+#define XTAL_GATE_USB                          BIT(8)
+#define _XTAL_USB_DRV(x)                       (((x) & 0x3) << 9)
+#define XTAL_GATE_AFE                          BIT(11)
+#define _XTAL_AFE_DRV(x)                       (((x) & 0x3) << 12)
+#define XTAL_RF_GATE                           BIT(14)
+#define _XTAL_RF_DRV(x)                                (((x) & 0x3) << 15)
+#define XTAL_GATE_DIG                          BIT(17)
+#define _XTAL_DIG_DRV(x)                       (((x) & 0x3) << 18)
+#define XTAL_BT_GATE                           BIT(20)
+#define _XTAL_BT_DRV(x)                                (((x) & 0x3) << 21)
+#define _XTAL_GPIO(x)                          (((x) & 0x7) << 23)
+
+#define CKDLY_AFE                              BIT(26)
+#define CKDLY_USB                              BIT(27)
+#define CKDLY_DIG                              BIT(28)
+#define CKDLY_BT                               BIT(29)
+
+#define APLL_EN                                        BIT(0)
+#define APLL_320_EN                            BIT(1)
+#define APLL_FREF_SEL                          BIT(2)
+#define APLL_EDGE_SEL                          BIT(3)
+#define APLL_WDOGB                             BIT(4)
+#define APLL_LPFEN                             BIT(5)
+
+#define APLL_REF_CLK_13MHZ                     0x1
+#define APLL_REF_CLK_19_2MHZ                   0x2
+#define APLL_REF_CLK_20MHZ                     0x3
+#define APLL_REF_CLK_25MHZ                     0x4
+#define APLL_REF_CLK_26MHZ                     0x5
+#define APLL_REF_CLK_38_4MHZ                   0x6
+#define APLL_REF_CLK_40MHZ                     0x7
+
+#define APLL_320EN                             BIT(14)
+#define APLL_80EN                              BIT(15)
+#define APLL_1MEN                              BIT(24)
+
+#define ALD_EN                                 BIT(18)
+#define EF_PD                                  BIT(19)
+#define EF_FLAG                                        BIT(31)
+
+#define EF_TRPT                                        BIT(7)
+#define LDOE25_EN                              BIT(31)
+
+#define RSM_EN                                 BIT(0)
+#define TIMER_EN                               BIT(4)
+
+#define TRSW0EN                                        BIT(2)
+#define TRSW1EN                                        BIT(3)
+#define EROM_EN                                        BIT(4)
+#define ENBT                                   BIT(5)
+#define ENUART                                 BIT(8)
+#define UART_910                               BIT(9)
+#define ENPMAC                                 BIT(10)
+#define SIC_SWRST                              BIT(11)
+#define ENSIC                                  BIT(12)
+#define SIC_23                                 BIT(13)
+#define ENHDP                                  BIT(14)
+#define SIC_LBK                                        BIT(15)
+
+#define LED0PL                                 BIT(4)
+#define LED1PL                                 BIT(12)
+#define LED0DIS                                        BIT(7)
+
+#define MCUFWDL_EN                             BIT(0)
+#define MCUFWDL_RDY                            BIT(1)
+#define FWDL_CHKSUM_RPT                                BIT(2)
+#define MACINI_RDY                             BIT(3)
+#define BBINI_RDY                              BIT(4)
+#define RFINI_RDY                              BIT(5)
+#define WINTINI_RDY                            BIT(6)
+#define CPRST                                  BIT(23)
+
+#define XCLK_VLD                               BIT(0)
+#define ACLK_VLD                               BIT(1)
+#define UCLK_VLD                               BIT(2)
+#define PCLK_VLD                               BIT(3)
+#define PCIRSTB                                        BIT(4)
+#define V15_VLD                                        BIT(5)
+#define TRP_B15V_EN                            BIT(7)
+#define SIC_IDLE                               BIT(8)
+#define BD_MAC2                                        BIT(9)
+#define BD_MAC1                                        BIT(10)
+#define IC_MACPHY_MODE                         BIT(11)
+#define VENDOR_ID                              BIT(19)
+#define PAD_HWPD_IDN                           BIT(22)
+#define TRP_VAUX_EN                            BIT(23)
+#define TRP_BT_EN                              BIT(24)
+#define BD_PKG_SEL                             BIT(25)
+#define BD_HCI_SEL                             BIT(26)
+#define TYPE_ID                                        BIT(27)
+
+#define CHIP_VER_RTL_MASK                      0xF000
+#define CHIP_VER_RTL_SHIFT                     12
+
+#define REG_LBMODE                             (REG_CR + 3)
+
+#define HCI_TXDMA_EN                           BIT(0)
+#define HCI_RXDMA_EN                           BIT(1)
+#define TXDMA_EN                               BIT(2)
+#define RXDMA_EN                               BIT(3)
+#define PROTOCOL_EN                            BIT(4)
+#define SCHEDULE_EN                            BIT(5)
+#define MACTXEN                                        BIT(6)
+#define MACRXEN                                        BIT(7)
+#define ENSWBCN                                        BIT(8)
+#define ENSEC                                  BIT(9)
+
+#define _NETTYPE(x)                            (((x) & 0x3) << 16)
+#define MASK_NETTYPE                           0x30000
+#define NT_NO_LINK                             0x0
+#define NT_LINK_AD_HOC                         0x1
+#define NT_LINK_AP                             0x2
+#define NT_AS_AP                               0x3
+
+#define _LBMODE(x)                             (((x) & 0xF) << 24)
+#define MASK_LBMODE                            0xF000000
+#define LOOPBACK_NORMAL                                0x0
+#define LOOPBACK_IMMEDIATELY                   0xB
+#define LOOPBACK_MAC_DELAY                     0x3
+#define LOOPBACK_PHY                           0x1
+#define LOOPBACK_DMA                           0x7
+
+#define GET_RX_PAGE_SIZE(value)                ((value) & 0xF)
+#define GET_TX_PAGE_SIZE(value)                (((value) & 0xF0) >> 4)
+#define _PSRX_MASK                             0xF
+#define _PSTX_MASK                             0xF0
+#define _PSRX(x)                               (x)
+#define _PSTX(x)                               ((x) << 4)
+
+#define PBP_64                                 0x0
+#define PBP_128                                        0x1
+#define PBP_256                                        0x2
+#define PBP_512                                        0x3
+#define PBP_1024                               0x4
+
+#define RXDMA_ARBBW_EN                         BIT(0)
+#define RXSHFT_EN                              BIT(1)
+#define RXDMA_AGG_EN                           BIT(2)
+#define QS_VO_QUEUE                            BIT(8)
+#define QS_VI_QUEUE                            BIT(9)
+#define QS_BE_QUEUE                            BIT(10)
+#define QS_BK_QUEUE                            BIT(11)
+#define QS_MANAGER_QUEUE                       BIT(12)
+#define QS_HIGH_QUEUE                          BIT(13)
+
+#define HQSEL_VOQ                              BIT(0)
+#define HQSEL_VIQ                              BIT(1)
+#define HQSEL_BEQ                              BIT(2)
+#define HQSEL_BKQ                              BIT(3)
+#define HQSEL_MGTQ                             BIT(4)
+#define HQSEL_HIQ                              BIT(5)
+
+#define _TXDMA_HIQ_MAP(x)                      (((x)&0x3) << 14)
+#define _TXDMA_MGQ_MAP(x)                      (((x)&0x3) << 12)
+#define _TXDMA_BKQ_MAP(x)                      (((x)&0x3) << 10)
+#define _TXDMA_BEQ_MAP(x)                      (((x)&0x3) << 8)
+#define _TXDMA_VIQ_MAP(x)                      (((x)&0x3) << 6)
+#define _TXDMA_VOQ_MAP(x)                      (((x)&0x3) << 4)
+
+#define QUEUE_LOW                              1
+#define QUEUE_NORMAL                           2
+#define QUEUE_HIGH                             3
+
+#define _LLT_NO_ACTIVE                         0x0
+#define _LLT_WRITE_ACCESS                      0x1
+#define _LLT_READ_ACCESS                       0x2
+
+#define _LLT_INIT_DATA(x)                      ((x) & 0xFF)
+#define _LLT_INIT_ADDR(x)                      (((x) & 0xFF) << 8)
+#define _LLT_OP(x)                             (((x) & 0x3) << 30)
+#define _LLT_OP_VALUE(x)                       (((x) >> 30) & 0x3)
+
+#define BB_WRITE_READ_MASK                     (BIT(31) | BIT(30))
+#define BB_WRITE_EN                            BIT(30)
+#define BB_READ_EN                             BIT(31)
+
+#define _HPQ(x)                                        ((x) & 0xFF)
+#define _LPQ(x)                                        (((x) & 0xFF) << 8)
+#define _PUBQ(x)                               (((x) & 0xFF) << 16)
+#define _NPQ(x)                                        ((x) & 0xFF)
+
+#define HPQ_PUBLIC_DIS                         BIT(24)
+#define LPQ_PUBLIC_DIS                         BIT(25)
+#define LD_RQPN                                        BIT(31)
+
+#define BCN_VALID                              BIT(16)
+#define BCN_HEAD(x)                            (((x) & 0xFF) << 8)
+#define        BCN_HEAD_MASK                           0xFF00
+
+#define BLK_DESC_NUM_SHIFT                     4
+#define BLK_DESC_NUM_MASK                      0xF
+
+#define DROP_DATA_EN                           BIT(9)
+
+#define EN_AMPDU_RTY_NEW                       BIT(7)
+
+#define _INIRTSMCS_SEL(x)                      ((x) & 0x3F)
+
+#define _SPEC_SIFS_CCK(x)                      ((x) & 0xFF)
+#define _SPEC_SIFS_OFDM(x)                     (((x) & 0xFF) << 8)
+
+#define RATE_REG_BITMAP_ALL                    0xFFFFF
+
+#define _RRSC_BITMAP(x)                                ((x) & 0xFFFFF)
+
+#define _RRSR_RSC(x)                           (((x) & 0x3) << 21)
+#define RRSR_RSC_RESERVED                      0x0
+#define RRSR_RSC_UPPER_SUBCHANNEL              0x1
+#define RRSR_RSC_LOWER_SUBCHANNEL              0x2
+#define RRSR_RSC_DUPLICATE_MODE                        0x3
+
+#define USE_SHORT_G1                           BIT(20)
+
+#define _AGGLMT_MCS0(x)                                ((x) & 0xF)
+#define _AGGLMT_MCS1(x)                                (((x) & 0xF) << 4)
+#define _AGGLMT_MCS2(x)                                (((x) & 0xF) << 8)
+#define _AGGLMT_MCS3(x)                                (((x) & 0xF) << 12)
+#define _AGGLMT_MCS4(x)                                (((x) & 0xF) << 16)
+#define _AGGLMT_MCS5(x)                                (((x) & 0xF) << 20)
+#define _AGGLMT_MCS6(x)                                (((x) & 0xF) << 24)
+#define _AGGLMT_MCS7(x)                                (((x) & 0xF) << 28)
+
+#define        RETRY_LIMIT_SHORT_SHIFT                 8
+#define        RETRY_LIMIT_LONG_SHIFT                  0
+
+#define _DARF_RC1(x)                           ((x) & 0x1F)
+#define _DARF_RC2(x)                           (((x) & 0x1F) << 8)
+#define _DARF_RC3(x)                           (((x) & 0x1F) << 16)
+#define _DARF_RC4(x)                           (((x) & 0x1F) << 24)
+#define _DARF_RC5(x)                           ((x) & 0x1F)
+#define _DARF_RC6(x)                           (((x) & 0x1F) << 8)
+#define _DARF_RC7(x)                           (((x) & 0x1F) << 16)
+#define _DARF_RC8(x)                           (((x) & 0x1F) << 24)
+
+#define _RARF_RC1(x)                           ((x) & 0x1F)
+#define _RARF_RC2(x)                           (((x) & 0x1F) << 8)
+#define _RARF_RC3(x)                           (((x) & 0x1F) << 16)
+#define _RARF_RC4(x)                           (((x) & 0x1F) << 24)
+#define _RARF_RC5(x)                           ((x) & 0x1F)
+#define _RARF_RC6(x)                           (((x) & 0x1F) << 8)
+#define _RARF_RC7(x)                           (((x) & 0x1F) << 16)
+#define _RARF_RC8(x)                           (((x) & 0x1F) << 24)
+
+#define AC_PARAM_TXOP_LIMIT_OFFSET             16
+#define AC_PARAM_ECW_MAX_OFFSET                        12
+#define AC_PARAM_ECW_MIN_OFFSET                        8
+#define AC_PARAM_AIFS_OFFSET                   0
+
+#define _AIFS(x)                               (x)
+#define _ECW_MAX_MIN(x)                                ((x) << 8)
+#define _TXOP_LIMIT(x)                         ((x) << 16)
+
+#define _BCNIFS(x)                             ((x) & 0xFF)
+#define _BCNECW(x)                             ((((x) & 0xF)) << 8)
+
+#define _LRL(x)                                        ((x) & 0x3F)
+#define _SRL(x)                                        (((x) & 0x3F) << 8)
+
+#define _SIFS_CCK_CTX(x)                       ((x) & 0xFF)
+#define _SIFS_CCK_TRX(x)                       (((x) & 0xFF) << 8);
+
+#define _SIFS_OFDM_CTX(x)                      ((x) & 0xFF)
+#define _SIFS_OFDM_TRX(x)                      (((x) & 0xFF) << 8);
+
+#define _TBTT_PROHIBIT_HOLD(x)                 (((x) & 0xFF) << 8)
+
+#define DIS_EDCA_CNT_DWN                       BIT(11)
+
+#define EN_MBSSID                              BIT(1)
+#define EN_TXBCN_RPT                           BIT(2)
+#define        EN_BCN_FUNCTION                         BIT(3)
+
+#define TSFTR_RST                              BIT(0)
+#define TSFTR1_RST                             BIT(1)
+
+#define STOP_BCNQ                              BIT(6)
+
+#define        DIS_TSF_UDT0_NORMAL_CHIP                BIT(4)
+#define        DIS_TSF_UDT0_TEST_CHIP                  BIT(5)
+
+#define        ACMHW_HWEN                              BIT(0)
+#define        ACMHW_BEQEN                             BIT(1)
+#define        ACMHW_VIQEN                             BIT(2)
+#define        ACMHW_VOQEN                             BIT(3)
+#define        ACMHW_BEQSTATUS                         BIT(4)
+#define        ACMHW_VIQSTATUS                         BIT(5)
+#define        ACMHW_VOQSTATUS                         BIT(6)
+
+#define APSDOFF                                        BIT(6)
+#define APSDOFF_STATUS                         BIT(7)
+
+#define BW_20MHZ                               BIT(2)
+
+#define RATE_BITMAP_ALL                                0xFFFFF
+
+#define RATE_RRSR_CCK_ONLY_1M                  0xFFFF1
+
+#define TSFRST                                 BIT(0)
+#define DIS_GCLK                               BIT(1)
+#define PAD_SEL                                        BIT(2)
+#define PWR_ST                                 BIT(6)
+#define PWRBIT_OW_EN                           BIT(7)
+#define ACRC                                   BIT(8)
+#define CFENDFORM                              BIT(9)
+#define ICV                                    BIT(10)
+
+#define AAP                                    BIT(0)
+#define APM                                    BIT(1)
+#define AM                                     BIT(2)
+#define AB                                     BIT(3)
+#define ADD3                                   BIT(4)
+#define APWRMGT                                        BIT(5)
+#define CBSSID                                 BIT(6)
+#define CBSSID_DATA                            BIT(6)
+#define CBSSID_BCN                             BIT(7)
+#define ACRC32                                 BIT(8)
+#define AICV                                   BIT(9)
+#define ADF                                    BIT(11)
+#define ACF                                    BIT(12)
+#define AMF                                    BIT(13)
+#define HTC_LOC_CTRL                           BIT(14)
+#define UC_DATA_EN                             BIT(16)
+#define BM_DATA_EN                             BIT(17)
+#define MFBEN                                  BIT(22)
+#define LSIGEN                                 BIT(23)
+#define ENMBID                                 BIT(24)
+#define APP_BASSN                              BIT(27)
+#define APP_PHYSTS                             BIT(28)
+#define APP_ICV                                        BIT(29)
+#define APP_MIC                                        BIT(30)
+#define APP_FCS                                        BIT(31)
+
+#define _MIN_SPACE(x)                          ((x) & 0x7)
+#define _SHORT_GI_PADDING(x)                   (((x) & 0x1F) << 3)
+
+#define RXERR_TYPE_OFDM_PPDU                   0
+#define RXERR_TYPE_OFDM_FALSE_ALARM            1
+#define        RXERR_TYPE_OFDM_MPDU_OK                 2
+#define RXERR_TYPE_OFDM_MPDU_FAIL              3
+#define RXERR_TYPE_CCK_PPDU                    4
+#define RXERR_TYPE_CCK_FALSE_ALARM             5
+#define RXERR_TYPE_CCK_MPDU_OK                 6
+#define RXERR_TYPE_CCK_MPDU_FAIL               7
+#define RXERR_TYPE_HT_PPDU                     8
+#define RXERR_TYPE_HT_FALSE_ALARM              9
+#define RXERR_TYPE_HT_MPDU_TOTAL               10
+#define RXERR_TYPE_HT_MPDU_OK                  11
+#define RXERR_TYPE_HT_MPDU_FAIL                        12
+#define RXERR_TYPE_RX_FULL_DROP                        15
+
+#define RXERR_COUNTER_MASK                     0xFFFFF
+#define RXERR_RPT_RST                          BIT(27)
+#define _RXERR_RPT_SEL(type)                   ((type) << 28)
+
+#define        SCR_TXUSEDK                             BIT(0)
+#define        SCR_RXUSEDK                             BIT(1)
+#define        SCR_TXENCENABLE                         BIT(2)
+#define        SCR_RXDECENABLE                         BIT(3)
+#define        SCR_SKBYA2                              BIT(4)
+#define        SCR_NOSKMC                              BIT(5)
+#define SCR_TXBCUSEDK                          BIT(6)
+#define SCR_RXBCUSEDK                          BIT(7)
+
+#define USB_IS_HIGH_SPEED                      0
+#define USB_IS_FULL_SPEED                      1
+#define USB_SPEED_MASK                         BIT(5)
+
+#define USB_NORMAL_SIE_EP_MASK                 0xF
+#define USB_NORMAL_SIE_EP_SHIFT                        4
+
+#define USB_TEST_EP_MASK                       0x30
+#define USB_TEST_EP_SHIFT                      4
+
+#define USB_AGG_EN                             BIT(3)
+
+#define MAC_ADDR_LEN                           6
+#define LAST_ENTRY_OF_TX_PKT_BUFFER            175/*255    88e*/
+
+#define POLLING_LLT_THRESHOLD                  20
+#define POLLING_READY_TIMEOUT_COUNT            3000
+
+#define        MAX_MSS_DENSITY_2T                      0x13
+#define        MAX_MSS_DENSITY_1T                      0x0A
+
+#define EPROM_CMD_OPERATING_MODE_MASK          ((1<<7)|(1<<6))
+#define EPROM_CMD_CONFIG                       0x3
+#define EPROM_CMD_LOAD                         1
+
+#define        HWSET_MAX_SIZE_92S                      HWSET_MAX_SIZE
+
+#define        HAL_8192C_HW_GPIO_WPS_BIT               BIT(2)
+
+#define        RPMAC_RESET                             0x100
+#define        RPMAC_TXSTART                           0x104
+#define        RPMAC_TXLEGACYSIG                       0x108
+#define        RPMAC_TXHTSIG1                          0x10c
+#define        RPMAC_TXHTSIG2                          0x110
+#define        RPMAC_PHYDEBUG                          0x114
+#define        RPMAC_TXPACKETNUM                       0x118
+#define        RPMAC_TXIDLE                            0x11c
+#define        RPMAC_TXMACHEADER0                      0x120
+#define        RPMAC_TXMACHEADER1                      0x124
+#define        RPMAC_TXMACHEADER2                      0x128
+#define        RPMAC_TXMACHEADER3                      0x12c
+#define        RPMAC_TXMACHEADER4                      0x130
+#define        RPMAC_TXMACHEADER5                      0x134
+#define        RPMAC_TXDADATYPE                        0x138
+#define        RPMAC_TXRANDOMSEED                      0x13c
+#define        RPMAC_CCKPLCPPREAMBLE                   0x140
+#define        RPMAC_CCKPLCPHEADER                     0x144
+#define        RPMAC_CCKCRC16                          0x148
+#define        RPMAC_OFDMRXCRC32OK                     0x170
+#define        RPMAC_OFDMRXCRC32Er                     0x174
+#define        RPMAC_OFDMRXPARITYER                    0x178
+#define        RPMAC_OFDMRXCRC8ER                      0x17c
+#define        RPMAC_CCKCRXRC16ER                      0x180
+#define        RPMAC_CCKCRXRC32ER                      0x184
+#define        RPMAC_CCKCRXRC32OK                      0x188
+#define        RPMAC_TXSTATUS                          0x18c
+
+#define        RFPGA0_RFMOD                            0x800
+
+#define        RFPGA0_TXINFO                           0x804
+#define        RFPGA0_PSDFUNCTION                      0x808
+
+#define        RFPGA0_TXGAINSTAGE                      0x80c
+
+#define        RFPGA0_RFTIMING1                        0x810
+#define        RFPGA0_RFTIMING2                        0x814
+
+#define        RFPGA0_XA_HSSIPARAMETER1                0x820
+#define        RFPGA0_XA_HSSIPARAMETER2                0x824
+#define        RFPGA0_XB_HSSIPARAMETER1                0x828
+#define        RFPGA0_XB_HSSIPARAMETER2                0x82c
+
+#define        RFPGA0_XA_LSSIPARAMETER                 0x840
+#define        RFPGA0_XB_LSSIPARAMETER                 0x844
+
+#define        RFPGA0_RFWAKEUPPARAMETER                0x850
+#define        RFPGA0_RFSLEEPUPPARAMETER               0x854
+
+#define        RFPGA0_XAB_SWITCHCONTROL                0x858
+#define        RFPGA0_XCD_SWITCHCONTROL                0x85c
+
+#define        RFPGA0_XA_RFINTERFACEOE                 0x860
+#define        RFPGA0_XB_RFINTERFACEOE                 0x864
+
+#define        RFPGA0_XAB_RFINTERFACESW                0x870
+#define        RFPGA0_XCD_RFINTERFACESW                0x874
+
+#define        rFPGA0_XAB_RFPARAMETER                  0x878
+#define        rFPGA0_XCD_RFPARAMETER                  0x87c
+
+#define        RFPGA0_ANALOGPARAMETER1                 0x880
+#define        RFPGA0_ANALOGPARAMETER2                 0x884
+#define        RFPGA0_ANALOGPARAMETER3                 0x888
+#define        RFPGA0_ANALOGPARAMETER4                 0x88c
+
+#define        RFPGA0_XA_LSSIREADBACK                  0x8a0
+#define        RFPGA0_XB_LSSIREADBACK                  0x8a4
+#define        RFPGA0_XC_LSSIREADBACK                  0x8a8
+#define        RFPGA0_XD_LSSIREADBACK                  0x8ac
+
+#define        RFPGA0_PSDREPORT                        0x8b4
+#define        TRANSCEIVEA_HSPI_READBACK               0x8b8
+#define        TRANSCEIVEB_HSPI_READBACK               0x8bc
+#define        REG_SC_CNT                              0x8c4
+#define        RFPGA0_XAB_RFINTERFACERB                0x8e0
+#define        RFPGA0_XCD_RFINTERFACERB                0x8e4
+
+#define        RFPGA1_RFMOD                            0x900
+
+#define        RFPGA1_TXBLOCK                          0x904
+#define        RFPGA1_DEBUGSELECT                      0x908
+#define        RFPGA1_TXINFO                           0x90c
+
+#define        RCCK0_SYSTEM                            0xa00
+
+#define        RCCK0_AFESETTING                        0xa04
+#define        RCCK0_CCA                               0xa08
+
+#define        RCCK0_RXAGC1                            0xa0c
+#define        RCCK0_RXAGC2                            0xa10
+
+#define        RCCK0_RXHP                              0xa14
+
+#define        RCCK0_DSPPARAMETER1                     0xa18
+#define        RCCK0_DSPPARAMETER2                     0xa1c
+
+#define        RCCK0_TXFILTER1                         0xa20
+#define        RCCK0_TXFILTER2                         0xa24
+#define        RCCK0_DEBUGPORT                         0xa28
+#define        RCCK0_FALSEALARMREPORT                  0xa2c
+#define        RCCK0_TRSSIREPORT                       0xa50
+#define        RCCK0_RXREPORT                          0xa54
+#define        RCCK0_FACOUNTERLOWER                    0xa5c
+#define        RCCK0_FACOUNTERUPPER                    0xa58
+#define        RCCK0_CCA_CNT                           0xa60
+
+
+/* PageB(0xB00) */
+#define        RPDP_ANTA                               0xb00
+#define        RPDP_ANTA_4                             0xb04
+#define        RPDP_ANTA_8                             0xb08
+#define        RPDP_ANTA_C                             0xb0c
+#define        RPDP_ANTA_10                            0xb10
+#define        RPDP_ANTA_14                            0xb14
+#define        RPDP_ANTA_18                            0xb18
+#define        RPDP_ANTA_1C                            0xb1c
+#define        RPDP_ANTA_20                            0xb20
+#define        RPDP_ANTA_24                            0xb24
+
+#define        RCONFIG_PMPD_ANTA                       0xb28
+#define        RCONFIG_RAM64X16                        0xb2c
+
+#define        RBNDA                                   0xb30
+#define        RHSSIPAR                                0xb34
+
+#define        RCONFIG_ANTA                            0xb68
+#define        RCONFIG_ANTB                            0xb6c
+
+#define        RPDP_ANTB                               0xb70
+#define        RPDP_ANTB_4                             0xb74
+#define        RPDP_ANTB_8                             0xb78
+#define        RPDP_ANTB_C                             0xb7c
+#define        RPDP_ANTB_10                            0xb80
+#define        RPDP_ANTB_14                            0xb84
+#define        RPDP_ANTB_18                            0xb88
+#define        RPDP_ANTB_1C                            0xb8c
+#define        RPDP_ANTB_20                            0xb90
+#define        RPDP_ANTB_24                            0xb94
+
+#define        RCONFIG_PMPD_ANTB                       0xb98
+
+#define        RBNDB                                   0xba0
+
+#define        RAPK                                    0xbd8
+#define        rPm_Rx0_AntA                            0xbdc
+#define        rPm_Rx1_AntA                            0xbe0
+#define        rPm_Rx2_AntA                            0xbe4
+#define        rPm_Rx3_AntA                            0xbe8
+#define        rPm_Rx0_AntB                            0xbec
+#define        rPm_Rx1_AntB                            0xbf0
+#define        rPm_Rx2_AntB                            0xbf4
+#define        rPm_Rx3_AntB                            0xbf8
+
+/*Page C*/
+#define        ROFDM0_LSTF                             0xc00
+
+#define        ROFDM0_TRXPATHENABLE                    0xc04
+#define        ROFDM0_TRMUXPAR                         0xc08
+#define        ROFDM0_TRSWISOLATION                    0xc0c
+
+#define        ROFDM0_XARXAFE                          0xc10
+#define        ROFDM0_XARXIQIMBAL                      0xc14
+#define        ROFDM0_XBRXAFE                          0xc18
+#define        ROFDM0_XBRXIQIMBAL                      0xc1c
+#define        ROFDM0_XCRXAFE                          0xc20
+#define        ROFDM0_XCRXIQIMBAL                      0xc24
+#define        ROFDM0_XDRXAFE                          0xc28
+#define        ROFDM0_XDRXIQIMBAL                      0xc2c
+
+#define        ROFDM0_RXDETECTOR1                      0xc30
+#define        ROFDM0_RXDETECTOR2                      0xc34
+#define        ROFDM0_RXDETECTOR3                      0xc38
+#define        ROFDM0_RXDETECTOR4                      0xc3c
+
+#define        ROFDM0_RXDSP                            0xc40
+#define        ROFDM0_CFOANDDAGC                       0xc44
+#define        ROFDM0_CCADROPTHRES                     0xc48
+#define        ROFDM0_ECCATHRES                        0xc4c
+
+#define        ROFDM0_XAAGCCORE1                       0xc50
+#define        ROFDM0_XAAGCCORE2                       0xc54
+#define        ROFDM0_XBAGCCORE1                       0xc58
+#define        ROFDM0_XBAGCCORE2                       0xc5c
+#define        ROFDM0_XCAGCCORE1                       0xc60
+#define        ROFDM0_XCAGCCORE2                       0xc64
+#define        ROFDM0_XDAGCCORE1                       0xc68
+#define        ROFDM0_XDAGCCORE2                       0xc6c
+
+#define        ROFDM0_AGCPARAMETER1                    0xc70
+#define        ROFDM0_AGCPARAMETER2                    0xc74
+#define        ROFDM0_AGCRSSITABLE                     0xc78
+#define        ROFDM0_HTSTFAGC                         0xc7c
+
+#define        ROFDM0_XATXIQIMBAL                      0xc80
+#define        ROFDM0_XATXAFE                          0xc84
+#define        ROFDM0_XBTXIQIMBAL                      0xc88
+#define        ROFDM0_XBTXAFE                          0xc8c
+#define        ROFDM0_XCTXIQIMBAL                      0xc90
+#define        ROFDM0_XCTXAFE                          0xc94
+#define        ROFDM0_XDTXIQIMBAL                      0xc98
+#define        ROFDM0_XDTXAFE                          0xc9c
+
+#define ROFDM0_RXIQEXTANTA                     0xca0
+#define        ROFDM0_TXCOEFF1                         0xca4
+#define        ROFDM0_TXCOEFF2                         0xca8
+#define        ROFDM0_TXCOEFF3                         0xcac
+#define        ROFDM0_TXCOEFF4                         0xcb0
+#define        ROFDM0_TXCOEFF5                         0xcb4
+#define        ROFDM0_TXCOEFF6                         0xcb8
+
+#define        ROFDM0_RXHPPARAMETER                    0xce0
+#define        ROFDM0_TXPSEUDONOISEWGT                 0xce4
+#define        ROFDM0_FRAMESYNC                        0xcf0
+#define        ROFDM0_DFSREPORT                        0xcf4
+
+
+#define        ROFDM1_LSTF                             0xd00
+#define        ROFDM1_TRXPATHENABLE                    0xd04
+
+#define        ROFDM1_CF0                              0xd08
+#define        ROFDM1_CSI1                             0xd10
+#define        ROFDM1_SBD                              0xd14
+#define        ROFDM1_CSI2                             0xd18
+#define        ROFDM1_CFOTRACKING                      0xd2c
+#define        ROFDM1_TRXMESAURE1                      0xd34
+#define        ROFDM1_INTFDET                          0xd3c
+#define        ROFDM1_PSEUDONOISESTATEAB               0xd50
+#define        ROFDM1_PSEUDONOISESTATECD               0xd54
+#define        ROFDM1_RXPSEUDONOISEWGT                 0xd58
+
+#define        ROFDM_PHYCOUNTER1                       0xda0
+#define        ROFDM_PHYCOUNTER2                       0xda4
+#define        ROFDM_PHYCOUNTER3                       0xda8
+
+#define        ROFDM_SHORTCFOAB                        0xdac
+#define        ROFDM_SHORTCFOCD                        0xdb0
+#define        ROFDM_LONGCFOAB                         0xdb4
+#define        ROFDM_LONGCFOCD                         0xdb8
+#define        ROFDM_TAILCF0AB                         0xdbc
+#define        ROFDM_TAILCF0CD                         0xdc0
+#define        ROFDM_PWMEASURE1                        0xdc4
+#define        ROFDM_PWMEASURE2                        0xdc8
+#define        ROFDM_BWREPORT                          0xdcc
+#define        ROFDM_AGCREPORT                         0xdd0
+#define        ROFDM_RXSNR                             0xdd4
+#define        ROFDM_RXEVMCSI                          0xdd8
+#define        ROFDM_SIGREPORT                         0xddc
+
+#define        RTXAGC_A_RATE18_06                      0xe00
+#define        RTXAGC_A_RATE54_24                      0xe04
+#define        RTXAGC_A_CCK1_MCS32                     0xe08
+#define        RTXAGC_A_MCS03_MCS00                    0xe10
+#define        RTXAGC_A_MCS07_MCS04                    0xe14
+#define        RTXAGC_A_MCS11_MCS08                    0xe18
+#define        RTXAGC_A_MCS15_MCS12                    0xe1c
+
+#define        RTXAGC_B_RATE18_06                      0x830
+#define        RTXAGC_B_RATE54_24                      0x834
+#define        RTXAGC_B_CCK1_55_MCS32                  0x838
+#define        RTXAGC_B_MCS03_MCS00                    0x83c
+#define        RTXAGC_B_MCS07_MCS04                    0x848
+#define        RTXAGC_B_MCS11_MCS08                    0x84c
+#define        RTXAGC_B_MCS15_MCS12                    0x868
+#define        RTXAGC_B_CCK11_A_CCK2_11                0x86c
+
+#define        RFPGA0_IQK                              0xe28
+#define        RTX_IQK_TONE_A                          0xe30
+#define        RRX_IQK_TONE_A                          0xe34
+#define        RTX_IQK_PI_A                            0xe38
+#define        RRX_IQK_PI_A                            0xe3c
+
+#define        RTX_IQK                                 0xe40
+#define        RRX_IQK                                 0xe44
+#define        RIQK_AGC_PTS                            0xe48
+#define        RIQK_AGC_RSP                            0xe4c
+#define        RTX_IQK_TONE_B                          0xe50
+#define        RRX_IQK_TONE_B                          0xe54
+#define        RTX_IQK_PI_B                            0xe58
+#define        RRX_IQK_PI_B                            0xe5c
+#define        RIQK_AGC_CONT                           0xe60
+
+#define        RBLUE_TOOTH                             0xe6c
+#define        RRX_WAIT_CCA                            0xe70
+#define        RTX_CCK_RFON                            0xe74
+#define        RTX_CCK_BBON                            0xe78
+#define        RTX_OFDM_RFON                           0xe7c
+#define        RTX_OFDM_BBON                           0xe80
+#define        RTX_TO_RX                               0xe84
+#define        RTX_TO_TX                               0xe88
+#define        RRX_CCK                                 0xe8c
+
+#define        RTX_POWER_BEFORE_IQK_A                  0xe94
+#define        RTX_POWER_AFTER_IQK_A                   0xe9c
+
+#define        RRX_POWER_BEFORE_IQK_A                  0xea0
+#define        RRX_POWER_BEFORE_IQK_A_2                0xea4
+#define        RRX_POWER_AFTER_IQK_A                   0xea8
+#define        RRX_POWER_AFTER_IQK_A_2                 0xeac
+
+#define        RTX_POWER_BEFORE_IQK_B                  0xeb4
+#define        RTX_POWER_AFTER_IQK_B                   0xebc
+
+#define        RRX_POWER_BEFORE_IQK_B                  0xec0
+#define        RRX_POWER_BEFORE_IQK_B_2                0xec4
+#define        RRX_POWER_AFTER_IQK_B                   0xec8
+#define        RRX_POWER_AFTER_IQK_B_2                 0xecc
+
+#define        RRX_OFDM                                0xed0
+#define        RRX_WAIT_RIFS                           0xed4
+#define        RRX_TO_RX                               0xed8
+#define        RSTANDBY                                0xedc
+#define        RSLEEP                                  0xee0
+#define        RPMPD_ANAEN                             0xeec
+
+#define        RZEBRA1_HSSIENABLE                      0x0
+#define        RZEBRA1_TRXENABLE1                      0x1
+#define        RZEBRA1_TRXENABLE2                      0x2
+#define        RZEBRA1_AGC                             0x4
+#define        RZEBRA1_CHARGEPUMP                      0x5
+#define        RZEBRA1_CHANNEL                         0x7
+
+#define        RZEBRA1_TXGAIN                          0x8
+#define        RZEBRA1_TXLPF                           0x9
+#define        RZEBRA1_RXLPF                           0xb
+#define        RZEBRA1_RXHPFCORNER                     0xc
+
+#define        RGLOBALCTRL                             0
+#define        RRTL8256_TXLPF                          19
+#define        RRTL8256_RXLPF                          11
+#define        RRTL8258_TXLPF                          0x11
+#define        RRTL8258_RXLPF                          0x13
+#define        RRTL8258_RSSILPF                        0xa
+
+#define        RF_AC                                   0x00
+
+#define        RF_IQADJ_G1                             0x01
+#define        RF_IQADJ_G2                             0x02
+#define        RF_POW_TRSW                             0x05
+
+#define        RF_GAIN_RX                              0x06
+#define        RF_GAIN_TX                              0x07
+
+#define        RF_TXM_IDAC                             0x08
+#define        RF_BS_IQGEN                             0x0F
+
+#define        RF_MODE1                                0x10
+#define        RF_MODE2                                0x11
+
+#define        RF_RX_AGC_HP                            0x12
+#define        RF_TX_AGC                               0x13
+#define        RF_BIAS                                 0x14
+#define        RF_IPA                                  0x15
+#define        RF_POW_ABILITY                          0x17
+#define        RF_MODE_AG                              0x18
+#define        RRFCHANNEL                              0x18
+#define        RF_CHNLBW                               0x18
+#define        RF_TOP                                  0x19
+
+#define        RF_RX_G1                                0x1A
+#define        RF_RX_G2                                0x1B
+
+#define        RF_RX_BB2                               0x1C
+#define        RF_RX_BB1                               0x1D
+
+#define        RF_RCK1                                 0x1E
+#define        RF_RCK2                                 0x1F
+
+#define        RF_TX_G1                                0x20
+#define        RF_TX_G2                                0x21
+#define        RF_TX_G3                                0x22
+
+#define        RF_TX_BB1                               0x23
+#define        RF_T_METER                              0x42
+
+#define        RF_SYN_G1                               0x25
+#define        RF_SYN_G2                               0x26
+#define        RF_SYN_G3                               0x27
+#define        RF_SYN_G4                               0x28
+#define        RF_SYN_G5                               0x29
+#define        RF_SYN_G6                               0x2A
+#define        RF_SYN_G7                               0x2B
+#define        RF_SYN_G8                               0x2C
+
+#define        RF_RCK_OS                               0x30
+#define        RF_TXPA_G1                              0x31
+#define        RF_TXPA_G2                              0x32
+#define        RF_TXPA_G3                              0x33
+
+#define        RF_TX_BIAS_A                            0x35
+#define        RF_TX_BIAS_D                            0x36
+#define        RF_LOBF_9                               0x38
+#define        RF_RXRF_A3                              0x3C
+#define        RF_TRSW                                 0x3F
+
+#define        RF_TXRF_A2                              0x41
+#define        RF_TXPA_G4                              0x46
+#define        RF_TXPA_A4                              0x4B
+
+#define        RF_WE_LUT                               0xEF
+
+#define        BBBRESETB                               0x100
+#define        BGLOBALRESETB                           0x200
+#define        BOFDMTXSTART                            0x4
+#define        BCCKTXSTART                             0x8
+#define        BCRC32DEBUG                             0x100
+#define        BPMACLOOPBACK                           0x10
+#define        BTXLSIG                                 0xffffff
+#define        BOFDMTXRATE                             0xf
+#define        BOFDMTXRESERVED                         0x10
+#define        BOFDMTXLENGTH                           0x1ffe0
+#define        BOFDMTXPARITY                           0x20000
+#define        BTXHTSIG1                               0xffffff
+#define        BTXHTMCSRATE                            0x7f
+#define        BTXHTBW                                 0x80
+#define        BTXHTLENGTH                             0xffff00
+#define        BTXHTSIG2                               0xffffff
+#define        BTXHTSMOOTHING                          0x1
+#define        BTXHTSOUNDING                           0x2
+#define        BTXHTRESERVED                           0x4
+#define        BTXHTAGGREATION                         0x8
+#define        BTXHTSTBC                               0x30
+#define        BTXHTADVANCECODING                      0x40
+#define        BTXHTSHORTGI                            0x80
+#define        BTXHTNUMBERHT_LTF                       0x300
+#define        BTXHTCRC8                               0x3fc00
+#define        BCOUNTERRESET                           0x10000
+#define        BNUMOFOFDMTX                            0xffff
+#define        BNUMOFCCKTX                             0xffff0000
+#define        BTXIDLEINTERVAL                         0xffff
+#define        BOFDMSERVICE                            0xffff0000
+#define        BTXMACHEADER                            0xffffffff
+#define        BTXDATAINIT                             0xff
+#define        BTXHTMODE                               0x100
+#define        BTXDATATYPE                             0x30000
+#define        BTXRANDOMSEED                           0xffffffff
+#define        BCCKTXPREAMBLE                          0x1
+#define        BCCKTXSFD                               0xffff0000
+#define        BCCKTXSIG                               0xff
+#define        BCCKTXSERVICE                           0xff00
+#define        BCCKLENGTHEXT                           0x8000
+#define        BCCKTXLENGHT                            0xffff0000
+#define        BCCKTXCRC16                             0xffff
+#define        BCCKTXSTATUS                            0x1
+#define        BOFDMTXSTATUS                           0x2
+#define IS_BB_REG_OFFSET_92S(_offset)  \
+       ((_offset >= 0x800) && (_offset <= 0xfff))
+
+#define        BRFMOD                                  0x1
+#define        BJAPANMODE                              0x2
+#define        BCCKTXSC                                0x30
+#define        BCCKEN                                  0x1000000
+#define        BOFDMEN                                 0x2000000
+
+#define        BOFDMRXADCPHASE                         0x10000
+#define        BOFDMTXDACPHASE                         0x40000
+#define        BXATXAGC                                0x3f
+
+#define        BXBTXAGC                                0xf00
+#define        BXCTXAGC                                0xf000
+#define        BXDTXAGC                                0xf0000
+
+#define        BPASTART                                0xf0000000
+#define        BTRSTART                                0x00f00000
+#define        BRFSTART                                0x0000f000
+#define        BBBSTART                                0x000000f0
+#define        BBBCCKSTART                             0x0000000f
+#define        BPAEND                                  0xf
+#define        BTREND                                  0x0f000000
+#define        BRFEND                                  0x000f0000
+#define        BCCAMASK                                0x000000f0
+#define        BR2RCCAMASK                             0x00000f00
+#define        BHSSI_R2TDELAY                          0xf8000000
+#define        BHSSI_T2RDELAY                          0xf80000
+#define        BCONTXHSSI                              0x400
+#define        BIGFROMCCK                              0x200
+#define        BAGCADDRESS                             0x3f
+#define        BRXHPTX                                 0x7000
+#define        BRXHP2RX                                0x38000
+#define        BRXHPCCKINI                             0xc0000
+#define        BAGCTXCODE                              0xc00000
+#define        BAGCRXCODE                              0x300000
+
+#define        B3WIREDATALENGTH                        0x800
+#define        B3WIREADDREAALENGTH                     0x400
+
+#define        B3WIRERFPOWERDOWN                       0x1
+#define        B5GPAPEPOLARITY                         0x40000000
+#define        B2GPAPEPOLARITY                         0x80000000
+#define        BRFSW_TXDEFAULTANT                      0x3
+#define        BRFSW_TXOPTIONANT                       0x30
+#define        BRFSW_RXDEFAULTANT                      0x300
+#define        BRFSW_RXOPTIONANT                       0x3000
+#define        BRFSI_3WIREDATA                         0x1
+#define        BRFSI_3WIRECLOCK                        0x2
+#define        BRFSI_3WIRELOAD                         0x4
+#define        BRFSI_3WIRERW                           0x8
+#define        BRFSI_3WIRE                             0xf
+
+#define        BRFSI_RFENV                             0x10
+
+#define        BRFSI_TRSW                              0x20
+#define        BRFSI_TRSWB                             0x40
+#define        BRFSI_ANTSW                             0x100
+#define        BRFSI_ANTSWB                            0x200
+#define        BRFSI_PAPE                              0x400
+#define        BRFSI_PAPE5G                            0x800
+#define        BBANDSELECT                             0x1
+#define        BHTSIG2_GI                              0x80
+#define        BHTSIG2_SMOOTHING                       0x01
+#define        BHTSIG2_SOUNDING                        0x02
+#define        BHTSIG2_AGGREATON                       0x08
+#define        BHTSIG2_STBC                            0x30
+#define        BHTSIG2_ADVCODING                       0x40
+#define        BHTSIG2_NUMOFHTLTF                      0x300
+#define        BHTSIG2_CRC8                            0x3fc
+#define        BHTSIG1_MCS                             0x7f
+#define        BHTSIG1_BANDWIDTH                       0x80
+#define        BHTSIG1_HTLENGTH                        0xffff
+#define        BLSIG_RATE                              0xf
+#define        BLSIG_RESERVED                          0x10
+#define        BLSIG_LENGTH                            0x1fffe
+#define        BLSIG_PARITY                            0x20
+#define        BCCKRXPHASE                             0x4
+
+#define        BLSSIREADADDRESS                        0x7f800000
+#define        BLSSIREADEDGE                           0x80000000
+
+#define        BLSSIREADBACKDATA                       0xfffff
+
+#define        BLSSIREADOKFLAG                         0x1000
+#define        BCCKSAMPLERATE                          0x8
+#define        BREGULATOR0STANDBY                      0x1
+#define        BREGULATORPLLSTANDBY                    0x2
+#define        BREGULATOR1STANDBY                      0x4
+#define        BPLLPOWERUP                             0x8
+#define        BDPLLPOWERUP                            0x10
+#define        BDA10POWERUP                            0x20
+#define        BAD7POWERUP                             0x200
+#define        BDA6POWERUP                             0x2000
+#define        BXTALPOWERUP                            0x4000
+#define        B40MDCLKPOWERUP                         0x8000
+#define        BDA6DEBUGMODE                           0x20000
+#define        BDA6SWING                               0x380000
+
+#define        BADCLKPHASE                             0x4000000
+#define        B80MCLKDELAY                            0x18000000
+#define        BAFEWATCHDOGENABLE                      0x20000000
+
+#define        BXTALCAP01                              0xc0000000
+#define        BXTALCAP23                              0x3
+#define        BXTALCAP92X                             0x0f000000
+#define BXTALCAP                               0x0f000000
+
+#define        BINTDIFCLKENABLE                        0x400
+#define        BEXTSIGCLKENABLE                        0x800
+#define        BBANDGAP_MBIAS_POWERUP                  0x10000
+#define        BAD11SH_GAIN                            0xc0000
+#define        BAD11NPUT_RANGE                         0x700000
+#define        BAD110P_CURRENT                         0x3800000
+#define        BLPATH_LOOPBACK                         0x4000000
+#define        BQPATH_LOOPBACK                         0x8000000
+#define        BAFE_LOOPBACK                           0x10000000
+#define        BDA10_SWING                             0x7e0
+#define        BDA10_REVERSE                           0x800
+#define        BDA_CLK_SOURCE                          0x1000
+#define        BDA7INPUT_RANGE                         0x6000
+#define        BDA7_GAIN                               0x38000
+#define        BDA7OUTPUT_CM_MODE                      0x40000
+#define        BDA7INPUT_CM_MODE                       0x380000
+#define        BDA7CURRENT                             0xc00000
+#define        BREGULATOR_ADJUST                       0x7000000
+#define        BAD11POWERUP_ATTX                       0x1
+#define        BDA10PS_ATTX                            0x10
+#define        BAD11POWERUP_ATRX                       0x100
+#define        BDA10PS_ATRX                            0x1000
+#define        BCCKRX_AGC_FORMAT                       0x200
+#define        BPSDFFT_SAMPLE_POINT                    0xc000
+#define        BPSD_AVERAGE_NUM                        0x3000
+#define        BIQPATH_CONTROL                         0xc00
+#define        BPSD_FREQ                               0x3ff
+#define        BPSD_ANTENNA_PATH                       0x30
+#define        BPSD_IQ_SWITCH                          0x40
+#define        BPSD_RX_TRIGGER                         0x400000
+#define        BPSD_TX_TRIGGERCW                       0x80000000
+#define        BPSD_SINE_TONE_SCALE                    0x7f000000
+#define        BPSD_REPORT                             0xffff
+
+#define        BOFDM_TXSC                              0x30000000
+#define        BCCK_TXON                               0x1
+#define        BOFDM_TXON                              0x2
+#define        BDEBUG_PAGE                             0xfff
+#define        BDEBUG_ITEM                             0xff
+#define        BANTL                                   0x10
+#define        BANT_NONHT                              0x100
+#define        BANT_HT1                                0x1000
+#define        BANT_HT2                                0x10000
+#define        BANT_HT1S1                              0x100000
+#define        BANT_NONHTS1                            0x1000000
+
+#define        BCCK_BBMODE                             0x3
+#define        BCCK_TXPOWERSAVING                      0x80
+#define        BCCK_RXPOWERSAVING                      0x40
+
+#define        BCCK_SIDEBAND                           0x10
+
+#define        BCCK_SCRAMBLE                           0x8
+#define        BCCK_ANTDIVERSITY                       0x8000
+#define        BCCK_CARRIER_RECOVERY                   0x4000
+#define        BCCK_TXRATE                             0x3000
+#define        BCCK_DCCANCEL                           0x0800
+#define        BCCK_ISICANCEL                          0x0400
+#define        BCCK_MATCH_FILTER                       0x0200
+#define        BCCK_EQUALIZER                          0x0100
+#define        BCCK_PREAMBLE_DETECT                    0x800000
+#define        BCCK_FAST_FALSECCA                      0x400000
+#define        BCCK_CH_ESTSTART                        0x300000
+#define        BCCK_CCA_COUNT                          0x080000
+#define        BCCK_CS_LIM                             0x070000
+#define        BCCK_BIST_MODE                          0x80000000
+#define        BCCK_CCAMASK                            0x40000000
+#define        BCCK_TX_DAC_PHASE                       0x4
+#define        BCCK_RX_ADC_PHASE                       0x20000000
+#define        BCCKR_CP_MODE                           0x0100
+#define        BCCK_TXDC_OFFSET                        0xf0
+#define        BCCK_RXDC_OFFSET                        0xf
+#define        BCCK_CCA_MODE                           0xc000
+#define        BCCK_FALSECS_LIM                        0x3f00
+#define        BCCK_CS_RATIO                           0xc00000
+#define        BCCK_CORGBIT_SEL                        0x300000
+#define        BCCK_PD_LIM                             0x0f0000
+#define        BCCK_NEWCCA                             0x80000000
+#define        BCCK_RXHP_OF_IG                         0x8000
+#define        BCCK_RXIG                               0x7f00
+#define        BCCK_LNA_POLARITY                       0x800000
+#define        BCCK_RX1ST_BAIN                         0x7f0000
+#define        BCCK_RF_EXTEND                          0x20000000
+#define        BCCK_RXAGC_SATLEVEL                     0x1f000000
+#define        BCCK_RXAGC_SATCOUNT                     0xe0
+#define        BCCKRXRFSETTLE                          0x1f
+#define        BCCK_FIXED_RXAGC                        0x8000
+#define        BCCK_ANTENNA_POLARITY                   0x2000
+#define        BCCK_TXFILTER_TYPE                      0x0c00
+#define        BCCK_RXAGC_REPORTTYPE                   0x0300
+#define        BCCK_RXDAGC_EN                          0x80000000
+#define        BCCK_RXDAGC_PERIOD                      0x20000000
+#define        BCCK_RXDAGC_SATLEVEL                    0x1f000000
+#define        BCCK_TIMING_RECOVERY                    0x800000
+#define        BCCK_TXC0                               0x3f0000
+#define        BCCK_TXC1                               0x3f000000
+#define        BCCK_TXC2                               0x3f
+#define        BCCK_TXC3                               0x3f00
+#define        BCCK_TXC4                               0x3f0000
+#define        BCCK_TXC5                               0x3f000000
+#define        BCCK_TXC6                               0x3f
+#define        BCCK_TXC7                               0x3f00
+#define        BCCK_DEBUGPORT                          0xff0000
+#define        BCCK_DAC_DEBUG                          0x0f000000
+#define        BCCK_FALSEALARM_ENABLE                  0x8000
+#define        BCCK_FALSEALARM_READ                    0x4000
+#define        BCCK_TRSSI                              0x7f
+#define        BCCK_RXAGC_REPORT                       0xfe
+#define        BCCK_RXREPORT_ANTSEL                    0x80000000
+#define        BCCK_RXREPORT_MFOFF                     0x40000000
+#define        BCCK_RXREPORT_SQLOSS                    0x20000000
+#define        BCCK_RXREPORT_PKTLOSS                   0x10000000
+#define        BCCK_RXREPORT_LOCKEDBIT                 0x08000000
+#define        BCCK_RXREPORT_RATEERROR                 0x04000000
+#define        BCCK_RXREPORT_RXRATE                    0x03000000
+#define        BCCK_RXFA_COUNTER_LOWER                 0xff
+#define        BCCK_RXFA_COUNTER_UPPER                 0xff000000
+#define        BCCK_RXHPAGC_START                      0xe000
+#define        BCCK_RXHPAGC_FINAL                      0x1c00
+#define        BCCK_RXFALSEALARM_ENABLE                0x8000
+#define        BCCK_FACOUNTER_FREEZE                   0x4000
+#define        BCCK_TXPATH_SEL                         0x10000000
+#define        BCCK_DEFAULT_RXPATH                     0xc000000
+#define        BCCK_OPTION_RXPATH                      0x3000000
+
+#define        BNUM_OFSTF                              0x3
+#define        BSHIFT_L                                0xc0
+#define        BGI_TH                                  0xc
+#define        BRXPATH_A                               0x1
+#define        BRXPATH_B                               0x2
+#define        BRXPATH_C                               0x4
+#define        BRXPATH_D                               0x8
+#define        BTXPATH_A                               0x1
+#define        BTXPATH_B                               0x2
+#define        BTXPATH_C                               0x4
+#define        BTXPATH_D                               0x8
+#define        BTRSSI_FREQ                             0x200
+#define        BADC_BACKOFF                            0x3000
+#define        BDFIR_BACKOFF                           0xc000
+#define        BTRSSI_LATCH_PHASE                      0x10000
+#define        BRX_LDC_OFFSET                          0xff
+#define        BRX_QDC_OFFSET                          0xff00
+#define        BRX_DFIR_MODE                           0x1800000
+#define        BRX_DCNF_TYPE                           0xe000000
+#define        BRXIQIMB_A                              0x3ff
+#define        BRXIQIMB_B                              0xfc00
+#define        BRXIQIMB_C                              0x3f0000
+#define        BRXIQIMB_D                              0xffc00000
+#define        BDC_DC_NOTCH                            0x60000
+#define        BRXNB_NOTCH                             0x1f000000
+#define        BPD_TH                                  0xf
+#define        BPD_TH_OPT2                             0xc000
+#define        BPWED_TH                                0x700
+#define        BIFMF_WIN_L                             0x800
+#define        BPD_OPTION                              0x1000
+#define        BMF_WIN_L                               0xe000
+#define        BBW_SEARCH_L                            0x30000
+#define        BWIN_ENH_L                              0xc0000
+#define        BBW_TH                                  0x700000
+#define        BED_TH2                                 0x3800000
+#define        BBW_OPTION                              0x4000000
+#define        BRADIO_TH                               0x18000000
+#define        BWINDOW_L                               0xe0000000
+#define        BSBD_OPTION                             0x1
+#define        BFRAME_TH                               0x1c
+#define        BFS_OPTION                              0x60
+#define        BDC_SLOPE_CHECK                         0x80
+#define        BFGUARD_COUNTER_DC_L                    0xe00
+#define        BFRAME_WEIGHT_SHORT                     0x7000
+#define        BSUB_TUNE                               0xe00000
+#define        BFRAME_DC_LENGTH                        0xe000000
+#define        BSBD_START_OFFSET                       0x30000000
+#define        BFRAME_TH_2                             0x7
+#define        BFRAME_GI2_TH                           0x38
+#define        BGI2_SYNC_EN                            0x40
+#define        BSARCH_SHORT_EARLY                      0x300
+#define        BSARCH_SHORT_LATE                       0xc00
+#define        BSARCH_GI2_LATE                         0x70000
+#define        BCFOANTSUM                              0x1
+#define        BCFOACC                                 0x2
+#define        BCFOSTARTOFFSET                         0xc
+#define        BCFOLOOPBACK                            0x70
+#define        BCFOSUMWEIGHT                           0x80
+#define        BDAGCENABLE                             0x10000
+#define        BTXIQIMB_A                              0x3ff
+#define        BTXIQIMB_B                              0xfc00
+#define        BTXIQIMB_C                              0x3f0000
+#define        BTXIQIMB_D                              0xffc00000
+#define        BTXIDCOFFSET                            0xff
+#define        BTXIQDCOFFSET                           0xff00
+#define        BTXDFIRMODE                             0x10000
+#define        BTXPESUDO_NOISEON                       0x4000000
+#define        BTXPESUDO_NOISE_A                       0xff
+#define        BTXPESUDO_NOISE_B                       0xff00
+#define        BTXPESUDO_NOISE_C                       0xff0000
+#define        BTXPESUDO_NOISE_D                       0xff000000
+#define        BCCA_DROPOPTION                         0x20000
+#define        BCCA_DROPTHRES                          0xfff00000
+#define        BEDCCA_H                                0xf
+#define        BEDCCA_L                                0xf0
+#define        BLAMBDA_ED                              0x300
+#define        BRX_INITIALGAIN                         0x7f
+#define        BRX_ANTDIV_EN                           0x80
+#define        BRX_AGC_ADDRESS_FOR_LNA                 0x7f00
+#define        BRX_HIGHPOWER_FLOW                      0x8000
+#define        BRX_AGC_FREEZE_THRES                    0xc0000
+#define        BRX_FREEZESTEP_AGC1                     0x300000
+#define        BRX_FREEZESTEP_AGC2                     0xc00000
+#define        BRX_FREEZESTEP_AGC3                     0x3000000
+#define        BRX_FREEZESTEP_AGC0                     0xc000000
+#define        BRXRSSI_CMP_EN                          0x10000000
+#define        BRXQUICK_AGCEN                          0x20000000
+#define        BRXAGC_FREEZE_THRES_MODE                0x40000000
+#define        BRX_OVERFLOW_CHECKTYPE                  0x80000000
+#define        BRX_AGCSHIFT                            0x7f
+#define        BTRSW_TRI_ONLY                          0x80
+#define        BPOWER_THRES                            0x300
+#define        BRXAGC_EN                               0x1
+#define        BRXAGC_TOGETHER_EN                      0x2
+#define        BRXAGC_MIN                              0x4
+#define        BRXHP_INI                               0x7
+#define        BRXHP_TRLNA                             0x70
+#define        BRXHP_RSSI                              0x700
+#define        BRXHP_BBP1                              0x7000
+#define        BRXHP_BBP2                              0x70000
+#define        BRXHP_BBP3                              0x700000
+#define        BRSSI_H                                 0x7f0000
+#define        BRSSI_GEN                               0x7f000000
+#define        BRXSETTLE_TRSW                          0x7
+#define        BRXSETTLE_LNA                           0x38
+#define        BRXSETTLE_RSSI                          0x1c0
+#define        BRXSETTLE_BBP                           0xe00
+#define        BRXSETTLE_RXHP                          0x7000
+#define        BRXSETTLE_ANTSW_RSSI                    0x38000
+#define        BRXSETTLE_ANTSW                         0xc0000
+#define        BRXPROCESS_TIME_DAGC                    0x300000
+#define        BRXSETTLE_HSSI                          0x400000
+#define        BRXPROCESS_TIME_BBPPW                   0x800000
+#define        BRXANTENNA_POWER_SHIFT                  0x3000000
+#define        BRSSI_TABLE_SELECT                      0xc000000
+#define        BRXHP_FINAL                             0x7000000
+#define        BRXHPSETTLE_BBP                         0x7
+#define        BRXHTSETTLE_HSSI                        0x8
+#define        BRXHTSETTLE_RXHP                        0x70
+#define        BRXHTSETTLE_BBPPW                       0x80
+#define        BRXHTSETTLE_IDLE                        0x300
+#define        BRXHTSETTLE_RESERVED                    0x1c00
+#define        BRXHT_RXHP_EN                           0x8000
+#define        BRXAGC_FREEZE_THRES                     0x30000
+#define        BRXAGC_TOGETHEREN                       0x40000
+#define        BRXHTAGC_MIN                            0x80000
+#define        BRXHTAGC_EN                             0x100000
+#define        BRXHTDAGC_EN                            0x200000
+#define        BRXHT_RXHP_BBP                          0x1c00000
+#define        BRXHT_RXHP_FINAL                        0xe0000000
+#define        BRXPW_RADIO_TH                          0x3
+#define        BRXPW_RADIO_EN                          0x4
+#define        BRXMF_HOLD                              0x3800
+#define        BRXPD_DELAY_TH1                         0x38
+#define        BRXPD_DELAY_TH2                         0x1c0
+#define        BRXPD_DC_COUNT_MAX                      0x600
+#define        BRXPD_DELAY_TH                          0x8000
+#define        BRXPROCESS_DELAY                        0xf0000
+#define        BRXSEARCHRANGE_GI2_EARLY                0x700000
+#define        BRXFRAME_FUARD_COUNTER_L                0x3800000
+#define        BRXSGI_GUARD_L                          0xc000000
+#define        BRXSGI_SEARCH_L                         0x30000000
+#define        BRXSGI_TH                               0xc0000000
+#define        BDFSCNT0                                0xff
+#define        BDFSCNT1                                0xff00
+#define        BDFSFLAG                                0xf0000
+#define        BMF_WEIGHT_SUM                          0x300000
+#define        BMINIDX_TH                              0x7f000000
+#define        BDAFORMAT                               0x40000
+#define        BTXCH_EMU_ENABLE                        0x01000000
+#define        BTRSW_ISOLATION_A                       0x7f
+#define        BTRSW_ISOLATION_B                       0x7f00
+#define        BTRSW_ISOLATION_C                       0x7f0000
+#define        BTRSW_ISOLATION_D                       0x7f000000
+#define        BEXT_LNA_GAIN                           0x7c00
+
+#define        BSTBC_EN                                0x4
+#define        BANTENNA_MAPPING                        0x10
+#define        BNSS                                    0x20
+#define        BCFO_ANTSUM_ID                          0x200
+#define        BPHY_COUNTER_RESET                      0x8000000
+#define        BCFO_REPORT_GET                         0x4000000
+#define        BOFDM_CONTINUE_TX                       0x10000000
+#define        BOFDM_SINGLE_CARRIER                    0x20000000
+#define        BOFDM_SINGLE_TONE                       0x40000000
+#define        BHT_DETECT                              0x100
+#define        BCFOEN                                  0x10000
+#define        BCFOVALUE                               0xfff00000
+#define        BSIGTONE_RE                             0x3f
+#define        BSIGTONE_IM                             0x7f00
+#define        BCOUNTER_CCA                            0xffff
+#define        BCOUNTER_PARITYFAIL                     0xffff0000
+#define        BCOUNTER_RATEILLEGAL                    0xffff
+#define        BCOUNTER_CRC8FAIL                       0xffff0000
+#define        BCOUNTER_MCSNOSUPPORT                   0xffff
+#define        BCOUNTER_FASTSYNC                       0xffff
+#define        BSHORTCFO                               0xfff
+#define        BSHORTCFOT_LENGTH                       12
+#define        BSHORTCFOF_LENGTH                       11
+#define        BLONGCFO                                0x7ff
+#define        BLONGCFOT_LENGTH                        11
+#define        BLONGCFOF_LENGTH                        11
+#define        BTAILCFO                                0x1fff
+#define        BTAILCFOT_LENGTH                        13
+#define        BTAILCFOF_LENGTH                        12
+#define        BNOISE_EN_PWDB                          0xffff
+#define        BCC_POWER_DB                            0xffff0000
+#define        BMOISE_PWDB                             0xffff
+#define        BPOWERMEAST_LENGTH                      10
+#define        BPOWERMEASF_LENGTH                      3
+#define        BRX_HT_BW                               0x1
+#define        BRXSC                                   0x6
+#define        BRX_HT                                  0x8
+#define        BNB_INTF_DET_ON                         0x1
+#define        BINTF_WIN_LEN_CFG                       0x30
+#define        BNB_INTF_TH_CFG                         0x1c0
+#define        BRFGAIN                                 0x3f
+#define        BTABLESEL                               0x40
+#define        BTRSW                                   0x80
+#define        BRXSNR_A                                0xff
+#define        BRXSNR_B                                0xff00
+#define        BRXSNR_C                                0xff0000
+#define        BRXSNR_D                                0xff000000
+#define        BSNR_EVMT_LENGTH                        8
+#define        BSNR_EVMF_LENGTH                        1
+#define        BCSI1ST                                 0xff
+#define        BCSI2ND                                 0xff00
+#define        BRXEVM1ST                               0xff0000
+#define        BRXEVM2ND                               0xff000000
+#define        BSIGEVM                                 0xff
+#define        BPWDB                                   0xff00
+#define        BSGIEN                                  0x10000
+
+#define        BSFACTOR_QMA1                           0xf
+#define        BSFACTOR_QMA2                           0xf0
+#define        BSFACTOR_QMA3                           0xf00
+#define        BSFACTOR_QMA4                           0xf000
+#define        BSFACTOR_QMA5                           0xf0000
+#define        BSFACTOR_QMA6                           0xf0000
+#define        BSFACTOR_QMA7                           0xf00000
+#define        BSFACTOR_QMA8                           0xf000000
+#define        BSFACTOR_QMA9                           0xf0000000
+#define        BCSI_SCHEME                             0x100000
+
+#define        BNOISE_LVL_TOP_SET                      0x3
+#define        BCHSMOOTH                               0x4
+#define        BCHSMOOTH_CFG1                          0x38
+#define        BCHSMOOTH_CFG2                          0x1c0
+#define        BCHSMOOTH_CFG3                          0xe00
+#define        BCHSMOOTH_CFG4                          0x7000
+#define        BMRCMODE                                0x800000
+#define        BTHEVMCFG                               0x7000000
+
+#define        BLOOP_FIT_TYPE                          0x1
+#define        BUPD_CFO                                0x40
+#define        BUPD_CFO_OFFDATA                        0x80
+#define        BADV_UPD_CFO                            0x100
+#define        BADV_TIME_CTRL                          0x800
+#define        BUPD_CLKO                               0x1000
+#define        BFC                                     0x6000
+#define        BTRACKING_MODE                          0x8000
+#define        BPHCMP_ENABLE                           0x10000
+#define        BUPD_CLKO_LTF                           0x20000
+#define        BCOM_CH_CFO                             0x40000
+#define        BCSI_ESTI_MODE                          0x80000
+#define        BADV_UPD_EQZ                            0x100000
+#define        BUCHCFG                                 0x7000000
+#define        BUPDEQZ                                 0x8000000
+
+#define        BRX_PESUDO_NOISE_ON                     0x20000000
+#define        BRX_PESUDO_NOISE_A                      0xff
+#define        BRX_PESUDO_NOISE_B                      0xff00
+#define        BRX_PESUDO_NOISE_C                      0xff0000
+#define        BRX_PESUDO_NOISE_D                      0xff000000
+#define        BRX_PESUDO_NOISESTATE_A                 0xffff
+#define        BRX_PESUDO_NOISESTATE_B                 0xffff0000
+#define        BRX_PESUDO_NOISESTATE_C                 0xffff
+#define        BRX_PESUDO_NOISESTATE_D                 0xffff0000
+
+#define        BZEBRA1_HSSIENABLE                      0x8
+#define        BZEBRA1_TRXCONTROL                      0xc00
+#define        BZEBRA1_TRXGAINSETTING                  0x07f
+#define        BZEBRA1_RXCOUNTER                       0xc00
+#define        BZEBRA1_TXCHANGEPUMP                    0x38
+#define        BZEBRA1_RXCHANGEPUMP                    0x7
+#define        BZEBRA1_CHANNEL_NUM                     0xf80
+#define        BZEBRA1_TXLPFBW                         0x400
+#define        BZEBRA1_RXLPFBW                         0x600
+
+#define        BRTL8256REG_MODE_CTRL1                  0x100
+#define        BRTL8256REG_MODE_CTRL0                  0x40
+#define        BRTL8256REG_TXLPFBW                     0x18
+#define        BRTL8256REG_RXLPFBW                     0x600
+
+#define        BRTL8258_TXLPFBW                        0xc
+#define        BRTL8258_RXLPFBW                        0xc00
+#define        BRTL8258_RSSILPFBW                      0xc0
+
+#define        BBYTE0                                  0x1
+#define        BBYTE1                                  0x2
+#define        BBYTE2                                  0x4
+#define        BBYTE3                                  0x8
+#define        BWORD0                                  0x3
+#define        BWORD1                                  0xc
+#define        BWORD                                   0xf
+
+#define        MASKBYTE0                               0xff
+#define        MASKBYTE1                               0xff00
+#define        MASKBYTE2                               0xff0000
+#define        MASKBYTE3                               0xff000000
+#define        MASKHWORD                               0xffff0000
+#define        MASKLWORD                               0x0000ffff
+#define        MASKDWORD                               0xffffffff
+#define        MASK12BITS                              0xfff
+#define        MASKH4BITS                              0xf0000000
+#define MASKOFDM_D                             0xffc00000
+#define        MASKCCK                                 0x3f3f3f3f
+
+#define        MASK4BITS                               0x0f
+#define        MASK20BITS                              0xfffff
+#define RFREG_OFFSET_MASK                      0xfffff
+
+#define        BENABLE                                 0x1
+#define        BDISABLE                                0x0
+
+#define        LEFT_ANTENNA                            0x0
+#define        RIGHT_ANTENNA                           0x1
+
+#define        TCHECK_TXSTATUS                         500
+#define        TUPDATE_RXCOUNTER                       100
+
+#define        REG_UN_USED_REGISTER                    0x01bf
+
+/* WOL bit information */
+#define        HAL92C_WOL_PTK_UPDATE_EVENT             BIT(0)
+#define        HAL92C_WOL_GTK_UPDATE_EVENT             BIT(1)
+#define        HAL92C_WOL_DISASSOC_EVENT               BIT(2)
+#define        HAL92C_WOL_DEAUTH_EVENT                 BIT(3)
+#define        HAL92C_WOL_FW_DISCONNECT_EVENT          BIT(4)
+
+#define                WOL_REASON_PTK_UPDATE           BIT(0)
+#define                WOL_REASON_GTK_UPDATE           BIT(1)
+#define                WOL_REASON_DISASSOC             BIT(2)
+#define                WOL_REASON_DEAUTH               BIT(3)
+#define                WOL_REASON_FW_DISCONNECT        BIT(4)
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/rf.c b/drivers/net/wireless/rtlwifi/rtl8188ee/rf.c
new file mode 100644 (file)
index 0000000..4faafdb
--- /dev/null
@@ -0,0 +1,467 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "rf.h"
+#include "dm.h"
+
+void rtl88e_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+       switch (bandwidth) {
+       case HT_CHANNEL_WIDTH_20:
+               rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] &
+                                            0xfffff3ff) | BIT(10) | BIT(11));
+               rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK,
+                             rtlphy->rfreg_chnlval[0]);
+               break;
+       case HT_CHANNEL_WIDTH_20_40:
+               rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] &
+                                            0xfffff3ff) | BIT(10));
+               rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK,
+                             rtlphy->rfreg_chnlval[0]);
+               break;
+       default:
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "unknown bandwidth: %#X\n", bandwidth);
+               break;
+       }
+}
+
+void rtl88e_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+                                      u8 *plevel)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       u32 tx_agc[2] = {0, 0}, tmpval;
+       bool turbo_scanoff = false;
+       u8 idx1, idx2;
+       u8 *ptr;
+       u8 direction;
+       u32 pwrtrac_value;
+
+       if (rtlefuse->eeprom_regulatory != 0)
+               turbo_scanoff = true;
+
+       if (mac->act_scanning == true) {
+               tx_agc[RF90_PATH_A] = 0x3f3f3f3f;
+               tx_agc[RF90_PATH_B] = 0x3f3f3f3f;
+
+               if (turbo_scanoff) {
+                       for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+                               tx_agc[idx1] = plevel[idx1] |
+                                              (plevel[idx1] << 8) |
+                                              (plevel[idx1] << 16) |
+                                              (plevel[idx1] << 24);
+                       }
+               }
+       } else {
+               for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+                       tx_agc[idx1] = plevel[idx1] | (plevel[idx1] << 8) |
+                                      (plevel[idx1] << 16) |
+                                      (plevel[idx1] << 24);
+               }
+
+               if (rtlefuse->eeprom_regulatory == 0) {
+                       tmpval = (rtlphy->mcs_offset[0][6]) +
+                                (rtlphy->mcs_offset[0][7] << 8);
+                       tx_agc[RF90_PATH_A] += tmpval;
+
+                       tmpval = (rtlphy->mcs_offset[0][14]) +
+                                (rtlphy->mcs_offset[0][15] << 24);
+                       tx_agc[RF90_PATH_B] += tmpval;
+               }
+       }
+
+       for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+               ptr = (u8 *)(&(tx_agc[idx1]));
+               for (idx2 = 0; idx2 < 4; idx2++) {
+                       if (*ptr > RF6052_MAX_TX_PWR)
+                               *ptr = RF6052_MAX_TX_PWR;
+                       ptr++;
+               }
+       }
+       rtl88e_dm_txpower_track_adjust(hw, 1, &direction, &pwrtrac_value);
+       if (direction == 1) {
+               tx_agc[0] += pwrtrac_value;
+               tx_agc[1] += pwrtrac_value;
+       } else if (direction == 2) {
+               tx_agc[0] -= pwrtrac_value;
+               tx_agc[1] -= pwrtrac_value;
+       }
+       tmpval = tx_agc[RF90_PATH_A] & 0xff;
+       rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, MASKBYTE1, tmpval);
+
+       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+               "CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval,
+               RTXAGC_A_CCK1_MCS32);
+
+       tmpval = tx_agc[RF90_PATH_A] >> 8;
+
+       rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval);
+
+       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+               "CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval,
+                RTXAGC_B_CCK11_A_CCK2_11);
+
+       tmpval = tx_agc[RF90_PATH_B] >> 24;
+       rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, MASKBYTE0, tmpval);
+
+       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+               "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval,
+                RTXAGC_B_CCK11_A_CCK2_11);
+
+       tmpval = tx_agc[RF90_PATH_B] & 0x00ffffff;
+       rtl_set_bbreg(hw, RTXAGC_B_CCK1_55_MCS32, 0xffffff00, tmpval);
+
+       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+               "CCK PWR 1~5.5M (rf-B) = 0x%x (reg 0x%x)\n", tmpval,
+                RTXAGC_B_CCK1_55_MCS32);
+}
+
+static void rtl88e_phy_get_power_base(struct ieee80211_hw *hw,
+                                     u8 *pwrlvlofdm, u8 *pwrlvlbw20,
+                                     u8 *pwrlvlbw40, u8 channel,
+                                     u32 *ofdmbase, u32 *mcsbase)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       u32 base0, base1;
+       u8 i, powerlevel[2];
+
+       for (i = 0; i < 2; i++) {
+               base0 = pwrlvlofdm[i];
+
+               base0 = (base0 << 24) | (base0 << 16) |
+                            (base0 << 8) | base0;
+               *(ofdmbase + i) = base0;
+               RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                       "[OFDM power base index rf(%c) = 0x%x]\n",
+                       ((i == 0) ? 'A' : 'B'), *(ofdmbase + i));
+       }
+
+       for (i = 0; i < 2; i++) {
+               if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20)
+                       powerlevel[i] = pwrlvlbw20[i];
+               else
+                       powerlevel[i] = pwrlvlbw40[i];
+               base1 = powerlevel[i];
+               base1 = (base1 << 24) |
+                   (base1 << 16) | (base1 << 8) | base1;
+
+               *(mcsbase + i) = base1;
+
+               RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                       "[MCS power base index rf(%c) = 0x%x]\n",
+                       ((i == 0) ? 'A' : 'B'), *(mcsbase + i));
+       }
+}
+
+static void get_txpwr_by_reg(struct ieee80211_hw *hw, u8 chan, u8 index,
+                            u32 *base0, u32 *base1, u32 *outval)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       u8 i, chg = 0, pwr_lim[4], pwr_diff = 0, cust_pwr_dif;
+       u32 writeval, cust_lim, rf, tmp;
+       u8 ch = chan - 1;
+       u8 j;
+
+       for (rf = 0; rf < 2; rf++) {
+               j = index + (rf ? 8 : 0);
+               tmp = ((index < 2) ? base0[rf] : base1[rf]);
+               switch (rtlefuse->eeprom_regulatory) {
+               case 0:
+                       chg = 0;
+
+                       writeval = rtlphy->mcs_offset[chg][j] + tmp;
+
+                       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                               "RTK better performance, "
+                               "writeval(%c) = 0x%x\n",
+                               ((rf == 0) ? 'A' : 'B'), writeval);
+                       break;
+               case 1:
+                       if (rtlphy->pwrgroup_cnt == 1) {
+                               chg = 0;
+                       } else {
+                               chg = chan / 3;
+                               if (chan == 14)
+                                       chg = 5;
+                       }
+                       writeval = rtlphy->mcs_offset[chg][j] + tmp;
+
+                       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                               "Realtek regulatory, 20MHz, writeval(%c) = 0x%x\n",
+                                ((rf == 0) ? 'A' : 'B'), writeval);
+                       break;
+               case 2:
+                       writeval = ((index < 2) ? base0[rf] : base1[rf]);
+
+                       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                               "Better regulatory, writeval(%c) = 0x%x\n",
+                                ((rf == 0) ? 'A' : 'B'), writeval);
+                       break;
+               case 3:
+                       chg = 0;
+
+                       if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
+                               RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                                       "customer's limit, 40MHz rf(%c) = 0x%x\n",
+                                        ((rf == 0) ? 'A' : 'B'),
+                                        rtlefuse->pwrgroup_ht40[rf][ch]);
+                       } else {
+                               RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                                       "customer's limit, 20MHz rf(%c) = 0x%x\n",
+                                        ((rf == 0) ? 'A' : 'B'),
+                                        rtlefuse->pwrgroup_ht20[rf][ch]);
+                       }
+
+                       if (index < 2)
+                               pwr_diff = rtlefuse->txpwr_legacyhtdiff[rf][ch];
+                       else if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20)
+                               pwr_diff = rtlefuse->txpwr_ht20diff[rf][ch];
+
+                       if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40)
+                               cust_pwr_dif = rtlefuse->pwrgroup_ht40[rf][ch];
+                       else
+                               cust_pwr_dif = rtlefuse->pwrgroup_ht20[rf][ch];
+
+                       if (pwr_diff > cust_pwr_dif)
+                               pwr_diff = 0;
+                       else
+                               pwr_diff = cust_pwr_dif - pwr_diff;
+
+                       for (i = 0; i < 4; i++) {
+                               pwr_lim[i] = (u8)((rtlphy->mcs_offset[chg][j] &
+                                            (0x7f << (i * 8))) >> (i * 8));
+
+                               if (pwr_lim[i] > pwr_diff)
+                                       pwr_lim[i] = pwr_diff;
+                       }
+
+                       cust_lim = (pwr_lim[3] << 24) | (pwr_lim[2] << 16) |
+                                  (pwr_lim[1] << 8) | (pwr_lim[0]);
+
+                       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                               "Customer's limit rf(%c) = 0x%x\n",
+                               ((rf == 0) ? 'A' : 'B'), cust_lim);
+
+                       writeval = cust_lim + tmp;
+
+                       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                               "Customer, writeval rf(%c) = 0x%x\n",
+                               ((rf == 0) ? 'A' : 'B'), writeval);
+                       break;
+               default:
+                       chg = 0;
+                       writeval = rtlphy->mcs_offset[chg][j] + tmp;
+
+                       RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                               "RTK better performance, writeval "
+                               "rf(%c) = 0x%x\n",
+                               ((rf == 0) ? 'A' : 'B'), writeval);
+                       break;
+               }
+
+               if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1)
+                       writeval = writeval - 0x06060606;
+               else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
+                        TXHIGHPWRLEVEL_BT2)
+                       writeval -= 0x0c0c0c0c;
+               *(outval + rf) = writeval;
+       }
+}
+
+static void write_ofdm_pwr(struct ieee80211_hw *hw, u8 index, u32 *pvalue)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       u16 regoffset_a[6] = {
+               RTXAGC_A_RATE18_06, RTXAGC_A_RATE54_24,
+               RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04,
+               RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12
+       };
+       u16 regoffset_b[6] = {
+               RTXAGC_B_RATE18_06, RTXAGC_B_RATE54_24,
+               RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04,
+               RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12
+       };
+       u8 i, rf, pwr_val[4];
+       u32 writeval;
+       u16 regoffset;
+
+       for (rf = 0; rf < 2; rf++) {
+               writeval = pvalue[rf];
+               for (i = 0; i < 4; i++) {
+                       pwr_val[i] = (u8) ((writeval & (0x7f <<
+                                    (i * 8))) >> (i * 8));
+
+                       if (pwr_val[i] > RF6052_MAX_TX_PWR)
+                               pwr_val[i] = RF6052_MAX_TX_PWR;
+               }
+               writeval = (pwr_val[3] << 24) | (pwr_val[2] << 16) |
+                          (pwr_val[1] << 8) | pwr_val[0];
+
+               if (rf == 0)
+                       regoffset = regoffset_a[index];
+               else
+                       regoffset = regoffset_b[index];
+               rtl_set_bbreg(hw, regoffset, MASKDWORD, writeval);
+
+               RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
+                       "Set 0x%x = %08x\n", regoffset, writeval);
+       }
+}
+
+void rtl88e_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+                                       u8 *pwrlvlofdm,
+                                       u8 *pwrlvlbw20,
+                                       u8 *pwrlvlbw40, u8 chan)
+{
+       u32 writeval[2], base0[2], base1[2];
+       u8 index;
+       u8 direction;
+       u32 pwrtrac_value;
+
+       rtl88e_phy_get_power_base(hw, pwrlvlofdm, pwrlvlbw20,
+                                 pwrlvlbw40, chan, &base0[0],
+                                 &base1[0]);
+
+       rtl88e_dm_txpower_track_adjust(hw, 1, &direction, &pwrtrac_value);
+
+       for (index = 0; index < 6; index++) {
+               get_txpwr_by_reg(hw, chan, index, &base0[0], &base1[0],
+                                &writeval[0]);
+               if (direction == 1) {
+                       writeval[0] += pwrtrac_value;
+                       writeval[1] += pwrtrac_value;
+               } else if (direction == 2) {
+                       writeval[0] -= pwrtrac_value;
+                       writeval[1] -= pwrtrac_value;
+               }
+               write_ofdm_pwr(hw, index, &writeval[0]);
+       }
+}
+
+static bool rf6052_conf_para(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+       u32 u4val = 0;
+       u8 rfpath;
+       bool rtstatus = true;
+       struct bb_reg_def *pphyreg;
+
+       for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) {
+               pphyreg = &rtlphy->phyreg_def[rfpath];
+
+               switch (rfpath) {
+               case RF90_PATH_A:
+               case RF90_PATH_C:
+                       u4val = rtl_get_bbreg(hw, pphyreg->rfintfs,
+                                                   BRFSI_RFENV);
+                       break;
+               case RF90_PATH_B:
+               case RF90_PATH_D:
+                       u4val = rtl_get_bbreg(hw, pphyreg->rfintfs,
+                                                   BRFSI_RFENV << 16);
+                       break;
+               }
+
+               rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1);
+               udelay(1);
+
+               rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1);
+               udelay(1);
+
+               rtl_set_bbreg(hw, pphyreg->rfhssi_para2,
+                             B3WIREADDREAALENGTH, 0x0);
+               udelay(1);
+
+               rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0);
+               udelay(1);
+
+               switch (rfpath) {
+               case RF90_PATH_A:
+                       rtstatus = rtl88e_phy_config_rf_with_headerfile(hw,
+                                       (enum radio_path)rfpath);
+                       break;
+               case RF90_PATH_B:
+                       rtstatus = rtl88e_phy_config_rf_with_headerfile(hw,
+                                       (enum radio_path)rfpath);
+                       break;
+               case RF90_PATH_C:
+                       break;
+               case RF90_PATH_D:
+                       break;
+               }
+
+               switch (rfpath) {
+               case RF90_PATH_A:
+               case RF90_PATH_C:
+                       rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV, u4val);
+                       break;
+               case RF90_PATH_B:
+               case RF90_PATH_D:
+                       rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16,
+                                     u4val);
+                       break;
+               }
+
+               if (rtstatus != true) {
+                       RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                                "Radio[%d] Fail!!", rfpath);
+                       return false;
+               }
+       }
+
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "\n");
+       return rtstatus;
+}
+
+bool rtl88e_phy_rf6052_config(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+       if (rtlphy->rf_type == RF_1T1R)
+               rtlphy->num_total_rfpath = 1;
+       else
+               rtlphy->num_total_rfpath = 2;
+
+       return rf6052_conf_para(hw);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/rf.h b/drivers/net/wireless/rtlwifi/rtl8188ee/rf.h
new file mode 100644 (file)
index 0000000..a39a2a3
--- /dev/null
@@ -0,0 +1,46 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92C_RF_H__
+#define __RTL92C_RF_H__
+
+#define RF6052_MAX_TX_PWR              0x3F
+#define RF6052_MAX_REG                 0x3F
+
+void rtl88e_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
+                                    u8 bandwidth);
+void rtl88e_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+                                      u8 *ppowerlevel);
+void rtl88e_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+                                       u8 *ppowerlevel_ofdm,
+                                       u8 *ppowerlevel_bw20,
+                                       u8 *ppowerlevel_bw40, u8 channel);
+bool rtl88e_phy_rf6052_config(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c
new file mode 100644 (file)
index 0000000..c254693
--- /dev/null
@@ -0,0 +1,400 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../core.h"
+#include "../pci.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "dm.h"
+#include "hw.h"
+#include "sw.h"
+#include "trx.h"
+#include "led.h"
+#include "table.h"
+
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+
+static void rtl88e_init_aspm_vars(struct ieee80211_hw *hw)
+{
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       /*close ASPM for AMD defaultly */
+       rtlpci->const_amdpci_aspm = 0;
+
+       /* ASPM PS mode.
+        * 0 - Disable ASPM,
+        * 1 - Enable ASPM without Clock Req,
+        * 2 - Enable ASPM with Clock Req,
+        * 3 - Alwyas Enable ASPM with Clock Req,
+        * 4 - Always Enable ASPM without Clock Req.
+        * set defult to RTL8192CE:3 RTL8192E:2
+        */
+       rtlpci->const_pci_aspm = 3;
+
+       /*Setting for PCI-E device */
+       rtlpci->const_devicepci_aspm_setting = 0x03;
+
+       /*Setting for PCI-E bridge */
+       rtlpci->const_hostpci_aspm_setting = 0x02;
+
+       /* In Hw/Sw Radio Off situation.
+        * 0 - Default,
+        * 1 - From ASPM setting without low Mac Pwr,
+        * 2 - From ASPM setting with low Mac Pwr,
+        * 3 - Bus D3
+        * set default to RTL8192CE:0 RTL8192SE:2
+        */
+       rtlpci->const_hwsw_rfoff_d3 = 0;
+
+       /* This setting works for those device with
+        * backdoor ASPM setting such as EPHY setting.
+        * 0 - Not support ASPM,
+        * 1 - Support ASPM,
+        * 2 - According to chipset.
+        */
+       rtlpci->const_support_pciaspm = 1;
+}
+
+int rtl88e_init_sw_vars(struct ieee80211_hw *hw)
+{
+       int err = 0;
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       u8 tid;
+
+       rtl8188ee_bt_reg_init(hw);
+
+       rtlpriv->dm.dm_initialgain_enable = 1;
+       rtlpriv->dm.dm_flag = 0;
+       rtlpriv->dm.disable_framebursting = 0;
+       rtlpriv->dm.thermalvalue = 0;
+       rtlpci->transmit_config = CFENDFORM | BIT(15);
+
+       /* compatible 5G band 88ce just 2.4G band & smsp */
+       rtlpriv->rtlhal.current_bandtype = BAND_ON_2_4G;
+       rtlpriv->rtlhal.bandset = BAND_ON_2_4G;
+       rtlpriv->rtlhal.macphymode = SINGLEMAC_SINGLEPHY;
+
+       rtlpci->receive_config = (RCR_APPFCS |
+                                 RCR_APP_MIC |
+                                 RCR_APP_ICV |
+                                 RCR_APP_PHYST_RXFF |
+                                 RCR_HTC_LOC_CTRL |
+                                 RCR_AMF |
+                                 RCR_ACF |
+                                 RCR_ADF |
+                                 RCR_AICV |
+                                 RCR_ACRC32 |
+                                 RCR_AB |
+                                 RCR_AM |
+                                 RCR_APM |
+                                 0);
+
+       rtlpci->irq_mask[0] =
+                               (u32) (IMR_PSTIMEOUT    |
+                               IMR_HSISR_IND_ON_INT    |
+                               IMR_C2HCMD              |
+                               IMR_HIGHDOK             |
+                               IMR_MGNTDOK             |
+                               IMR_BKDOK               |
+                               IMR_BEDOK               |
+                               IMR_VIDOK               |
+                               IMR_VODOK               |
+                               IMR_RDU                 |
+                               IMR_ROK                 |
+                               0);
+       rtlpci->irq_mask[1] = (u32) (IMR_RXFOVW | 0);
+       rtlpci->sys_irq_mask = (u32) (HSIMR_PDN_INT_EN | HSIMR_RON_INT_EN);
+
+       /* for debug level */
+       rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
+       /* for LPS & IPS */
+       rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
+       rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
+       rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+       if (!rtlpriv->psc.inactiveps)
+               pr_info("rtl8188ee: Power Save off (module option)\n");
+       if (!rtlpriv->psc.fwctrl_lps)
+               pr_info("rtl8188ee: FW Power Save off (module option)\n");
+       rtlpriv->psc.reg_fwctrl_lps = 3;
+       rtlpriv->psc.reg_max_lps_awakeintvl = 5;
+       /* for ASPM, you can close aspm through
+        * set const_support_pciaspm = 0
+        */
+       rtl88e_init_aspm_vars(hw);
+
+       if (rtlpriv->psc.reg_fwctrl_lps == 1)
+               rtlpriv->psc.fwctrl_psmode = FW_PS_MIN_MODE;
+       else if (rtlpriv->psc.reg_fwctrl_lps == 2)
+               rtlpriv->psc.fwctrl_psmode = FW_PS_MAX_MODE;
+       else if (rtlpriv->psc.reg_fwctrl_lps == 3)
+               rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE;
+
+       /* for firmware buf */
+       rtlpriv->rtlhal.pfirmware = vmalloc(0x8000);
+       if (!rtlpriv->rtlhal.pfirmware) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "Can't alloc buffer for fw.\n");
+               return 1;
+       }
+
+       rtlpriv->cfg->fw_name = "rtlwifi/rtl8188efw.bin";
+       rtlpriv->max_fw_size = 0x8000;
+       pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
+       err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+                                     rtlpriv->io.dev, GFP_KERNEL, hw,
+                                     rtl_fw_cb);
+       if (err) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "Failed to request firmware!\n");
+               return 1;
+       }
+
+       /* for early mode */
+       rtlpriv->rtlhal.earlymode_enable = false;
+       rtlpriv->rtlhal.max_earlymode_num = 10;
+       for (tid = 0; tid < 8; tid++)
+               skb_queue_head_init(&rtlpriv->mac80211.skb_waitq[tid]);
+
+       /*low power */
+       rtlpriv->psc.low_power_enable = false;
+       if (rtlpriv->psc.low_power_enable) {
+               init_timer(&rtlpriv->works.fw_clockoff_timer);
+               setup_timer(&rtlpriv->works.fw_clockoff_timer,
+                           rtl88ee_fw_clk_off_timer_callback,
+                           (unsigned long)hw);
+       }
+
+       init_timer(&rtlpriv->works.fast_antenna_training_timer);
+       setup_timer(&rtlpriv->works.fast_antenna_training_timer,
+                   rtl88e_dm_fast_antenna_training_callback,
+                   (unsigned long)hw);
+       return err;
+}
+
+void rtl88e_deinit_sw_vars(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+       if (rtlpriv->rtlhal.pfirmware) {
+               vfree(rtlpriv->rtlhal.pfirmware);
+               rtlpriv->rtlhal.pfirmware = NULL;
+       }
+
+       if (rtlpriv->psc.low_power_enable)
+               del_timer_sync(&rtlpriv->works.fw_clockoff_timer);
+
+       del_timer_sync(&rtlpriv->works.fast_antenna_training_timer);
+}
+
+static struct rtl_hal_ops rtl8188ee_hal_ops = {
+       .init_sw_vars = rtl88e_init_sw_vars,
+       .deinit_sw_vars = rtl88e_deinit_sw_vars,
+       .read_eeprom_info = rtl88ee_read_eeprom_info,
+       .interrupt_recognized = rtl88ee_interrupt_recognized,/*need check*/
+       .hw_init = rtl88ee_hw_init,
+       .hw_disable = rtl88ee_card_disable,
+       .hw_suspend = rtl88ee_suspend,
+       .hw_resume = rtl88ee_resume,
+       .enable_interrupt = rtl88ee_enable_interrupt,
+       .disable_interrupt = rtl88ee_disable_interrupt,
+       .set_network_type = rtl88ee_set_network_type,
+       .set_chk_bssid = rtl88ee_set_check_bssid,
+       .set_qos = rtl88ee_set_qos,
+       .set_bcn_reg = rtl88ee_set_beacon_related_registers,
+       .set_bcn_intv = rtl88ee_set_beacon_interval,
+       .update_interrupt_mask = rtl88ee_update_interrupt_mask,
+       .get_hw_reg = rtl88ee_get_hw_reg,
+       .set_hw_reg = rtl88ee_set_hw_reg,
+       .update_rate_tbl = rtl88ee_update_hal_rate_tbl,
+       .fill_tx_desc = rtl88ee_tx_fill_desc,
+       .fill_tx_cmddesc = rtl88ee_tx_fill_cmddesc,
+       .query_rx_desc = rtl88ee_rx_query_desc,
+       .set_channel_access = rtl88ee_update_channel_access_setting,
+       .radio_onoff_checking = rtl88ee_gpio_radio_on_off_checking,
+       .set_bw_mode = rtl88e_phy_set_bw_mode,
+       .switch_channel = rtl88e_phy_sw_chnl,
+       .dm_watchdog = rtl88e_dm_watchdog,
+       .scan_operation_backup = rtl88e_phy_scan_operation_backup,
+       .set_rf_power_state = rtl88e_phy_set_rf_power_state,
+       .led_control = rtl88ee_led_control,
+       .set_desc = rtl88ee_set_desc,
+       .get_desc = rtl88ee_get_desc,
+       .tx_polling = rtl88ee_tx_polling,
+       .enable_hw_sec = rtl88ee_enable_hw_security_config,
+       .set_key = rtl88ee_set_key,
+       .init_sw_leds = rtl88ee_init_sw_leds,
+       .allow_all_destaddr = rtl88ee_allow_all_destaddr,
+       .get_bbreg = rtl88e_phy_query_bb_reg,
+       .set_bbreg = rtl88e_phy_set_bb_reg,
+       .get_rfreg = rtl88e_phy_query_rf_reg,
+       .set_rfreg = rtl88e_phy_set_rf_reg,
+};
+
+static struct rtl_mod_params rtl88ee_mod_params = {
+       .sw_crypto = false,
+       .inactiveps = true,
+       .swctrl_lps = false,
+       .fwctrl_lps = true,
+       .debug = DBG_EMERG,
+};
+
+static struct rtl_hal_cfg rtl88ee_hal_cfg = {
+       .bar_id = 2,
+       .write_readback = true,
+       .name = "rtl88e_pci",
+       .ops = &rtl8188ee_hal_ops,
+       .mod_params = &rtl88ee_mod_params,
+
+       .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL,
+       .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN,
+       .maps[SYS_CLK] = REG_SYS_CLKR,
+       .maps[MAC_RCR_AM] = AM,
+       .maps[MAC_RCR_AB] = AB,
+       .maps[MAC_RCR_ACRC32] = ACRC32,
+       .maps[MAC_RCR_ACF] = ACF,
+       .maps[MAC_RCR_AAP] = AAP,
+
+       .maps[EFUSE_ACCESS] = REG_EFUSE_ACCESS,
+
+       .maps[EFUSE_TEST] = REG_EFUSE_TEST,
+       .maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
+       .maps[EFUSE_CLK] = 0,
+       .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL,
+       .maps[EFUSE_PWC_EV12V] = PWC_EV12V,
+       .maps[EFUSE_FEN_ELDR] = FEN_ELDR,
+       .maps[EFUSE_LOADER_CLK_EN] = LOADER_CLK_EN,
+       .maps[EFUSE_ANA8M] = ANA8M,
+       .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE,
+       .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION,
+       .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN,
+       .maps[EFUSE_OOB_PROTECT_BYTES_LEN] = EFUSE_OOB_PROTECT_BYTES,
+
+       .maps[RWCAM] = REG_CAMCMD,
+       .maps[WCAMI] = REG_CAMWRITE,
+       .maps[RCAMO] = REG_CAMREAD,
+       .maps[CAMDBG] = REG_CAMDBG,
+       .maps[SECR] = REG_SECCFG,
+       .maps[SEC_CAM_NONE] = CAM_NONE,
+       .maps[SEC_CAM_WEP40] = CAM_WEP40,
+       .maps[SEC_CAM_TKIP] = CAM_TKIP,
+       .maps[SEC_CAM_AES] = CAM_AES,
+       .maps[SEC_CAM_WEP104] = CAM_WEP104,
+
+       .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6,
+       .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5,
+       .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4,
+       .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3,
+       .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2,
+       .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1,
+/*     .maps[RTL_IMR_BCNDOK8] = IMR_BCNDOK8,     */   /*need check*/
+       .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7,
+       .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6,
+       .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5,
+       .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4,
+       .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3,
+       .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2,
+       .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1,
+/*     .maps[RTL_IMR_TIMEOUT2] = IMR_TIMEOUT2,*/
+/*     .maps[RTL_IMR_TIMEOUT1] = IMR_TIMEOUT1,*/
+
+       .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW,
+       .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT,
+       .maps[RTL_IMR_BCNINT] = IMR_BCNDMAINT0,
+       .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW,
+       .maps[RTL_IMR_RDU] = IMR_RDU,
+       .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND,
+       .maps[RTL_IMR_BDOK] = IMR_BCNDOK0,
+       .maps[RTL_IMR_MGNTDOK] = IMR_MGNTDOK,
+       .maps[RTL_IMR_TBDER] = IMR_TBDER,
+       .maps[RTL_IMR_HIGHDOK] = IMR_HIGHDOK,
+       .maps[RTL_IMR_TBDOK] = IMR_TBDOK,
+       .maps[RTL_IMR_BKDOK] = IMR_BKDOK,
+       .maps[RTL_IMR_BEDOK] = IMR_BEDOK,
+       .maps[RTL_IMR_VIDOK] = IMR_VIDOK,
+       .maps[RTL_IMR_VODOK] = IMR_VODOK,
+       .maps[RTL_IMR_ROK] = IMR_ROK,
+       .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNDMAINT0 | IMR_TBDOK | IMR_TBDER),
+
+       .maps[RTL_RC_CCK_RATE1M] = DESC92C_RATE1M,
+       .maps[RTL_RC_CCK_RATE2M] = DESC92C_RATE2M,
+       .maps[RTL_RC_CCK_RATE5_5M] = DESC92C_RATE5_5M,
+       .maps[RTL_RC_CCK_RATE11M] = DESC92C_RATE11M,
+       .maps[RTL_RC_OFDM_RATE6M] = DESC92C_RATE6M,
+       .maps[RTL_RC_OFDM_RATE9M] = DESC92C_RATE9M,
+       .maps[RTL_RC_OFDM_RATE12M] = DESC92C_RATE12M,
+       .maps[RTL_RC_OFDM_RATE18M] = DESC92C_RATE18M,
+       .maps[RTL_RC_OFDM_RATE24M] = DESC92C_RATE24M,
+       .maps[RTL_RC_OFDM_RATE36M] = DESC92C_RATE36M,
+       .maps[RTL_RC_OFDM_RATE48M] = DESC92C_RATE48M,
+       .maps[RTL_RC_OFDM_RATE54M] = DESC92C_RATE54M,
+
+       .maps[RTL_RC_HT_RATEMCS7] = DESC92C_RATEMCS7,
+       .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15,
+};
+
+static DEFINE_PCI_DEVICE_TABLE(rtl88ee_pci_ids) = {
+       {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8179, rtl88ee_hal_cfg)},
+       {},
+};
+
+MODULE_DEVICE_TABLE(pci, rtl88ee_pci_ids);
+
+MODULE_AUTHOR("zhiyuan_yang    <zhiyuan_yang@realsil.com.cn>");
+MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>");
+MODULE_AUTHOR("Larry Finger    <Larry.Finger@lwfinger.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek 8188E 802.11n PCI wireless");
+MODULE_FIRMWARE("rtlwifi/rtl8188efw.bin");
+
+module_param_named(swenc, rtl88ee_mod_params.sw_crypto, bool, 0444);
+module_param_named(debug, rtl88ee_mod_params.debug, int, 0444);
+module_param_named(ips, rtl88ee_mod_params.inactiveps, bool, 0444);
+module_param_named(swlps, rtl88ee_mod_params.swctrl_lps, bool, 0444);
+module_param_named(fwlps, rtl88ee_mod_params.fwctrl_lps, bool, 0444);
+MODULE_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(debug, "Set debug level (0-5) (default 0)");
+
+static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
+
+static struct pci_driver rtl88ee_driver = {
+       .name = KBUILD_MODNAME,
+       .id_table = rtl88ee_pci_ids,
+       .probe = rtl_pci_probe,
+       .remove = rtl_pci_disconnect,
+       .driver.pm = &rtlwifi_pm_ops,
+};
+
+module_pci_driver(rtl88ee_driver);
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.h b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.h
new file mode 100644 (file)
index 0000000..85e02b3
--- /dev/null
@@ -0,0 +1,36 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92CE_SW_H__
+#define __RTL92CE_SW_H__
+
+int rtl88e_init_sw_vars(struct ieee80211_hw *hw);
+void rtl88e_deinit_sw_vars(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/table.c b/drivers/net/wireless/rtlwifi/rtl8188ee/table.c
new file mode 100644 (file)
index 0000000..fad373f
--- /dev/null
@@ -0,0 +1,643 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Created on  2010/ 5/18,  1:41
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "table.h"
+
+u32 RTL8188EEPHY_REG_1TARRAY[] = {
+               0x800, 0x80040000,
+               0x804, 0x00000003,
+               0x808, 0x0000FC00,
+               0x80C, 0x0000000A,
+               0x810, 0x10001331,
+               0x814, 0x020C3D10,
+               0x818, 0x02200385,
+               0x81C, 0x00000000,
+               0x820, 0x01000100,
+               0x824, 0x00390204,
+               0x828, 0x00000000,
+               0x82C, 0x00000000,
+               0x830, 0x00000000,
+               0x834, 0x00000000,
+               0x838, 0x00000000,
+               0x83C, 0x00000000,
+               0x840, 0x00010000,
+               0x844, 0x00000000,
+               0x848, 0x00000000,
+               0x84C, 0x00000000,
+               0x850, 0x00000000,
+               0x854, 0x00000000,
+               0x858, 0x569A11A9,
+               0x85C, 0x01000014,
+               0x860, 0x66F60110,
+               0x864, 0x061F0649,
+               0x868, 0x00000000,
+               0x86C, 0x27272700,
+               0x870, 0x07000760,
+               0x874, 0x25004000,
+               0x878, 0x00000808,
+               0x87C, 0x00000000,
+               0x880, 0xB0000C1C,
+               0x884, 0x00000001,
+               0x888, 0x00000000,
+               0x88C, 0xCCC000C0,
+               0x890, 0x00000800,
+               0x894, 0xFFFFFFFE,
+               0x898, 0x40302010,
+               0x89C, 0x00706050,
+               0x900, 0x00000000,
+               0x904, 0x00000023,
+               0x908, 0x00000000,
+               0x90C, 0x81121111,
+               0x910, 0x00000002,
+               0x914, 0x00000201,
+               0xA00, 0x00D047C8,
+               0xA04, 0x80FF000C,
+               0xA08, 0x8C838300,
+               0xA0C, 0x2E7F120F,
+               0xA10, 0x9500BB78,
+               0xA14, 0x1114D028,
+               0xA18, 0x00881117,
+               0xA1C, 0x89140F00,
+               0xA20, 0x1A1B0000,
+               0xA24, 0x090E1317,
+               0xA28, 0x00000204,
+               0xA2C, 0x00D30000,
+               0xA70, 0x101FBF00,
+               0xA74, 0x00000007,
+               0xA78, 0x00000900,
+               0xA7C, 0x225B0606,
+               0xA80, 0x218075B1,
+               0xB2C, 0x80000000,
+               0xC00, 0x48071D40,
+               0xC04, 0x03A05611,
+               0xC08, 0x000000E4,
+               0xC0C, 0x6C6C6C6C,
+               0xC10, 0x08800000,
+               0xC14, 0x40000100,
+               0xC18, 0x08800000,
+               0xC1C, 0x40000100,
+               0xC20, 0x00000000,
+               0xC24, 0x00000000,
+               0xC28, 0x00000000,
+               0xC2C, 0x00000000,
+               0xC30, 0x69E9AC47,
+               0xC34, 0x469652AF,
+               0xC38, 0x49795994,
+               0xC3C, 0x0A97971C,
+               0xC40, 0x1F7C403F,
+               0xC44, 0x000100B7,
+               0xC48, 0xEC020107,
+               0xC4C, 0x007F037F,
+               0xC50, 0x69553420,
+               0xC54, 0x43BC0094,
+               0xC58, 0x00013169,
+               0xC5C, 0x00250492,
+               0xC60, 0x00000000,
+               0xC64, 0x7112848B,
+               0xC68, 0x47C00BFF,
+               0xC6C, 0x00000036,
+               0xC70, 0x2C7F000D,
+               0xC74, 0x020610DB,
+               0xC78, 0x0000001F,
+               0xC7C, 0x00B91612,
+               0xC80, 0x390000E4,
+               0xC84, 0x20F60000,
+               0xC88, 0x40000100,
+               0xC8C, 0x20200000,
+               0xC90, 0x00091521,
+               0xC94, 0x00000000,
+               0xC98, 0x00121820,
+               0xC9C, 0x00007F7F,
+               0xCA0, 0x00000000,
+               0xCA4, 0x000300A0,
+               0xCA8, 0x00000000,
+               0xCAC, 0x00000000,
+               0xCB0, 0x00000000,
+               0xCB4, 0x00000000,
+               0xCB8, 0x00000000,
+               0xCBC, 0x28000000,
+               0xCC0, 0x00000000,
+               0xCC4, 0x00000000,
+               0xCC8, 0x00000000,
+               0xCCC, 0x00000000,
+               0xCD0, 0x00000000,
+               0xCD4, 0x00000000,
+               0xCD8, 0x64B22427,
+               0xCDC, 0x00766932,
+               0xCE0, 0x00222222,
+               0xCE4, 0x00000000,
+               0xCE8, 0x37644302,
+               0xCEC, 0x2F97D40C,
+               0xD00, 0x00000740,
+               0xD04, 0x00020401,
+               0xD08, 0x0000907F,
+               0xD0C, 0x20010201,
+               0xD10, 0xA0633333,
+               0xD14, 0x3333BC43,
+               0xD18, 0x7A8F5B6F,
+               0xD2C, 0xCC979975,
+               0xD30, 0x00000000,
+               0xD34, 0x80608000,
+               0xD38, 0x00000000,
+               0xD3C, 0x00127353,
+               0xD40, 0x00000000,
+               0xD44, 0x00000000,
+               0xD48, 0x00000000,
+               0xD4C, 0x00000000,
+               0xD50, 0x6437140A,
+               0xD54, 0x00000000,
+               0xD58, 0x00000282,
+               0xD5C, 0x30032064,
+               0xD60, 0x4653DE68,
+               0xD64, 0x04518A3C,
+               0xD68, 0x00002101,
+               0xD6C, 0x2A201C16,
+               0xD70, 0x1812362E,
+               0xD74, 0x322C2220,
+               0xD78, 0x000E3C24,
+               0xE00, 0x2D2D2D2D,
+               0xE04, 0x2D2D2D2D,
+               0xE08, 0x0390272D,
+               0xE10, 0x2D2D2D2D,
+               0xE14, 0x2D2D2D2D,
+               0xE18, 0x2D2D2D2D,
+               0xE1C, 0x2D2D2D2D,
+               0xE28, 0x00000000,
+               0xE30, 0x1000DC1F,
+               0xE34, 0x10008C1F,
+               0xE38, 0x02140102,
+               0xE3C, 0x681604C2,
+               0xE40, 0x01007C00,
+               0xE44, 0x01004800,
+               0xE48, 0xFB000000,
+               0xE4C, 0x000028D1,
+               0xE50, 0x1000DC1F,
+               0xE54, 0x10008C1F,
+               0xE58, 0x02140102,
+               0xE5C, 0x28160D05,
+               0xE60, 0x00000008,
+               0xE68, 0x001B25A4,
+               0xE6C, 0x00C00014,
+               0xE70, 0x00C00014,
+               0xE74, 0x01000014,
+               0xE78, 0x01000014,
+               0xE7C, 0x01000014,
+               0xE80, 0x01000014,
+               0xE84, 0x00C00014,
+               0xE88, 0x01000014,
+               0xE8C, 0x00C00014,
+               0xED0, 0x00C00014,
+               0xED4, 0x00C00014,
+               0xED8, 0x00C00014,
+               0xEDC, 0x00000014,
+               0xEE0, 0x00000014,
+               0xEEC, 0x01C00014,
+               0xF14, 0x00000003,
+               0xF4C, 0x00000000,
+               0xF00, 0x00000300,
+
+};
+
+u32 RTL8188EEPHY_REG_ARRAY_PG[] = {
+               0xE00, 0xFFFFFFFF, 0x06070809,
+               0xE04, 0xFFFFFFFF, 0x02020405,
+               0xE08, 0x0000FF00, 0x00000006,
+               0x86C, 0xFFFFFF00, 0x00020400,
+               0xE10, 0xFFFFFFFF, 0x08090A0B,
+               0xE14, 0xFFFFFFFF, 0x01030607,
+               0xE18, 0xFFFFFFFF, 0x08090A0B,
+               0xE1C, 0xFFFFFFFF, 0x01030607,
+               0xE00, 0xFFFFFFFF, 0x00000000,
+               0xE04, 0xFFFFFFFF, 0x00000000,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x00000000,
+               0xE14, 0xFFFFFFFF, 0x00000000,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x02020202,
+               0xE04, 0xFFFFFFFF, 0x00020202,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x04040404,
+               0xE14, 0xFFFFFFFF, 0x00020404,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x02020202,
+               0xE04, 0xFFFFFFFF, 0x00020202,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x04040404,
+               0xE14, 0xFFFFFFFF, 0x00020404,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x00000000,
+               0xE04, 0xFFFFFFFF, 0x00000000,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x00000000,
+               0xE14, 0xFFFFFFFF, 0x00000000,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x02020202,
+               0xE04, 0xFFFFFFFF, 0x00020202,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x04040404,
+               0xE14, 0xFFFFFFFF, 0x00020404,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x00000000,
+               0xE04, 0xFFFFFFFF, 0x00000000,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x00000000,
+               0xE14, 0xFFFFFFFF, 0x00000000,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x00000000,
+               0xE04, 0xFFFFFFFF, 0x00000000,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x00000000,
+               0xE14, 0xFFFFFFFF, 0x00000000,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x00000000,
+               0xE04, 0xFFFFFFFF, 0x00000000,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x00000000,
+               0xE14, 0xFFFFFFFF, 0x00000000,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x00000000,
+               0xE04, 0xFFFFFFFF, 0x00000000,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x00000000,
+               0xE14, 0xFFFFFFFF, 0x00000000,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+               0xE00, 0xFFFFFFFF, 0x00000000,
+               0xE04, 0xFFFFFFFF, 0x00000000,
+               0xE08, 0x0000FF00, 0x00000000,
+               0x86C, 0xFFFFFF00, 0x00000000,
+               0xE10, 0xFFFFFFFF, 0x00000000,
+               0xE14, 0xFFFFFFFF, 0x00000000,
+               0xE18, 0xFFFFFFFF, 0x00000000,
+               0xE1C, 0xFFFFFFFF, 0x00000000,
+
+};
+
+u32 RTL8188EE_RADIOA_1TARRAY[] = {
+               0x000, 0x00030000,
+               0x008, 0x00084000,
+               0x018, 0x00000407,
+               0x019, 0x00000012,
+               0x01E, 0x00080009,
+               0x01F, 0x00000880,
+               0x02F, 0x0001A060,
+               0x03F, 0x00000000,
+               0x042, 0x000060C0,
+               0x057, 0x000D0000,
+               0x058, 0x000BE180,
+               0x067, 0x00001552,
+               0x083, 0x00000000,
+               0x0B0, 0x000FF8FC,
+               0x0B1, 0x00054400,
+               0x0B2, 0x000CCC19,
+               0x0B4, 0x00043003,
+               0x0B6, 0x0004953E,
+               0x0B7, 0x0001C718,
+               0x0B8, 0x000060FF,
+               0x0B9, 0x00080001,
+               0x0BA, 0x00040000,
+               0x0BB, 0x00000400,
+               0x0BF, 0x000C0000,
+               0x0C2, 0x00002400,
+               0x0C3, 0x00000009,
+               0x0C4, 0x00040C91,
+               0x0C5, 0x00099999,
+               0x0C6, 0x000000A3,
+               0x0C7, 0x00088820,
+               0x0C8, 0x00076C06,
+               0x0C9, 0x00000000,
+               0x0CA, 0x00080000,
+               0x0DF, 0x00000180,
+               0x0EF, 0x000001A0,
+               0x051, 0x0006B27D,
+               0x052, 0x0007E49D,
+               0x053, 0x00000073,
+               0x056, 0x00051FF3,
+               0x035, 0x00000086,
+               0x035, 0x00000186,
+               0x035, 0x00000286,
+               0x036, 0x00001C25,
+               0x036, 0x00009C25,
+               0x036, 0x00011C25,
+               0x036, 0x00019C25,
+               0x0B6, 0x00048538,
+               0x018, 0x00000C07,
+               0x05A, 0x0004BD00,
+               0x019, 0x000739D0,
+               0x034, 0x0000ADF3,
+               0x034, 0x00009DF0,
+               0x034, 0x00008DED,
+               0x034, 0x00007DEA,
+               0x034, 0x00006DE7,
+               0x034, 0x000054EE,
+               0x034, 0x000044EB,
+               0x034, 0x000034E8,
+               0x034, 0x0000246B,
+               0x034, 0x00001468,
+               0x034, 0x0000006D,
+               0x000, 0x00030159,
+               0x084, 0x00068200,
+               0x086, 0x000000CE,
+               0x087, 0x00048A00,
+               0x08E, 0x00065540,
+               0x08F, 0x00088000,
+               0x0EF, 0x000020A0,
+               0x03B, 0x000F02B0,
+               0x03B, 0x000EF7B0,
+               0x03B, 0x000D4FB0,
+               0x03B, 0x000CF060,
+               0x03B, 0x000B0090,
+               0x03B, 0x000A0080,
+               0x03B, 0x00090080,
+               0x03B, 0x0008F780,
+               0x03B, 0x000722B0,
+               0x03B, 0x0006F7B0,
+               0x03B, 0x00054FB0,
+               0x03B, 0x0004F060,
+               0x03B, 0x00030090,
+               0x03B, 0x00020080,
+               0x03B, 0x00010080,
+               0x03B, 0x0000F780,
+               0x0EF, 0x000000A0,
+               0x000, 0x00010159,
+               0x018, 0x0000F407,
+               0xFFE, 0x00000000,
+               0xFFE, 0x00000000,
+               0x01F, 0x00080003,
+               0xFFE, 0x00000000,
+               0xFFE, 0x00000000,
+               0x01E, 0x00000001,
+               0x01F, 0x00080000,
+               0x000, 0x00033E60,
+
+};
+
+u32 RTL8188EEMAC_1T_ARRAY[] = {
+               0x026, 0x00000041,
+               0x027, 0x00000035,
+               0x428, 0x0000000A,
+               0x429, 0x00000010,
+               0x430, 0x00000000,
+               0x431, 0x00000001,
+               0x432, 0x00000002,
+               0x433, 0x00000004,
+               0x434, 0x00000005,
+               0x435, 0x00000006,
+               0x436, 0x00000007,
+               0x437, 0x00000008,
+               0x438, 0x00000000,
+               0x439, 0x00000000,
+               0x43A, 0x00000001,
+               0x43B, 0x00000002,
+               0x43C, 0x00000004,
+               0x43D, 0x00000005,
+               0x43E, 0x00000006,
+               0x43F, 0x00000007,
+               0x440, 0x0000005D,
+               0x441, 0x00000001,
+               0x442, 0x00000000,
+               0x444, 0x00000015,
+               0x445, 0x000000F0,
+               0x446, 0x0000000F,
+               0x447, 0x00000000,
+               0x458, 0x00000041,
+               0x459, 0x000000A8,
+               0x45A, 0x00000072,
+               0x45B, 0x000000B9,
+               0x460, 0x00000066,
+               0x461, 0x00000066,
+               0x480, 0x00000008,
+               0x4C8, 0x000000FF,
+               0x4C9, 0x00000008,
+               0x4CC, 0x000000FF,
+               0x4CD, 0x000000FF,
+               0x4CE, 0x00000001,
+               0x4D3, 0x00000001,
+               0x500, 0x00000026,
+               0x501, 0x000000A2,
+               0x502, 0x0000002F,
+               0x503, 0x00000000,
+               0x504, 0x00000028,
+               0x505, 0x000000A3,
+               0x506, 0x0000005E,
+               0x507, 0x00000000,
+               0x508, 0x0000002B,
+               0x509, 0x000000A4,
+               0x50A, 0x0000005E,
+               0x50B, 0x00000000,
+               0x50C, 0x0000004F,
+               0x50D, 0x000000A4,
+               0x50E, 0x00000000,
+               0x50F, 0x00000000,
+               0x512, 0x0000001C,
+               0x514, 0x0000000A,
+               0x516, 0x0000000A,
+               0x525, 0x0000004F,
+               0x550, 0x00000010,
+               0x551, 0x00000010,
+               0x559, 0x00000002,
+               0x55D, 0x000000FF,
+               0x605, 0x00000030,
+               0x608, 0x0000000E,
+               0x609, 0x0000002A,
+               0x620, 0x000000FF,
+               0x621, 0x000000FF,
+               0x622, 0x000000FF,
+               0x623, 0x000000FF,
+               0x624, 0x000000FF,
+               0x625, 0x000000FF,
+               0x626, 0x000000FF,
+               0x627, 0x000000FF,
+               0x652, 0x00000020,
+               0x63C, 0x0000000A,
+               0x63D, 0x0000000A,
+               0x63E, 0x0000000E,
+               0x63F, 0x0000000E,
+               0x640, 0x00000040,
+               0x66E, 0x00000005,
+               0x700, 0x00000021,
+               0x701, 0x00000043,
+               0x702, 0x00000065,
+               0x703, 0x00000087,
+               0x708, 0x00000021,
+               0x709, 0x00000043,
+               0x70A, 0x00000065,
+               0x70B, 0x00000087,
+
+};
+
+u32 RTL8188EEAGCTAB_1TARRAY[] = {
+               0xC78, 0xFB000001,
+               0xC78, 0xFB010001,
+               0xC78, 0xFB020001,
+               0xC78, 0xFB030001,
+               0xC78, 0xFB040001,
+               0xC78, 0xFB050001,
+               0xC78, 0xFA060001,
+               0xC78, 0xF9070001,
+               0xC78, 0xF8080001,
+               0xC78, 0xF7090001,
+               0xC78, 0xF60A0001,
+               0xC78, 0xF50B0001,
+               0xC78, 0xF40C0001,
+               0xC78, 0xF30D0001,
+               0xC78, 0xF20E0001,
+               0xC78, 0xF10F0001,
+               0xC78, 0xF0100001,
+               0xC78, 0xEF110001,
+               0xC78, 0xEE120001,
+               0xC78, 0xED130001,
+               0xC78, 0xEC140001,
+               0xC78, 0xEB150001,
+               0xC78, 0xEA160001,
+               0xC78, 0xE9170001,
+               0xC78, 0xE8180001,
+               0xC78, 0xE7190001,
+               0xC78, 0xE61A0001,
+               0xC78, 0xE51B0001,
+               0xC78, 0xE41C0001,
+               0xC78, 0xE31D0001,
+               0xC78, 0xE21E0001,
+               0xC78, 0xE11F0001,
+               0xC78, 0x8A200001,
+               0xC78, 0x89210001,
+               0xC78, 0x88220001,
+               0xC78, 0x87230001,
+               0xC78, 0x86240001,
+               0xC78, 0x85250001,
+               0xC78, 0x84260001,
+               0xC78, 0x83270001,
+               0xC78, 0x82280001,
+               0xC78, 0x6B290001,
+               0xC78, 0x6A2A0001,
+               0xC78, 0x692B0001,
+               0xC78, 0x682C0001,
+               0xC78, 0x672D0001,
+               0xC78, 0x662E0001,
+               0xC78, 0x652F0001,
+               0xC78, 0x64300001,
+               0xC78, 0x63310001,
+               0xC78, 0x62320001,
+               0xC78, 0x61330001,
+               0xC78, 0x46340001,
+               0xC78, 0x45350001,
+               0xC78, 0x44360001,
+               0xC78, 0x43370001,
+               0xC78, 0x42380001,
+               0xC78, 0x41390001,
+               0xC78, 0x403A0001,
+               0xC78, 0x403B0001,
+               0xC78, 0x403C0001,
+               0xC78, 0x403D0001,
+               0xC78, 0x403E0001,
+               0xC78, 0x403F0001,
+               0xC78, 0xFB400001,
+               0xC78, 0xFB410001,
+               0xC78, 0xFB420001,
+               0xC78, 0xFB430001,
+               0xC78, 0xFB440001,
+               0xC78, 0xFB450001,
+               0xC78, 0xFB460001,
+               0xC78, 0xFB470001,
+               0xC78, 0xFB480001,
+               0xC78, 0xFA490001,
+               0xC78, 0xF94A0001,
+               0xC78, 0xF84B0001,
+               0xC78, 0xF74C0001,
+               0xC78, 0xF64D0001,
+               0xC78, 0xF54E0001,
+               0xC78, 0xF44F0001,
+               0xC78, 0xF3500001,
+               0xC78, 0xF2510001,
+               0xC78, 0xF1520001,
+               0xC78, 0xF0530001,
+               0xC78, 0xEF540001,
+               0xC78, 0xEE550001,
+               0xC78, 0xED560001,
+               0xC78, 0xEC570001,
+               0xC78, 0xEB580001,
+               0xC78, 0xEA590001,
+               0xC78, 0xE95A0001,
+               0xC78, 0xE85B0001,
+               0xC78, 0xE75C0001,
+               0xC78, 0xE65D0001,
+               0xC78, 0xE55E0001,
+               0xC78, 0xE45F0001,
+               0xC78, 0xE3600001,
+               0xC78, 0xE2610001,
+               0xC78, 0xC3620001,
+               0xC78, 0xC2630001,
+               0xC78, 0xC1640001,
+               0xC78, 0x8B650001,
+               0xC78, 0x8A660001,
+               0xC78, 0x89670001,
+               0xC78, 0x88680001,
+               0xC78, 0x87690001,
+               0xC78, 0x866A0001,
+               0xC78, 0x856B0001,
+               0xC78, 0x846C0001,
+               0xC78, 0x676D0001,
+               0xC78, 0x666E0001,
+               0xC78, 0x656F0001,
+               0xC78, 0x64700001,
+               0xC78, 0x63710001,
+               0xC78, 0x62720001,
+               0xC78, 0x61730001,
+               0xC78, 0x60740001,
+               0xC78, 0x46750001,
+               0xC78, 0x45760001,
+               0xC78, 0x44770001,
+               0xC78, 0x43780001,
+               0xC78, 0x42790001,
+               0xC78, 0x417A0001,
+               0xC78, 0x407B0001,
+               0xC78, 0x407C0001,
+               0xC78, 0x407D0001,
+               0xC78, 0x407E0001,
+               0xC78, 0x407F0001,
+};
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/table.h b/drivers/net/wireless/rtlwifi/rtl8188ee/table.h
new file mode 100644 (file)
index 0000000..c1218e8
--- /dev/null
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Created on  2010/ 5/18,  1:41
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92CE_TABLE__H_
+#define __RTL92CE_TABLE__H_
+
+#include <linux/types.h>
+#define  RTL8188EEPHY_REG_1TARRAYLEN   382
+extern u32 RTL8188EEPHY_REG_1TARRAY[];
+#define RTL8188EEPHY_REG_ARRAY_PGLEN   264
+extern u32 RTL8188EEPHY_REG_ARRAY_PG[];
+#define        RTL8188EE_RADIOA_1TARRAYLEN     190
+extern u32 RTL8188EE_RADIOA_1TARRAY[];
+#define RTL8188EEMAC_1T_ARRAYLEN       180
+extern u32 RTL8188EEMAC_1T_ARRAY[];
+#define RTL8188EEAGCTAB_1TARRAYLEN     256
+extern u32 RTL8188EEAGCTAB_1TARRAY[];
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
new file mode 100644 (file)
index 0000000..d075237
--- /dev/null
@@ -0,0 +1,817 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../base.h"
+#include "../stats.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "trx.h"
+#include "led.h"
+#include "dm.h"
+
+static u8 _rtl88ee_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue)
+{
+       __le16 fc = rtl_get_fc(skb);
+
+       if (unlikely(ieee80211_is_beacon(fc)))
+               return QSLT_BEACON;
+       if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))
+               return QSLT_MGNT;
+
+       return skb->priority;
+}
+
+static void _rtl88ee_query_rxphystatus(struct ieee80211_hw *hw,
+                       struct rtl_stats *pstatus, u8 *pdesc,
+                       struct rx_fwinfo_88e *p_drvinfo,
+                       bool bpacket_match_bssid,
+                       bool bpacket_toself, bool packet_beacon)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv);
+       struct phy_sts_cck_8192s_t *cck_buf;
+       struct phy_status_rpt *phystrpt = (struct phy_status_rpt *)p_drvinfo;
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       char rx_pwr_all = 0, rx_pwr[4];
+       u8 rf_rx_num = 0, evm, pwdb_all;
+       u8 i, max_spatial_stream;
+       u32 rssi, total_rssi = 0;
+       bool is_cck = pstatus->is_cck;
+       u8 lan_idx, vga_idx;
+
+       /* Record it for next packet processing */
+       pstatus->packet_matchbssid = bpacket_match_bssid;
+       pstatus->packet_toself = bpacket_toself;
+       pstatus->packet_beacon = packet_beacon;
+       pstatus->rx_mimo_sig_qual[0] = -1;
+       pstatus->rx_mimo_sig_qual[1] = -1;
+
+       if (is_cck) {
+               u8 cck_hipwr;
+               u8 cck_agc_rpt;
+               /* CCK Driver info Structure is not the same as OFDM packet. */
+               cck_buf = (struct phy_sts_cck_8192s_t *)p_drvinfo;
+               cck_agc_rpt = cck_buf->cck_agc_rpt;
+
+               /* (1)Hardware does not provide RSSI for CCK
+                * (2)PWDB, Average PWDB cacluated by
+                * hardware (for rate adaptive)
+                */
+               if (ppsc->rfpwr_state == ERFON)
+                       cck_hipwr = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2,
+                                                 BIT(9));
+               else
+                       cck_hipwr = false;
+
+               lan_idx = ((cck_agc_rpt & 0xE0) >> 5);
+               vga_idx = (cck_agc_rpt & 0x1f);
+               switch (lan_idx) {
+               case 7:
+                       if (vga_idx <= 27)
+                               rx_pwr_all = -100 + 2 * (27 - vga_idx);
+                       else
+                               rx_pwr_all = -100;
+                       break;
+               case 6:
+                       rx_pwr_all = -48 + 2 * (2 - vga_idx); /*VGA_idx = 2~0*/
+                       break;
+               case 5:
+                       rx_pwr_all = -42 + 2 * (7 - vga_idx); /*VGA_idx = 7~5*/
+                       break;
+               case 4:
+                       rx_pwr_all = -36 + 2 * (7 - vga_idx); /*VGA_idx = 7~4*/
+                       break;
+               case 3:
+                       rx_pwr_all = -24 + 2 * (7 - vga_idx); /*VGA_idx = 7~0*/
+                       break;
+               case 2:
+                       if (cck_hipwr)
+                               rx_pwr_all = -12 + 2 * (5 - vga_idx);
+                       else
+                               rx_pwr_all = -6 + 2 * (5 - vga_idx);
+                       break;
+               case 1:
+                       rx_pwr_all = 8 - 2 * vga_idx;
+                       break;
+               case 0:
+                       rx_pwr_all = 14 - 2 * vga_idx;
+                       break;
+               default:
+                       break;
+               }
+               rx_pwr_all += 6;
+               pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all);
+               /* CCK gain is smaller than OFDM/MCS gain,
+                * so we add gain diff by experiences,
+                * the val is 6
+                */
+               pwdb_all += 6;
+               if (pwdb_all > 100)
+                       pwdb_all = 100;
+               /* modify the offset to make the same
+                * gain index with OFDM.
+                */
+               if (pwdb_all > 34 && pwdb_all <= 42)
+                       pwdb_all -= 2;
+               else if (pwdb_all > 26 && pwdb_all <= 34)
+                       pwdb_all -= 6;
+               else if (pwdb_all > 14 && pwdb_all <= 26)
+                       pwdb_all -= 8;
+               else if (pwdb_all > 4 && pwdb_all <= 14)
+                       pwdb_all -= 4;
+               if (cck_hipwr == false) {
+                       if (pwdb_all >= 80)
+                               pwdb_all = ((pwdb_all - 80)<<1) +
+                                          ((pwdb_all - 80)>>1) + 80;
+                       else if ((pwdb_all <= 78) && (pwdb_all >= 20))
+                               pwdb_all += 3;
+                       if (pwdb_all > 100)
+                               pwdb_all = 100;
+               }
+
+               pstatus->rx_pwdb_all = pwdb_all;
+               pstatus->recvsignalpower = rx_pwr_all;
+
+               /* (3) Get Signal Quality (EVM) */
+               if (bpacket_match_bssid) {
+                       u8 sq;
+
+                       if (pstatus->rx_pwdb_all > 40) {
+                               sq = 100;
+                       } else {
+                               sq = cck_buf->sq_rpt;
+                               if (sq > 64)
+                                       sq = 0;
+                               else if (sq < 20)
+                                       sq = 100;
+                               else
+                                       sq = ((64 - sq) * 100) / 44;
+                       }
+
+                       pstatus->signalquality = sq;
+                       pstatus->rx_mimo_sig_qual[0] = sq;
+                       pstatus->rx_mimo_sig_qual[1] = -1;
+               }
+       } else {
+               rtlpriv->dm.rfpath_rxenable[0] =
+                   rtlpriv->dm.rfpath_rxenable[1] = true;
+
+               /* (1)Get RSSI for HT rate */
+               for (i = RF90_PATH_A; i < RF6052_MAX_PATH; i++) {
+                       /* we will judge RF RX path now. */
+                       if (rtlpriv->dm.rfpath_rxenable[i])
+                               rf_rx_num++;
+
+                       rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2)-110;
+
+                       /* Translate DBM to percentage. */
+                       rssi = rtl_query_rxpwrpercentage(rx_pwr[i]);
+                       total_rssi += rssi;
+
+                       /* Get Rx snr value in DB */
+                       rtlpriv->stats.rx_snr_db[i] = p_drvinfo->rxsnr[i] / 2;
+
+                       /* Record Signal Strength for next packet */
+                       if (bpacket_match_bssid)
+                               pstatus->rx_mimo_signalstrength[i] = (u8) rssi;
+               }
+
+               /* (2)PWDB, Average PWDB cacluated by
+                * hardware (for rate adaptive)
+                */
+               rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110;
+
+               pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all);
+               pstatus->rx_pwdb_all = pwdb_all;
+               pstatus->rxpower = rx_pwr_all;
+               pstatus->recvsignalpower = rx_pwr_all;
+
+               /* (3)EVM of HT rate */
+               if (pstatus->is_ht && pstatus->rate >= DESC92C_RATEMCS8 &&
+                   pstatus->rate <= DESC92C_RATEMCS15)
+                       max_spatial_stream = 2;
+               else
+                       max_spatial_stream = 1;
+
+               for (i = 0; i < max_spatial_stream; i++) {
+                       evm = rtl_evm_db_to_percentage(p_drvinfo->rxevm[i]);
+
+                       if (bpacket_match_bssid) {
+                               /* Fill value in RFD, Get the first
+                                * spatial stream only
+                                */
+                               if (i == 0)
+                                       pstatus->signalquality = evm & 0xff;
+                               pstatus->rx_mimo_sig_qual[i] = evm & 0xff;
+                       }
+               }
+       }
+
+       /* UI BSS List signal strength(in percentage),
+        * make it good looking, from 0~100.
+        */
+       if (is_cck)
+               pstatus->signalstrength = (u8)(rtl_signal_scale_mapping(hw,
+                                         pwdb_all));
+       else if (rf_rx_num != 0)
+               pstatus->signalstrength = (u8)(rtl_signal_scale_mapping(hw,
+                                         total_rssi /= rf_rx_num));
+       /*HW antenna diversity*/
+       rtldm->fat_table.antsel_rx_keep_0 = phystrpt->ant_sel;
+       rtldm->fat_table.antsel_rx_keep_1 = phystrpt->ant_sel_b;
+       rtldm->fat_table.antsel_rx_keep_2 = phystrpt->antsel_rx_keep_2;
+}
+
+static void _rtl88ee_smart_antenna(struct ieee80211_hw *hw,
+       struct rtl_stats *pstatus)
+{
+       struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       u8 ant_mux;
+       struct fast_ant_training *pfat = &(rtldm->fat_table);
+
+       if (rtlefuse->antenna_div_type == CG_TRX_SMART_ANTDIV) {
+               if (pfat->fat_state == FAT_TRAINING_STATE) {
+                       if (pstatus->packet_toself) {
+                               ant_mux = (pfat->antsel_rx_keep_2 << 2) |
+                                               (pfat->antsel_rx_keep_1 << 1) |
+                                                pfat->antsel_rx_keep_0;
+                               pfat->ant_sum[ant_mux] += pstatus->rx_pwdb_all;
+                               pfat->ant_cnt[ant_mux]++;
+                       }
+               }
+       } else if ((rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) ||
+                  (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV)) {
+               if (pstatus->packet_toself || pstatus->packet_matchbssid) {
+                       ant_mux = (pfat->antsel_rx_keep_2 << 2) |
+                                 (pfat->antsel_rx_keep_1 << 1) |
+                                  pfat->antsel_rx_keep_0;
+                       rtl88e_dm_ant_sel_statistics(hw, ant_mux, 0,
+                                                    pstatus->rx_pwdb_all);
+               }
+       }
+}
+
+static void _rtl88ee_translate_rx_signal_stuff(struct ieee80211_hw *hw,
+               struct sk_buff *skb, struct rtl_stats *pstatus,
+               u8 *pdesc, struct rx_fwinfo_88e *p_drvinfo)
+{
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+       struct ieee80211_hdr *hdr;
+       u8 *tmp_buf;
+       u8 *praddr;
+       u8 *psaddr;
+       __le16 fc;
+       u16 type, ufc;
+       bool match_bssid, packet_toself, packet_beacon, addr;
+
+       tmp_buf = skb->data + pstatus->rx_drvinfo_size + pstatus->rx_bufshift;
+
+       hdr = (struct ieee80211_hdr *)tmp_buf;
+       fc = hdr->frame_control;
+       ufc = le16_to_cpu(fc);
+       type = WLAN_FC_GET_TYPE(fc);
+       praddr = hdr->addr1;
+       psaddr = ieee80211_get_SA(hdr);
+       memcpy(pstatus->psaddr, psaddr, ETH_ALEN);
+
+       addr = (!compare_ether_addr(mac->bssid, (ufc & IEEE80211_FCTL_TODS) ?
+               hdr->addr1 : (ufc & IEEE80211_FCTL_FROMDS) ?
+               hdr->addr2 : hdr->addr3));
+       match_bssid = ((IEEE80211_FTYPE_CTL != type) && (!pstatus->hwerror) &&
+                      (!pstatus->crc) && (!pstatus->icv)) && addr;
+
+       addr = (!compare_ether_addr(praddr, rtlefuse->dev_addr));
+       packet_toself = match_bssid && addr;
+
+       if (ieee80211_is_beacon(fc))
+               packet_beacon = true;
+
+       _rtl88ee_query_rxphystatus(hw, pstatus, pdesc, p_drvinfo,
+                                  match_bssid, packet_toself, packet_beacon);
+       _rtl88ee_smart_antenna(hw, pstatus);
+       rtl_process_phyinfo(hw, tmp_buf, pstatus);
+}
+
+static void insert_em(struct rtl_tcb_desc *ptcb_desc, u8 *virtualaddress)
+{
+       u32 dwtmp = 0;
+
+       memset(virtualaddress, 0, 8);
+
+       SET_EARLYMODE_PKTNUM(virtualaddress, ptcb_desc->empkt_num);
+       if (ptcb_desc->empkt_num == 1) {
+               dwtmp = ptcb_desc->empkt_len[0];
+       } else {
+               dwtmp = ptcb_desc->empkt_len[0];
+               dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+               dwtmp += ptcb_desc->empkt_len[1];
+       }
+       SET_EARLYMODE_LEN0(virtualaddress, dwtmp);
+
+       if (ptcb_desc->empkt_num <= 3) {
+               dwtmp = ptcb_desc->empkt_len[2];
+       } else {
+               dwtmp = ptcb_desc->empkt_len[2];
+               dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+               dwtmp += ptcb_desc->empkt_len[3];
+       }
+       SET_EARLYMODE_LEN1(virtualaddress, dwtmp);
+       if (ptcb_desc->empkt_num <= 5) {
+               dwtmp = ptcb_desc->empkt_len[4];
+       } else {
+               dwtmp = ptcb_desc->empkt_len[4];
+               dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+               dwtmp += ptcb_desc->empkt_len[5];
+       }
+       SET_EARLYMODE_LEN2_1(virtualaddress, dwtmp & 0xF);
+       SET_EARLYMODE_LEN2_2(virtualaddress, dwtmp >> 4);
+       if (ptcb_desc->empkt_num <= 7) {
+               dwtmp = ptcb_desc->empkt_len[6];
+       } else {
+               dwtmp = ptcb_desc->empkt_len[6];
+               dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+               dwtmp += ptcb_desc->empkt_len[7];
+       }
+       SET_EARLYMODE_LEN3(virtualaddress, dwtmp);
+       if (ptcb_desc->empkt_num <= 9) {
+               dwtmp = ptcb_desc->empkt_len[8];
+       } else {
+               dwtmp = ptcb_desc->empkt_len[8];
+               dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+               dwtmp += ptcb_desc->empkt_len[9];
+       }
+       SET_EARLYMODE_LEN4(virtualaddress, dwtmp);
+}
+
+bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw,
+                          struct rtl_stats *status,
+                          struct ieee80211_rx_status *rx_status,
+                          u8 *pdesc, struct sk_buff *skb)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rx_fwinfo_88e *p_drvinfo;
+       struct ieee80211_hdr *hdr;
+
+       u32 phystatus = GET_RX_DESC_PHYST(pdesc);
+       status->packet_report_type = (u8)GET_RX_STATUS_DESC_RPT_SEL(pdesc);
+       if (status->packet_report_type == TX_REPORT2)
+               status->length = (u16) GET_RX_RPT2_DESC_PKT_LEN(pdesc);
+       else
+               status->length = (u16) GET_RX_DESC_PKT_LEN(pdesc);
+       status->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) *
+                                      RX_DRV_INFO_SIZE_UNIT;
+       status->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03);
+       status->icv = (u16) GET_RX_DESC_ICV(pdesc);
+       status->crc = (u16) GET_RX_DESC_CRC32(pdesc);
+       status->hwerror = (status->crc | status->icv);
+       status->decrypted = !GET_RX_DESC_SWDEC(pdesc);
+       status->rate = (u8) GET_RX_DESC_RXMCS(pdesc);
+       status->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc);
+       status->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1);
+       status->isfirst_ampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1) &&
+                                       (GET_RX_DESC_FAGGR(pdesc) == 1));
+       if (status->packet_report_type == NORMAL_RX)
+               status->timestamp_low = GET_RX_DESC_TSFL(pdesc);
+       status->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc);
+       status->is_ht = (bool)GET_RX_DESC_RXHT(pdesc);
+
+       status->is_cck = RTL8188_RX_HAL_IS_CCK_RATE(status->rate);
+
+       status->macid = GET_RX_DESC_MACID(pdesc);
+       if (GET_RX_STATUS_DESC_MAGIC_MATCH(pdesc))
+               status->wake_match = BIT(2);
+       else if (GET_RX_STATUS_DESC_MAGIC_MATCH(pdesc))
+               status->wake_match = BIT(1);
+       else if (GET_RX_STATUS_DESC_UNICAST_MATCH(pdesc))
+               status->wake_match = BIT(0);
+       else
+               status->wake_match = 0;
+       if (status->wake_match)
+               RT_TRACE(rtlpriv, COMP_RXDESC, DBG_LOUD,
+                        "Get Wakeup Packet!! WakeMatch =%d\n",
+                        status->wake_match);
+       rx_status->freq = hw->conf.channel->center_freq;
+       rx_status->band = hw->conf.channel->band;
+
+       if (status->crc)
+               rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+       if (status->rx_is40Mhzpacket)
+               rx_status->flag |= RX_FLAG_40MHZ;
+
+       if (status->is_ht)
+               rx_status->flag |= RX_FLAG_HT;
+
+       rx_status->flag |= RX_FLAG_MACTIME_START;
+
+       /* hw will set status->decrypted true, if it finds the
+        * frame is open data frame or mgmt frame.
+        * So hw will not decryption robust managment frame
+        * for IEEE80211w but still set status->decrypted
+        * true, so here we should set it back to undecrypted
+        * for IEEE80211w frame, and mac80211 sw will help
+        * to decrypt it
+        */
+       if (status->decrypted) {
+               hdr = (struct ieee80211_hdr *)(skb->data +
+                      status->rx_drvinfo_size + status->rx_bufshift);
+
+               if (!hdr) {
+                       /* During testing, hdr was NULL */
+                       return false;
+               }
+               if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+                   (ieee80211_has_protected(hdr->frame_control)))
+                       rx_status->flag &= ~RX_FLAG_DECRYPTED;
+               else
+                       rx_status->flag |= RX_FLAG_DECRYPTED;
+       }
+
+       /* rate_idx: index of data rate into band's
+        * supported rates or MCS index if HT rates
+        * are use (RX_FLAG_HT)
+        * Notice: this is diff with windows define
+        */
+       rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht,
+                                                  status->rate, false);
+
+       rx_status->mactime = status->timestamp_low;
+       if (phystatus == true) {
+               p_drvinfo = (struct rx_fwinfo_88e *)(skb->data +
+                                                    status->rx_bufshift);
+
+               _rtl88ee_translate_rx_signal_stuff(hw, skb, status, pdesc,
+                                                  p_drvinfo);
+       }
+
+       /*rx_status->qual = status->signal; */
+       rx_status->signal = status->recvsignalpower + 10;
+       /*rx_status->noise = -status->noise; */
+       if (status->packet_report_type == TX_REPORT2) {
+               status->macid_valid_entry[0] =
+                        GET_RX_RPT2_DESC_MACID_VALID_1(pdesc);
+               status->macid_valid_entry[1] =
+                        GET_RX_RPT2_DESC_MACID_VALID_2(pdesc);
+       }
+       return true;
+}
+
+void rtl88ee_tx_fill_desc(struct ieee80211_hw *hw,
+                         struct ieee80211_hdr *hdr, u8 *pdesc_tx,
+                         struct ieee80211_tx_info *info,
+                         struct ieee80211_sta *sta,
+                         struct sk_buff *skb,
+                         u8 hw_queue, struct rtl_tcb_desc *ptcb_desc)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+       u8 *pdesc = (u8 *)pdesc_tx;
+       u16 seq_number;
+       __le16 fc = hdr->frame_control;
+       unsigned int buf_len = 0;
+       unsigned int skb_len = skb->len;
+       u8 fw_qsel = _rtl88ee_map_hwqueue_to_fwqueue(skb, hw_queue);
+       bool firstseg = ((hdr->seq_ctrl &
+                           cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0);
+       bool lastseg = ((hdr->frame_control &
+                          cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) == 0);
+       dma_addr_t mapping;
+       u8 bw_40 = 0;
+       u8 short_gi = 0;
+
+       if (mac->opmode == NL80211_IFTYPE_STATION) {
+               bw_40 = mac->bw_40;
+       } else if (mac->opmode == NL80211_IFTYPE_AP ||
+               mac->opmode == NL80211_IFTYPE_ADHOC) {
+               if (sta)
+                       bw_40 = sta->ht_cap.cap &
+                               IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       }
+       seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
+       rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc);
+       /* reserve 8 byte for AMPDU early mode */
+       if (rtlhal->earlymode_enable) {
+               skb_push(skb, EM_HDR_LEN);
+               memset(skb->data, 0, EM_HDR_LEN);
+       }
+       buf_len = skb->len;
+       mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len,
+                                PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(rtlpci->pdev, mapping)) {
+               RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+                        "DMA mapping error");
+               return;
+       }
+       CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_88e));
+       if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) {
+               firstseg = true;
+               lastseg = true;
+       }
+       if (firstseg) {
+               if (rtlhal->earlymode_enable) {
+                       SET_TX_DESC_PKT_OFFSET(pdesc, 1);
+                       SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN +
+                                          EM_HDR_LEN);
+                       if (ptcb_desc->empkt_num) {
+                               RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+                                        "Insert 8 byte.pTcb->EMPktNum:%d\n",
+                                        ptcb_desc->empkt_num);
+                               insert_em(ptcb_desc, (u8 *)(skb->data));
+                       }
+               } else {
+                       SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN);
+               }
+
+               ptcb_desc->use_driver_rate = true;
+               SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate);
+               if (ptcb_desc->hw_rate > DESC92C_RATEMCS0)
+                       short_gi = (ptcb_desc->use_shortgi) ? 1 : 0;
+               else
+                       short_gi = (ptcb_desc->use_shortpreamble) ? 1 : 0;
+               SET_TX_DESC_DATA_SHORTGI(pdesc, short_gi);
+
+               if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+                       SET_TX_DESC_AGG_ENABLE(pdesc, 1);
+                       SET_TX_DESC_MAX_AGG_NUM(pdesc, 0x14);
+               }
+               SET_TX_DESC_SEQ(pdesc, seq_number);
+               SET_TX_DESC_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable &&
+                                             !ptcb_desc->cts_enable) ? 1 : 0));
+               SET_TX_DESC_HW_RTS_ENABLE(pdesc, 0);
+               SET_TX_DESC_CTS2SELF(pdesc, ((ptcb_desc->cts_enable) ? 1 : 0));
+               SET_TX_DESC_RTS_STBC(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0));
+
+               SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate);
+               SET_TX_DESC_RTS_BW(pdesc, 0);
+               SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc);
+               SET_TX_DESC_RTS_SHORT(pdesc,
+                       ((ptcb_desc->rts_rate <= DESC92C_RATE54M) ?
+                       (ptcb_desc->rts_use_shortpreamble ? 1 : 0) :
+                       (ptcb_desc->rts_use_shortgi ? 1 : 0)));
+
+               if (ptcb_desc->btx_enable_sw_calc_duration)
+                       SET_TX_DESC_NAV_USE_HDR(pdesc, 1);
+
+               if (bw_40) {
+                       if (ptcb_desc->packet_bw) {
+                               SET_TX_DESC_DATA_BW(pdesc, 1);
+                               SET_TX_DESC_TX_SUB_CARRIER(pdesc, 3);
+                       } else {
+                               SET_TX_DESC_DATA_BW(pdesc, 0);
+                               SET_TX_DESC_TX_SUB_CARRIER(pdesc,
+                                                  mac->cur_40_prime_sc);
+                       }
+               } else {
+                       SET_TX_DESC_DATA_BW(pdesc, 0);
+                       SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0);
+               }
+
+               SET_TX_DESC_LINIP(pdesc, 0);
+               SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb_len);
+               if (sta) {
+                       u8 ampdu_density = sta->ht_cap.ampdu_density;
+                       SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density);
+               }
+               if (info->control.hw_key) {
+                       struct ieee80211_key_conf *keyconf;
+                       keyconf = info->control.hw_key;
+                       switch (keyconf->cipher) {
+                       case WLAN_CIPHER_SUITE_WEP40:
+                       case WLAN_CIPHER_SUITE_WEP104:
+                       case WLAN_CIPHER_SUITE_TKIP:
+                               SET_TX_DESC_SEC_TYPE(pdesc, 0x1);
+                               break;
+                       case WLAN_CIPHER_SUITE_CCMP:
+                               SET_TX_DESC_SEC_TYPE(pdesc, 0x3);
+                               break;
+                       default:
+                               SET_TX_DESC_SEC_TYPE(pdesc, 0x0);
+                               break;
+                       }
+               }
+
+               SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel);
+               SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F);
+               SET_TX_DESC_RTS_RATE_FB_LIMIT(pdesc, 0xF);
+               SET_TX_DESC_DISABLE_FB(pdesc, ptcb_desc->disable_ratefallback ?
+                                      1 : 0);
+               SET_TX_DESC_USE_RATE(pdesc, ptcb_desc->use_driver_rate ? 1 : 0);
+
+               /* Set TxRate and RTSRate in TxDesc  */
+               /* This prevent Tx initial rate of new-coming packets */
+               /* from being overwritten by retried  packet rate.*/
+               if (!ptcb_desc->use_driver_rate) {
+                       /*SET_TX_DESC_RTS_RATE(pdesc, 0x08); */
+                       /* SET_TX_DESC_TX_RATE(pdesc, 0x0b); */
+               }
+               if (ieee80211_is_data_qos(fc)) {
+                       if (mac->rdg_en) {
+                               RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+                                        "Enable RDG function.\n");
+                               SET_TX_DESC_RDG_ENABLE(pdesc, 1);
+                               SET_TX_DESC_HTC(pdesc, 1);
+                       }
+               }
+       }
+
+       SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0));
+       SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0));
+       SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) buf_len);
+       SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping);
+       if (rtlpriv->dm.useramask) {
+               SET_TX_DESC_RATE_ID(pdesc, ptcb_desc->ratr_index);
+               SET_TX_DESC_MACID(pdesc, ptcb_desc->mac_id);
+       } else {
+               SET_TX_DESC_RATE_ID(pdesc, 0xC + ptcb_desc->ratr_index);
+               SET_TX_DESC_MACID(pdesc, ptcb_desc->ratr_index);
+       }
+       if (ieee80211_is_data_qos(fc))
+               SET_TX_DESC_QOS(pdesc, 1);
+
+       if (!ieee80211_is_data_qos(fc))
+               SET_TX_DESC_HWSEQ_EN(pdesc, 1);
+       SET_TX_DESC_MORE_FRAG(pdesc, (lastseg ? 0 : 1));
+       if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
+           is_broadcast_ether_addr(ieee80211_get_DA(hdr)))
+               SET_TX_DESC_BMC(pdesc, 1);
+
+       rtl88e_dm_set_tx_ant_by_tx_info(hw, pdesc, ptcb_desc->mac_id);
+       RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "\n");
+}
+
+void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw,
+                            u8 *pdesc, bool firstseg,
+                            bool lastseg, struct sk_buff *skb)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       u8 fw_queue = QSLT_BEACON;
+
+       dma_addr_t mapping = pci_map_single(rtlpci->pdev,
+                                           skb->data, skb->len,
+                                           PCI_DMA_TODEVICE);
+
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);
+       __le16 fc = hdr->frame_control;
+
+       if (pci_dma_mapping_error(rtlpci->pdev, mapping)) {
+               RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+                        "DMA mapping error");
+               return;
+       }
+       CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE);
+
+       if (firstseg)
+               SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN);
+
+       SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE1M);
+
+       SET_TX_DESC_SEQ(pdesc, 0);
+
+       SET_TX_DESC_LINIP(pdesc, 0);
+
+       SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue);
+
+       SET_TX_DESC_FIRST_SEG(pdesc, 1);
+       SET_TX_DESC_LAST_SEG(pdesc, 1);
+
+       SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)(skb->len));
+
+       SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping);
+
+       SET_TX_DESC_RATE_ID(pdesc, 7);
+       SET_TX_DESC_MACID(pdesc, 0);
+
+       SET_TX_DESC_OWN(pdesc, 1);
+
+       SET_TX_DESC_PKT_SIZE((u8 *)pdesc, (u16)(skb->len));
+
+       SET_TX_DESC_FIRST_SEG(pdesc, 1);
+       SET_TX_DESC_LAST_SEG(pdesc, 1);
+
+       SET_TX_DESC_OFFSET(pdesc, 0x20);
+
+       SET_TX_DESC_USE_RATE(pdesc, 1);
+
+       if (!ieee80211_is_data_qos(fc))
+               SET_TX_DESC_HWSEQ_EN(pdesc, 1);
+
+       RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD,
+                     "H2C Tx Cmd Content\n",
+                     pdesc, TX_DESC_SIZE);
+}
+
+void rtl88ee_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
+{
+       if (istx == true) {
+               switch (desc_name) {
+               case HW_DESC_OWN:
+                       SET_TX_DESC_OWN(pdesc, 1);
+                       break;
+               case HW_DESC_TX_NEXTDESC_ADDR:
+                       SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *)val);
+                       break;
+               default:
+                       RT_ASSERT(false, "ERR txdesc :%d not processed\n",
+                                 desc_name);
+                       break;
+               }
+       } else {
+               switch (desc_name) {
+               case HW_DESC_RXOWN:
+                       SET_RX_DESC_OWN(pdesc, 1);
+                       break;
+               case HW_DESC_RXBUFF_ADDR:
+                       SET_RX_DESC_BUFF_ADDR(pdesc, *(u32 *)val);
+                       break;
+               case HW_DESC_RXPKT_LEN:
+                       SET_RX_DESC_PKT_LEN(pdesc, *(u32 *)val);
+                       break;
+               case HW_DESC_RXERO:
+                       SET_RX_DESC_EOR(pdesc, 1);
+                       break;
+               default:
+                       RT_ASSERT(false, "ERR rxdesc :%d not processed\n",
+                                 desc_name);
+                       break;
+               }
+       }
+}
+
+u32 rtl88ee_get_desc(u8 *pdesc, bool istx, u8 desc_name)
+{
+       u32 ret = 0;
+
+       if (istx == true) {
+               switch (desc_name) {
+               case HW_DESC_OWN:
+                       ret = GET_TX_DESC_OWN(pdesc);
+                       break;
+               case HW_DESC_TXBUFF_ADDR:
+                       ret = GET_TX_DESC_TX_BUFFER_ADDRESS(pdesc);
+                       break;
+               default:
+                       RT_ASSERT(false, "ERR txdesc :%d not processed\n",
+                                 desc_name);
+                       break;
+               }
+       } else {
+               switch (desc_name) {
+               case HW_DESC_OWN:
+                       ret = GET_RX_DESC_OWN(pdesc);
+                       break;
+               case HW_DESC_RXPKT_LEN:
+                       ret = GET_RX_DESC_PKT_LEN(pdesc);
+                       break;
+               default:
+                       RT_ASSERT(false, "ERR rxdesc :%d not processed\n",
+                                 desc_name);
+                       break;
+               }
+       }
+       return ret;
+}
+
+void rtl88ee_tx_polling(struct ieee80211_hw *hw, u8 hw_queue)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       if (hw_queue == BEACON_QUEUE) {
+               rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(4));
+       } else {
+               rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG,
+                              BIT(0) << (hw_queue));
+       }
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h
new file mode 100644 (file)
index 0000000..d3a02e7
--- /dev/null
@@ -0,0 +1,795 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013  Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL92CE_TRX_H__
+#define __RTL92CE_TRX_H__
+
+#define TX_DESC_SIZE                           64
+#define TX_DESC_AGGR_SUBFRAME_SIZE             32
+
+#define RX_DESC_SIZE                           32
+#define RX_DRV_INFO_SIZE_UNIT                  8
+
+#define        TX_DESC_NEXT_DESC_OFFSET                40
+#define USB_HWDESC_HEADER_LEN                  32
+#define CRCLENGTH                              4
+
+#define SET_TX_DESC_PKT_SIZE(__pdesc, __val)           \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 0, 16, __val)
+#define SET_TX_DESC_OFFSET(__pdesc, __val)             \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 16, 8, __val)
+#define SET_TX_DESC_BMC(__pdesc, __val)                        \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 24, 1, __val)
+#define SET_TX_DESC_HTC(__pdesc, __val)                        \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 25, 1, __val)
+#define SET_TX_DESC_LAST_SEG(__pdesc, __val)           \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 26, 1, __val)
+#define SET_TX_DESC_FIRST_SEG(__pdesc, __val)          \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 27, 1, __val)
+#define SET_TX_DESC_LINIP(__pdesc, __val)              \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 28, 1, __val)
+#define SET_TX_DESC_NO_ACM(__pdesc, __val)             \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 29, 1, __val)
+#define SET_TX_DESC_GF(__pdesc, __val)                 \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 30, 1, __val)
+#define SET_TX_DESC_OWN(__pdesc, __val)                        \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 31, 1, __val)
+
+#define GET_TX_DESC_PKT_SIZE(__pdesc)                  \
+       LE_BITS_TO_4BYTE(__pdesc, 0, 16)
+#define GET_TX_DESC_OFFSET(__pdesc)                    \
+       LE_BITS_TO_4BYTE(__pdesc, 16, 8)
+#define GET_TX_DESC_BMC(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc, 24, 1)
+#define GET_TX_DESC_HTC(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc, 25, 1)
+#define GET_TX_DESC_LAST_SEG(__pdesc)                  \
+       LE_BITS_TO_4BYTE(__pdesc, 26, 1)
+#define GET_TX_DESC_FIRST_SEG(__pdesc)                 \
+       LE_BITS_TO_4BYTE(__pdesc, 27, 1)
+#define GET_TX_DESC_LINIP(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc, 28, 1)
+#define GET_TX_DESC_NO_ACM(__pdesc)                    \
+       LE_BITS_TO_4BYTE(__pdesc, 29, 1)
+#define GET_TX_DESC_GF(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc, 30, 1)
+#define GET_TX_DESC_OWN(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc, 31, 1)
+
+#define SET_TX_DESC_MACID(__pdesc, __val)              \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 0, 6, __val)
+#define SET_TX_DESC_QUEUE_SEL(__pdesc, __val)          \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 8, 5, __val)
+#define SET_TX_DESC_RDG_NAV_EXT(__pdesc, __val)                \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 13, 1, __val)
+#define SET_TX_DESC_LSIG_TXOP_EN(__pdesc, __val)       \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 14, 1, __val)
+#define SET_TX_DESC_PIFS(__pdesc, __val)               \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 15, 1, __val)
+#define SET_TX_DESC_RATE_ID(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 16, 4, __val)
+#define SET_TX_DESC_NAV_USE_HDR(__pdesc, __val)        \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 20, 1, __val)
+#define SET_TX_DESC_EN_DESC_ID(__pdesc, __val)         \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 21, 1, __val)
+#define SET_TX_DESC_SEC_TYPE(__pdesc, __val)           \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 22, 2, __val)
+#define SET_TX_DESC_PKT_OFFSET(__pdesc, __val)         \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 26, 5, __val)
+#define SET_TX_DESC_PADDING_LEN(__pdesc, __val)                \
+       SET_BITS_TO_LE_4BYTE(__pdesc+4, 24, 8, __val)
+
+#define GET_TX_DESC_MACID(__pdesc)                             \
+       LE_BITS_TO_4BYTE(__pdesc+4, 0, 5)
+#define GET_TX_DESC_AGG_ENABLE(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+4, 5, 1)
+#define GET_TX_DESC_AGG_BREAK(__pdesc)                         \
+       LE_BITS_TO_4BYTE(__pdesc+4, 6, 1)
+#define GET_TX_DESC_RDG_ENABLE(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+4, 7, 1)
+#define GET_TX_DESC_QUEUE_SEL(__pdesc)                         \
+       LE_BITS_TO_4BYTE(__pdesc+4, 8, 5)
+#define GET_TX_DESC_RDG_NAV_EXT(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc+4, 13, 1)
+#define GET_TX_DESC_LSIG_TXOP_EN(__pdesc)                      \
+       LE_BITS_TO_4BYTE(__pdesc+4, 14, 1)
+#define GET_TX_DESC_PIFS(__pdesc)                              \
+       LE_BITS_TO_4BYTE(__pdesc+4, 15, 1)
+#define GET_TX_DESC_RATE_ID(__pdesc)                           \
+       LE_BITS_TO_4BYTE(__pdesc+4, 16, 4)
+#define GET_TX_DESC_NAV_USE_HDR(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc+4, 20, 1)
+#define GET_TX_DESC_EN_DESC_ID(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+4, 21, 1)
+#define GET_TX_DESC_SEC_TYPE(__pdesc)                          \
+       LE_BITS_TO_4BYTE(__pdesc+4, 22, 2)
+#define GET_TX_DESC_PKT_OFFSET(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+4, 24, 8)
+
+#define SET_TX_DESC_RTS_RC(__pdesc, __val)                     \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 0, 6, __val)
+#define SET_TX_DESC_DATA_RC(__pdesc, __val)                    \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 6, 6, __val)
+#define SET_TX_DESC_AGG_ENABLE(__pdesc, __val)                 \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 12, 1, __val)
+#define SET_TX_DESC_RDG_ENABLE(__pdesc, __val)                 \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 13, 1, __val)
+#define SET_TX_DESC_BAR_RTY_TH(__pdesc, __val)                 \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 14, 2, __val)
+#define SET_TX_DESC_AGG_BREAK(__pdesc, __val)                  \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 16, 1, __val)
+#define SET_TX_DESC_MORE_FRAG(__pdesc, __val)                  \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 17, 1, __val)
+#define SET_TX_DESC_RAW(__pdesc, __val)                                \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 18, 1, __val)
+#define SET_TX_DESC_CCX(__pdesc, __val)                                \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 19, 1, __val)
+#define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val)              \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 20, 3, __val)
+#define SET_TX_DESC_BT_INT(__pdesc, __val)                     \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 23, 1, __val)
+#define SET_TX_DESC_ANTSEL_A(__pdesc, __val)                   \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 24, 1, __val)
+#define SET_TX_DESC_ANTSEL_B(__pdesc, __val)                   \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 25, 1, __val)
+#define SET_TX_DESC_TX_ANT_CCK(__pdesc, __val)                 \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 26, 2, __val)
+#define SET_TX_DESC_TX_ANTL(__pdesc, __val)                    \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 28, 2, __val)
+#define SET_TX_DESC_TX_ANT_HT(__pdesc, __val)                  \
+       SET_BITS_TO_LE_4BYTE(__pdesc+8, 30, 2, __val)
+
+#define GET_TX_DESC_RTS_RC(__pdesc)                            \
+       LE_BITS_TO_4BYTE(__pdesc+8, 0, 6)
+#define GET_TX_DESC_DATA_RC(__pdesc)                           \
+       LE_BITS_TO_4BYTE(__pdesc+8, 6, 6)
+#define GET_TX_DESC_BAR_RTY_TH(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+8, 14, 2)
+#define GET_TX_DESC_MORE_FRAG(__pdesc)                         \
+       LE_BITS_TO_4BYTE(__pdesc+8, 17, 1)
+#define GET_TX_DESC_RAW(__pdesc)                               \
+       LE_BITS_TO_4BYTE(__pdesc+8, 18, 1)
+#define GET_TX_DESC_CCX(__pdesc)                               \
+       LE_BITS_TO_4BYTE(__pdesc+8, 19, 1)
+#define GET_TX_DESC_AMPDU_DENSITY(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc+8, 20, 3)
+#define GET_TX_DESC_ANTSEL_A(__pdesc)                          \
+       LE_BITS_TO_4BYTE(__pdesc+8, 24, 1)
+#define GET_TX_DESC_ANTSEL_B(__pdesc)                          \
+       LE_BITS_TO_4BYTE(__pdesc+8, 25, 1)
+#define GET_TX_DESC_TX_ANT_CCK(__pdesc)                        \
+       LE_BITS_TO_4BYTE(__pdesc+8, 26, 2)
+#define GET_TX_DESC_TX_ANTL(__pdesc)                           \
+       LE_BITS_TO_4BYTE(__pdesc+8, 28, 2)
+#define GET_TX_DESC_TX_ANT_HT(__pdesc)                         \
+       LE_BITS_TO_4BYTE(__pdesc+8, 30, 2)
+
+#define SET_TX_DESC_NEXT_HEAP_PAGE(__pdesc, __val)     \
+       SET_BITS_TO_LE_4BYTE(__pdesc+12, 0, 8, __val)
+#define SET_TX_DESC_TAIL_PAGE(__pdesc, __val)          \
+       SET_BITS_TO_LE_4BYTE(__pdesc+12, 8, 8, __val)
+#define SET_TX_DESC_SEQ(__pdesc, __val)                        \
+       SET_BITS_TO_LE_4BYTE(__pdesc+12, 16, 12, __val)
+#define SET_TX_DESC_CPU_HANDLE(__pdesc, __val)                 \
+       SET_BITS_TO_LE_4BYTE(__pdesc+12, 28, 1, __val)
+#define SET_TX_DESC_TAG1(__pdesc, __val)                       \
+       SET_BITS_TO_LE_4BYTE(__pdesc+12, 29, 1, __val)
+#define SET_TX_DESC_TRIGGER_INT(__pdesc, __val)                        \
+       SET_BITS_TO_LE_4BYTE(__pdesc+12, 30, 1, __val)
+#define SET_TX_DESC_HWSEQ_EN(__pdesc, __val)                   \
+       SET_BITS_TO_LE_4BYTE(__pdesc+12, 31, 1, __val)
+
+
+#define GET_TX_DESC_NEXT_HEAP_PAGE(__pdesc)            \
+       LE_BITS_TO_4BYTE(__pdesc+12, 0, 8)
+#define GET_TX_DESC_TAIL_PAGE(__pdesc)                         \
+       LE_BITS_TO_4BYTE(__pdesc+12, 8, 8)
+#define GET_TX_DESC_SEQ(__pdesc)                                       \
+       LE_BITS_TO_4BYTE(__pdesc+12, 16, 12)
+
+
+#define SET_TX_DESC_RTS_RATE(__pdesc, __val)           \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 0, 5, __val)
+#define SET_TX_DESC_AP_DCFE(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 5, 1, __val)
+#define SET_TX_DESC_QOS(__pdesc, __val)                        \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 6, 1, __val)
+#define SET_TX_DESC_HWSEQ_SSN(__pdesc, __val)          \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 7, 1, __val)
+#define SET_TX_DESC_USE_RATE(__pdesc, __val)           \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 8, 1, __val)
+#define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val)     \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 9, 1, __val)
+#define SET_TX_DESC_DISABLE_FB(__pdesc, __val)         \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 10, 1, __val)
+#define SET_TX_DESC_CTS2SELF(__pdesc, __val)           \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 11, 1, __val)
+#define SET_TX_DESC_RTS_ENABLE(__pdesc, __val)         \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 12, 1, __val)
+#define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val)      \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 13, 1, __val)
+#define SET_TX_DESC_PORT_ID(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 14, 1, __val)
+#define SET_TX_DESC_PWR_STATUS(__pdesc, __val)         \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 15, 3, __val)
+#define SET_TX_DESC_WAIT_DCTS(__pdesc, __val)          \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 18, 1, __val)
+#define SET_TX_DESC_CTS2AP_EN(__pdesc, __val)          \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 19, 1, __val)
+#define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val)     \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 20, 2, __val)
+#define SET_TX_DESC_TX_STBC(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 22, 2, __val)
+#define SET_TX_DESC_DATA_SHORT(__pdesc, __val)         \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 24, 1, __val)
+#define SET_TX_DESC_DATA_BW(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 25, 1, __val)
+#define SET_TX_DESC_RTS_SHORT(__pdesc, __val)          \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 26, 1, __val)
+#define SET_TX_DESC_RTS_BW(__pdesc, __val)             \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 27, 1, __val)
+#define SET_TX_DESC_RTS_SC(__pdesc, __val)             \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 28, 2, __val)
+#define SET_TX_DESC_RTS_STBC(__pdesc, __val)           \
+       SET_BITS_TO_LE_4BYTE(__pdesc+16, 30, 2, __val)
+
+#define GET_TX_DESC_RTS_RATE(__pdesc)                  \
+       LE_BITS_TO_4BYTE(__pdesc+16, 0, 5)
+#define GET_TX_DESC_AP_DCFE(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc+16, 5, 1)
+#define GET_TX_DESC_QOS(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc+16, 6, 1)
+#define GET_TX_DESC_HWSEQ_EN(__pdesc)                  \
+       LE_BITS_TO_4BYTE(__pdesc+16, 7, 1)
+#define GET_TX_DESC_USE_RATE(__pdesc)                  \
+       LE_BITS_TO_4BYTE(__pdesc+16, 8, 1)
+#define GET_TX_DESC_DISABLE_RTS_FB(__pdesc)            \
+       LE_BITS_TO_4BYTE(__pdesc+16, 9, 1)
+#define GET_TX_DESC_DISABLE_FB(__pdesc)                        \
+       LE_BITS_TO_4BYTE(__pdesc+16, 10, 1)
+#define GET_TX_DESC_CTS2SELF(__pdesc)                  \
+       LE_BITS_TO_4BYTE(__pdesc+16, 11, 1)
+#define GET_TX_DESC_RTS_ENABLE(__pdesc)                        \
+       LE_BITS_TO_4BYTE(__pdesc+16, 12, 1)
+#define GET_TX_DESC_HW_RTS_ENABLE(__pdesc)             \
+       LE_BITS_TO_4BYTE(__pdesc+16, 13, 1)
+#define GET_TX_DESC_PORT_ID(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc+16, 14, 1)
+#define GET_TX_DESC_WAIT_DCTS(__pdesc)                 \
+       LE_BITS_TO_4BYTE(__pdesc+16, 18, 1)
+#define GET_TX_DESC_CTS2AP_EN(__pdesc)                 \
+       LE_BITS_TO_4BYTE(__pdesc+16, 19, 1)
+#define GET_TX_DESC_TX_SUB_CARRIER(__pdesc)            \
+       LE_BITS_TO_4BYTE(__pdesc+16, 20, 2)
+#define GET_TX_DESC_TX_STBC(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc+16, 22, 2)
+#define GET_TX_DESC_DATA_SHORT(__pdesc)                        \
+       LE_BITS_TO_4BYTE(__pdesc+16, 24, 1)
+#define GET_TX_DESC_DATA_BW(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc+16, 25, 1)
+#define GET_TX_DESC_RTS_SHORT(__pdesc)                 \
+       LE_BITS_TO_4BYTE(__pdesc+16, 26, 1)
+#define GET_TX_DESC_RTS_BW(__pdesc)                    \
+       LE_BITS_TO_4BYTE(__pdesc+16, 27, 1)
+#define GET_TX_DESC_RTS_SC(__pdesc)                    \
+       LE_BITS_TO_4BYTE(__pdesc+16, 28, 2)
+#define GET_TX_DESC_RTS_STBC(__pdesc)                  \
+       LE_BITS_TO_4BYTE(__pdesc+16, 30, 2)
+
+#define SET_TX_DESC_TX_RATE(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+20, 0, 6, __val)
+#define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val)       \
+       SET_BITS_TO_LE_4BYTE(__pdesc+20, 6, 1, __val)
+#define SET_TX_DESC_CCX_TAG(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+20, 7, 1, __val)
+#define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \
+       SET_BITS_TO_LE_4BYTE(__pdesc+20, 8, 5, __val)
+#define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val)  \
+       SET_BITS_TO_LE_4BYTE(__pdesc+20, 13, 4, __val)
+#define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \
+       SET_BITS_TO_LE_4BYTE(__pdesc+20, 17, 1, __val)
+#define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val)   \
+       SET_BITS_TO_LE_4BYTE(__pdesc+20, 18, 6, __val)
+#define SET_TX_DESC_USB_TXAGG_NUM(__pdesc, __val)      \
+       SET_BITS_TO_LE_4BYTE(__pdesc+20, 24, 8, __val)
+
+#define GET_TX_DESC_TX_RATE(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc+20, 0, 6)
+#define GET_TX_DESC_DATA_SHORTGI(__pdesc)              \
+       LE_BITS_TO_4BYTE(__pdesc+20, 6, 1)
+#define GET_TX_DESC_CCX_TAG(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc+20, 7, 1)
+#define GET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc)                \
+       LE_BITS_TO_4BYTE(__pdesc+20, 8, 5)
+#define GET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc)         \
+       LE_BITS_TO_4BYTE(__pdesc+20, 13, 4)
+#define GET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc)                \
+       LE_BITS_TO_4BYTE(__pdesc+20, 17, 1)
+#define GET_TX_DESC_DATA_RETRY_LIMIT(__pdesc)          \
+       LE_BITS_TO_4BYTE(__pdesc+20, 18, 6)
+#define GET_TX_DESC_USB_TXAGG_NUM(__pdesc)             \
+       LE_BITS_TO_4BYTE(__pdesc+20, 24, 8)
+
+#define SET_TX_DESC_TXAGC_A(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 0, 5, __val)
+#define SET_TX_DESC_TXAGC_B(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 5, 5, __val)
+#define SET_TX_DESC_USE_MAX_LEN(__pdesc, __val)                \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 10, 1, __val)
+#define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val)                \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 11, 5, __val)
+#define SET_TX_DESC_MCSG1_MAX_LEN(__pdesc, __val)      \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 16, 4, __val)
+#define SET_TX_DESC_MCSG2_MAX_LEN(__pdesc, __val)      \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 20, 4, __val)
+#define SET_TX_DESC_MCSG3_MAX_LEN(__pdesc, __val)      \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 24, 4, __val)
+#define SET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc, __val)   \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 28, 4, __val)
+
+#define GET_TX_DESC_TXAGC_A(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc+24, 0, 5)
+#define GET_TX_DESC_TXAGC_B(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc+24, 5, 5)
+#define GET_TX_DESC_USE_MAX_LEN(__pdesc)               \
+       LE_BITS_TO_4BYTE(__pdesc+24, 10, 1)
+#define GET_TX_DESC_MAX_AGG_NUM(__pdesc)               \
+       LE_BITS_TO_4BYTE(__pdesc+24, 11, 5)
+#define GET_TX_DESC_MCSG1_MAX_LEN(__pdesc)             \
+       LE_BITS_TO_4BYTE(__pdesc+24, 16, 4)
+#define GET_TX_DESC_MCSG2_MAX_LEN(__pdesc)             \
+       LE_BITS_TO_4BYTE(__pdesc+24, 20, 4)
+#define GET_TX_DESC_MCSG3_MAX_LEN(__pdesc)             \
+       LE_BITS_TO_4BYTE(__pdesc+24, 24, 4)
+#define GET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc)          \
+       LE_BITS_TO_4BYTE(__pdesc+24, 28, 4)
+
+#define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val)     \
+       SET_BITS_TO_LE_4BYTE(__pdesc+28, 0, 16, __val)
+#define SET_TX_DESC_SW_OFFSET30(__pdesc, __val)                \
+       SET_BITS_TO_LE_4BYTE(__pdesc+28, 16, 8, __val)
+#define SET_TX_DESC_SW_OFFSET31(__pdesc, __val)                \
+       SET_BITS_TO_LE_4BYTE(__pdesc+28, 24, 4, __val)
+#define SET_TX_DESC_ANTSEL_C(__pdesc, __val)           \
+       SET_BITS_TO_LE_4BYTE(__pdesc+28, 29, 1, __val)
+#define SET_TX_DESC_NULL_0(__pdesc, __val)             \
+       SET_BITS_TO_LE_4BYTE(__pdesc+28, 30, 1, __val)
+#define SET_TX_DESC_NULL_1(__pdesc, __val)             \
+       SET_BITS_TO_LE_4BYTE(__pdesc+28, 30, 1, __val)
+
+#define GET_TX_DESC_TX_BUFFER_SIZE(__pdesc)            \
+       LE_BITS_TO_4BYTE(__pdesc+28, 0, 16)
+
+
+#define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val)  \
+       SET_BITS_TO_LE_4BYTE(__pdesc+32, 0, 32, __val)
+#define SET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc, __val) \
+       SET_BITS_TO_LE_4BYTE(__pdesc+36, 0, 32, __val)
+
+#define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc)         \
+       LE_BITS_TO_4BYTE(__pdesc+32, 0, 32)
+#define GET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc)       \
+       LE_BITS_TO_4BYTE(__pdesc+36, 0, 32)
+
+#define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val)  \
+       SET_BITS_TO_LE_4BYTE(__pdesc+40, 0, 32, __val)
+#define SET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc, __val) \
+       SET_BITS_TO_LE_4BYTE(__pdesc+44, 0, 32, __val)
+
+#define GET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc)         \
+       LE_BITS_TO_4BYTE(__pdesc+40, 0, 32)
+#define GET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc)       \
+       LE_BITS_TO_4BYTE(__pdesc+44, 0, 32)
+
+#define GET_RX_DESC_PKT_LEN(__pdesc)                   \
+       LE_BITS_TO_4BYTE(__pdesc, 0, 14)
+#define GET_RX_DESC_CRC32(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc, 14, 1)
+#define GET_RX_DESC_ICV(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc, 15, 1)
+#define GET_RX_DESC_DRV_INFO_SIZE(__pdesc)             \
+       LE_BITS_TO_4BYTE(__pdesc, 16, 4)
+#define GET_RX_DESC_SECURITY(__pdesc)                  \
+       LE_BITS_TO_4BYTE(__pdesc, 20, 3)
+#define GET_RX_DESC_QOS(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc, 23, 1)
+#define GET_RX_DESC_SHIFT(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc, 24, 2)
+#define GET_RX_DESC_PHYST(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc, 26, 1)
+#define GET_RX_DESC_SWDEC(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc, 27, 1)
+#define GET_RX_DESC_LS(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc, 28, 1)
+#define GET_RX_DESC_FS(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc, 29, 1)
+#define GET_RX_DESC_EOR(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc, 30, 1)
+#define GET_RX_DESC_OWN(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc, 31, 1)
+
+#define SET_RX_DESC_PKT_LEN(__pdesc, __val)            \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 0, 14, __val)
+#define SET_RX_DESC_EOR(__pdesc, __val)                        \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 30, 1, __val)
+#define SET_RX_DESC_OWN(__pdesc, __val)                        \
+       SET_BITS_TO_LE_4BYTE(__pdesc, 31, 1, __val)
+
+#define GET_RX_DESC_MACID(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc+4, 0, 6)
+#define GET_RX_DESC_PAGGR(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc+4, 14, 1)
+#define GET_RX_DESC_FAGGR(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc+4, 15, 1)
+#define GET_RX_DESC_A1_FIT(__pdesc)                    \
+       LE_BITS_TO_4BYTE(__pdesc+4, 16, 4)
+#define GET_RX_DESC_A2_FIT(__pdesc)                    \
+       LE_BITS_TO_4BYTE(__pdesc+4, 20, 4)
+#define GET_RX_DESC_PAM(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc+4, 24, 1)
+#define GET_RX_DESC_PWR(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc+4, 25, 1)
+#define GET_RX_DESC_MD(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+4, 26, 1)
+#define GET_RX_DESC_MF(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+4, 27, 1)
+#define GET_RX_DESC_TYPE(__pdesc)                      \
+       LE_BITS_TO_4BYTE(__pdesc+4, 28, 2)
+#define GET_RX_DESC_MC(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+4, 30, 1)
+#define GET_RX_DESC_BC(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+4, 31, 1)
+#define GET_RX_DESC_SEQ(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc+8, 0, 12)
+#define GET_RX_DESC_FRAG(__pdesc)                      \
+       LE_BITS_TO_4BYTE(__pdesc+8, 12, 4)
+
+#define GET_RX_DESC_RXMCS(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc+12, 0, 6)
+#define GET_RX_DESC_RXHT(__pdesc)                      \
+       LE_BITS_TO_4BYTE(__pdesc+12, 6, 1)
+#define GET_RX_STATUS_DESC_RX_GF(__pdesc)              \
+       LE_BITS_TO_4BYTE(__pdesc+12, 7, 1)
+#define GET_RX_DESC_SPLCP(__pdesc)                     \
+       LE_BITS_TO_4BYTE(__pdesc+12, 8, 1)
+#define GET_RX_DESC_BW(__pdesc)                                \
+       LE_BITS_TO_4BYTE(__pdesc+12, 9, 1)
+#define GET_RX_DESC_HTC(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc+12, 10, 1)
+#define GET_RX_STATUS_DESC_EOSP(__pdesc)               \
+       LE_BITS_TO_4BYTE(__pdesc+12, 11, 1)
+#define GET_RX_STATUS_DESC_BSSID_FIT(__pdesc)          \
+       LE_BITS_TO_4BYTE(__pdesc+12, 12, 2)
+#define GET_RX_STATUS_DESC_RPT_SEL(__pdesc)            \
+       LE_BITS_TO_4BYTE(__pdesc+12, 14, 2)
+
+#define GET_RX_STATUS_DESC_PATTERN_MATCH(__pdesc)      \
+       LE_BITS_TO_4BYTE(__pdesc+12, 29, 1)
+#define GET_RX_STATUS_DESC_UNICAST_MATCH(__pdesc)      \
+       LE_BITS_TO_4BYTE(__pdesc+12, 30, 1)
+#define GET_RX_STATUS_DESC_MAGIC_MATCH(__pdesc)                \
+       LE_BITS_TO_4BYTE(__pdesc+12, 31, 1)
+
+#define GET_RX_DESC_IV1(__pdesc)                       \
+       LE_BITS_TO_4BYTE(__pdesc+16, 0, 32)
+#define GET_RX_DESC_TSFL(__pdesc)                      \
+       LE_BITS_TO_4BYTE(__pdesc+20, 0, 32)
+
+#define GET_RX_DESC_BUFF_ADDR(__pdesc)                 \
+       LE_BITS_TO_4BYTE(__pdesc+24, 0, 32)
+#define GET_RX_DESC_BUFF_ADDR64(__pdesc)               \
+       LE_BITS_TO_4BYTE(__pdesc+28, 0, 32)
+
+#define SET_RX_DESC_BUFF_ADDR(__pdesc, __val)  \
+       SET_BITS_TO_LE_4BYTE(__pdesc+24, 0, 32, __val)
+#define SET_RX_DESC_BUFF_ADDR64(__pdesc, __val) \
+       SET_BITS_TO_LE_4BYTE(__pdesc+28, 0, 32, __val)
+
+/* TX report 2 format in Rx desc*/
+
+#define GET_RX_RPT2_DESC_PKT_LEN(__status)     \
+       LE_BITS_TO_4BYTE(__status, 0, 9)
+#define GET_RX_RPT2_DESC_MACID_VALID_1(__status)       \
+       LE_BITS_TO_4BYTE(__status+16, 0, 32)
+#define GET_RX_RPT2_DESC_MACID_VALID_2(__status)       \
+       LE_BITS_TO_4BYTE(__status+20, 0, 32)
+
+#define SET_EARLYMODE_PKTNUM(__paddr, __value) \
+       SET_BITS_TO_LE_4BYTE(__paddr, 0, 4, __value)
+#define SET_EARLYMODE_LEN0(__paddr, __value)   \
+       SET_BITS_TO_LE_4BYTE(__paddr, 4, 12, __value)
+#define SET_EARLYMODE_LEN1(__paddr, __value)   \
+       SET_BITS_TO_LE_4BYTE(__paddr, 16, 12, __value)
+#define SET_EARLYMODE_LEN2_1(__paddr, __value) \
+       SET_BITS_TO_LE_4BYTE(__paddr, 28, 4, __value)
+#define SET_EARLYMODE_LEN2_2(__paddr, __value) \
+       SET_BITS_TO_LE_4BYTE(__paddr+4, 0, 8, __value)
+#define SET_EARLYMODE_LEN3(__paddr, __value)   \
+       SET_BITS_TO_LE_4BYTE(__paddr+4, 8, 12, __value)
+#define SET_EARLYMODE_LEN4(__paddr, __value)   \
+       SET_BITS_TO_LE_4BYTE(__paddr+4, 20, 12, __value)
+
+#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size)              \
+do {                                                           \
+       if (_size > TX_DESC_NEXT_DESC_OFFSET)                   \
+               memset(__pdesc, 0, TX_DESC_NEXT_DESC_OFFSET);   \
+       else                                                    \
+               memset(__pdesc, 0, _size);                      \
+} while (0)
+
+#define RTL8188_RX_HAL_IS_CCK_RATE(rxmcs)\
+       (rxmcs == DESC92C_RATE1M ||\
+        rxmcs == DESC92C_RATE2M ||\
+        rxmcs == DESC92C_RATE5_5M ||\
+        rxmcs == DESC92C_RATE11M)
+
+struct phy_rx_agc_info_t {
+       #if __LITTLE_ENDIAN
+               u8      gain:7, trsw:1;
+       #else
+               u8      trsw:1, gain:7;
+       #endif
+};
+struct phy_status_rpt {
+       struct phy_rx_agc_info_t path_agc[2];
+       u8      ch_corr[2];
+       u8      cck_sig_qual_ofdm_pwdb_all;
+       u8      cck_agc_rpt_ofdm_cfosho_a;
+       u8      cck_rpt_b_ofdm_cfosho_b;
+       u8      rsvd_1;
+       u8      noise_power_db_msb;
+       u8      path_cfotail[2];
+       u8      pcts_mask[2];
+       u8      stream_rxevm[2];
+       u8      path_rxsnr[2];
+       u8      noise_power_db_lsb;
+       u8      rsvd_2[3];
+       u8      stream_csi[2];
+       u8      stream_target_csi[2];
+       u8      sig_evm;
+       u8      rsvd_3;
+#if __LITTLE_ENDIAN
+       u8      antsel_rx_keep_2:1;     /*ex_intf_flg:1;*/
+       u8      sgi_en:1;
+       u8      rxsc:2;
+       u8      idle_long:1;
+       u8      r_ant_train_en:1;
+       u8      ant_sel_b:1;
+       u8      ant_sel:1;
+#else  /* _BIG_ENDIAN_ */
+       u8      ant_sel:1;
+       u8      ant_sel_b:1;
+       u8      r_ant_train_en:1;
+       u8      idle_long:1;
+       u8      rxsc:2;
+       u8      sgi_en:1;
+       u8      antsel_rx_keep_2:1;     /*ex_intf_flg:1;*/
+#endif
+} __packed;
+
+struct rx_fwinfo_88e {
+       u8 gain_trsw[4];
+       u8 pwdb_all;
+       u8 cfosho[4];
+       u8 cfotail[4];
+       char rxevm[2];
+       char rxsnr[4];
+       u8 pdsnr[2];
+       u8 csi_current[2];
+       u8 csi_target[2];
+       u8 sigevm;
+       u8 max_ex_pwr;
+       u8 ex_intf_flag:1;
+       u8 sgi_en:1;
+       u8 rxsc:2;
+       u8 reserve:4;
+} __packed;
+
+struct tx_desc_88e {
+       u32 pktsize:16;
+       u32 offset:8;
+       u32 bmc:1;
+       u32 htc:1;
+       u32 lastseg:1;
+       u32 firstseg:1;
+       u32 linip:1;
+       u32 noacm:1;
+       u32 gf:1;
+       u32 own:1;
+
+       u32 macid:6;
+       u32 rsvd0:2;
+       u32 queuesel:5;
+       u32 rd_nav_ext:1;
+       u32 lsig_txop_en:1;
+       u32 pifs:1;
+       u32 rateid:4;
+       u32 nav_usehdr:1;
+       u32 en_descid:1;
+       u32 sectype:2;
+       u32 pktoffset:8;
+
+       u32 rts_rc:6;
+       u32 data_rc:6;
+       u32 agg_en:1;
+       u32 rdg_en:1;
+       u32 bar_retryht:2;
+       u32 agg_break:1;
+       u32 morefrag:1;
+       u32 raw:1;
+       u32 ccx:1;
+       u32 ampdudensity:3;
+       u32 bt_int:1;
+       u32 ant_sela:1;
+       u32 ant_selb:1;
+       u32 txant_cck:2;
+       u32 txant_l:2;
+       u32 txant_ht:2;
+
+       u32 nextheadpage:8;
+       u32 tailpage:8;
+       u32 seq:12;
+       u32 cpu_handle:1;
+       u32 tag1:1;
+       u32 trigger_int:1;
+       u32 hwseq_en:1;
+
+       u32 rtsrate:5;
+       u32 apdcfe:1;
+       u32 qos:1;
+       u32 hwseq_ssn:1;
+       u32 userrate:1;
+       u32 dis_rtsfb:1;
+       u32 dis_datafb:1;
+       u32 cts2self:1;
+       u32 rts_en:1;
+       u32 hwrts_en:1;
+       u32 portid:1;
+       u32 pwr_status:3;
+       u32 waitdcts:1;
+       u32 cts2ap_en:1;
+       u32 txsc:2;
+       u32 stbc:2;
+       u32 txshort:1;
+       u32 txbw:1;
+       u32 rtsshort:1;
+       u32 rtsbw:1;
+       u32 rtssc:2;
+       u32 rtsstbc:2;
+
+       u32 txrate:6;
+       u32 shortgi:1;
+       u32 ccxt:1;
+       u32 txrate_fb_lmt:5;
+       u32 rtsrate_fb_lmt:4;
+       u32 retrylmt_en:1;
+       u32 txretrylmt:6;
+       u32 usb_txaggnum:8;
+
+       u32 txagca:5;
+       u32 txagcb:5;
+       u32 usemaxlen:1;
+       u32 maxaggnum:5;
+       u32 mcsg1maxlen:4;
+       u32 mcsg2maxlen:4;
+       u32 mcsg3maxlen:4;
+       u32 mcs7sgimaxlen:4;
+
+       u32 txbuffersize:16;
+       u32 sw_offset30:8;
+       u32 sw_offset31:4;
+       u32 rsvd1:1;
+       u32 antsel_c:1;
+       u32 null_0:1;
+       u32 null_1:1;
+
+       u32 txbuffaddr;
+       u32 txbufferaddr64;
+       u32 nextdescaddress;
+       u32 nextdescaddress64;
+
+       u32 reserve_pass_pcie_mm_limit[4];
+} __packed;
+
+struct rx_desc_88e {
+       u32 length:14;
+       u32 crc32:1;
+       u32 icverror:1;
+       u32 drv_infosize:4;
+       u32 security:3;
+       u32 qos:1;
+       u32 shift:2;
+       u32 phystatus:1;
+       u32 swdec:1;
+       u32 lastseg:1;
+       u32 firstseg:1;
+       u32 eor:1;
+       u32 own:1;
+
+       u32 macid:6;
+       u32 tid:4;
+       u32 hwrsvd:5;
+       u32 paggr:1;
+       u32 faggr:1;
+       u32 a1_fit:4;
+       u32 a2_fit:4;
+       u32 pam:1;
+       u32 pwr:1;
+       u32 moredata:1;
+       u32 morefrag:1;
+       u32 type:2;
+       u32 mc:1;
+       u32 bc:1;
+
+       u32 seq:12;
+       u32 frag:4;
+       u32 nextpktlen:14;
+       u32 nextind:1;
+       u32 rsvd:1;
+
+       u32 rxmcs:6;
+       u32 rxht:1;
+       u32 amsdu:1;
+       u32 splcp:1;
+       u32 bandwidth:1;
+       u32 htc:1;
+       u32 tcpchk_rpt:1;
+       u32 ipcchk_rpt:1;
+       u32 tcpchk_valid:1;
+       u32 hwpcerr:1;
+       u32 hwpcind:1;
+       u32 iv0:16;
+
+       u32 iv1;
+
+       u32 tsfl;
+
+       u32 bufferaddress;
+       u32 bufferaddress64;
+
+} __packed;
+
+void rtl88ee_tx_fill_desc(struct ieee80211_hw *hw,
+                         struct ieee80211_hdr *hdr, u8 *pdesc_tx,
+                         struct ieee80211_tx_info *info,
+                         struct ieee80211_sta *sta,
+                         struct sk_buff *skb,
+                         u8 hw_queue, struct rtl_tcb_desc *ptcb_desc);
+bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw,
+                          struct rtl_stats *status,
+                          struct ieee80211_rx_status *rx_status,
+                          u8 *pdesc, struct sk_buff *skb);
+void rtl88ee_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val);
+u32 rtl88ee_get_desc(u8 *pdesc, bool istx, u8 desc_name);
+void rtl88ee_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
+void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
+                            bool b_firstseg, bool b_lastseg,
+                            struct sk_buff *skb);
+
+#endif
index b793a659a46535e3689867d69f6ec28f6a76c400..926e2a34c7663961c306110717876e858f5f8a83 100644 (file)
@@ -174,8 +174,8 @@ static void rtl92c_dm_diginit(struct ieee80211_hw *hw)
        dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
        dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
        dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-       dm_digtable->rx_gain_range_max = DM_DIG_MAX;
-       dm_digtable->rx_gain_range_min = DM_DIG_MIN;
+       dm_digtable->rx_gain_max = DM_DIG_MAX;
+       dm_digtable->rx_gain_min = DM_DIG_MIN;
        dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
        dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
        dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
@@ -300,11 +300,11 @@ static void rtl92c_dm_ctrl_initgain_by_rssi(struct ieee80211_hw *hw)
        }
 
        if ((digtable->rssi_val_min + 10 - digtable->back_val) >
-           digtable->rx_gain_range_max)
-               digtable->cur_igvalue = digtable->rx_gain_range_max;
+           digtable->rx_gain_max)
+               digtable->cur_igvalue = digtable->rx_gain_max;
        else if ((digtable->rssi_val_min + 10 -
-                 digtable->back_val) < digtable->rx_gain_range_min)
-               digtable->cur_igvalue = digtable->rx_gain_range_min;
+                 digtable->back_val) < digtable->rx_gain_min)
+               digtable->cur_igvalue = digtable->rx_gain_min;
        else
                digtable->cur_igvalue = digtable->rssi_val_min + 10 -
                    digtable->back_val;
@@ -1147,75 +1147,6 @@ void rtl92c_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(rtl92c_dm_init_rate_adaptive_mask);
 
-static void rtl92c_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
-       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
-       struct rate_adaptive *p_ra = &(rtlpriv->ra);
-       u32 low_rssi_thresh, high_rssi_thresh;
-       struct ieee80211_sta *sta = NULL;
-
-       if (is_hal_stop(rtlhal)) {
-               RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
-                        "<---- driver is going to unload\n");
-               return;
-       }
-
-       if (!rtlpriv->dm.useramask) {
-               RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
-                        "<---- driver does not control rate adaptive mask\n");
-               return;
-       }
-
-       if (mac->link_state == MAC80211_LINKED &&
-           mac->opmode == NL80211_IFTYPE_STATION) {
-               switch (p_ra->pre_ratr_state) {
-               case DM_RATR_STA_HIGH:
-                       high_rssi_thresh = 50;
-                       low_rssi_thresh = 20;
-                       break;
-               case DM_RATR_STA_MIDDLE:
-                       high_rssi_thresh = 55;
-                       low_rssi_thresh = 20;
-                       break;
-               case DM_RATR_STA_LOW:
-                       high_rssi_thresh = 50;
-                       low_rssi_thresh = 25;
-                       break;
-               default:
-                       high_rssi_thresh = 50;
-                       low_rssi_thresh = 20;
-                       break;
-               }
-
-               if (rtlpriv->dm.undec_sm_pwdb > (long)high_rssi_thresh)
-                       p_ra->ratr_state = DM_RATR_STA_HIGH;
-               else if (rtlpriv->dm.undec_sm_pwdb > (long)low_rssi_thresh)
-                       p_ra->ratr_state = DM_RATR_STA_MIDDLE;
-               else
-                       p_ra->ratr_state = DM_RATR_STA_LOW;
-
-               if (p_ra->pre_ratr_state != p_ra->ratr_state) {
-                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, "RSSI = %ld\n",
-                                rtlpriv->dm.undec_sm_pwdb);
-                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
-                                "RSSI_LEVEL = %d\n", p_ra->ratr_state);
-                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
-                                "PreState = %d, CurState = %d\n",
-                                p_ra->pre_ratr_state, p_ra->ratr_state);
-
-                       rcu_read_lock();
-                       sta = ieee80211_find_sta(mac->vif, mac->bssid);
-                       rtlpriv->cfg->ops->update_rate_tbl(hw, sta,
-                                       p_ra->ratr_state);
-
-                       p_ra->pre_ratr_state = p_ra->ratr_state;
-                       rcu_read_unlock();
-               }
-       }
-}
-
 static void rtl92c_dm_init_dynamic_bb_powersaving(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1437,6 +1368,9 @@ void rtl92c_dm_watchdog(struct ieee80211_hw *hw)
        rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FWLPS_RF_ON,
                                      (u8 *) (&fw_ps_awake));
 
+       if (ppsc->p2p_ps_info.p2p_ps_mode)
+               fw_ps_awake = false;
+
        if ((ppsc->rfpwr_state == ERFON) && ((!fw_current_inpsmode) &&
                                             fw_ps_awake)
            && (!ppsc->rfchange_inprogress)) {
@@ -1446,7 +1380,7 @@ void rtl92c_dm_watchdog(struct ieee80211_hw *hw)
                rtl92c_dm_dynamic_bb_powersaving(hw);
                rtl92c_dm_dynamic_txpower(hw);
                rtl92c_dm_check_txpower_tracking(hw);
-               rtl92c_dm_refresh_rate_adaptive_mask(hw);
+               /* rtl92c_dm_refresh_rate_adaptive_mask(hw); */
                rtl92c_dm_bt_coexist(hw);
                rtl92c_dm_check_edca_turbo(hw);
        }
@@ -1651,7 +1585,7 @@ static void rtl92c_bt_set_normal(struct ieee80211_hw *hw)
        }
 }
 
-static void rtl92c_bt_ant_isolation(struct ieee80211_hw *hw)
+static void rtl92c_bt_ant_isolation(struct ieee80211_hw *hw, u8 tmp1byte)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
@@ -1673,9 +1607,9 @@ static void rtl92c_bt_ant_isolation(struct ieee80211_hw *hw)
                            BT_RSSI_STATE_SPECIAL_LOW)) {
                        rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0);
                } else if (rtlpcipriv->bt_coexist.bt_service == BT_PAN) {
-                       rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0x00);
+                       rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, tmp1byte);
                } else {
-                       rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0x00);
+                       rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, tmp1byte);
                }
        }
 
@@ -1726,12 +1660,17 @@ static void rtl92c_check_bt_change(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       u8 tmp1byte = 0;
 
+       if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version) &&
+           rtlpcipriv->bt_coexist.bt_coexistence)
+               tmp1byte |= BIT(5);
        if (rtlpcipriv->bt_coexist.bt_cur_state) {
                if (rtlpcipriv->bt_coexist.bt_ant_isolation)
-                       rtl92c_bt_ant_isolation(hw);
+                       rtl92c_bt_ant_isolation(hw, tmp1byte);
        } else {
-               rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0x00);
+               rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, tmp1byte);
                rtlpriv->cfg->ops->set_rfreg(hw, RF90_PATH_A, 0x1e, 0xf0,
                                rtlpcipriv->bt_coexist.bt_rfreg_origin_1e);
 
index 883f23ae95194f66fc416853a2a027a4a8a6f9a1..04a41628ceedd374ed12f86740ffe1f71d543f71 100644 (file)
@@ -552,7 +552,9 @@ void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
        RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
 
        SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode);
-       SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1);
+       SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode,
+                                        (rtlpriv->mac80211.p2p) ?
+                                        ppsc->smart_ps : 1);
        SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode,
                                              ppsc->reg_max_lps_awakeintvl);
 
@@ -808,3 +810,98 @@ void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus)
        rtl92c_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm);
 }
 EXPORT_SYMBOL(rtl92c_set_fw_joinbss_report_cmd);
+
+static void rtl92c_set_p2p_ctw_period_cmd(struct ieee80211_hw *hw, u8 ctwindow)
+{
+       u8 u1_ctwindow_period[1] = {ctwindow};
+
+       rtl92c_fill_h2c_cmd(hw, H2C_P2P_PS_CTW_CMD, 1, u1_ctwindow_period);
+}
+
+void rtl92c_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_p2p_ps_info *p2pinfo = &(rtlps->p2p_ps_info);
+       struct p2p_ps_offload_t *p2p_ps_offload = &rtlhal->p2p_ps_offload;
+       u8      i;
+       u16     ctwindow;
+       u32     start_time, tsf_low;
+
+       switch (p2p_ps_state) {
+       case P2P_PS_DISABLE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_DISABLE\n");
+               memset(p2p_ps_offload, 0, sizeof(struct p2p_ps_offload_t));
+               break;
+       case P2P_PS_ENABLE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_ENABLE\n");
+               /* update CTWindow value. */
+               if (p2pinfo->ctwindow > 0) {
+                       p2p_ps_offload->ctwindow_en = 1;
+                       ctwindow = p2pinfo->ctwindow;
+                       rtl92c_set_p2p_ctw_period_cmd(hw, ctwindow);
+               }
+               /* hw only support 2 set of NoA */
+               for (i = 0; i < p2pinfo->noa_num; i++) {
+                       /* To control the register setting for which NOA*/
+                       rtl_write_byte(rtlpriv, 0x5cf, (i << 4));
+                       if (i == 0)
+                               p2p_ps_offload->noa0_en = 1;
+                       else
+                               p2p_ps_offload->noa1_en = 1;
+
+                       /* config P2P NoA Descriptor Register */
+                       rtl_write_dword(rtlpriv, 0x5E0,
+                                       p2pinfo->noa_duration[i]);
+                       rtl_write_dword(rtlpriv, 0x5E4,
+                                       p2pinfo->noa_interval[i]);
+
+                       /*Get Current TSF value */
+                       tsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
+
+                       start_time = p2pinfo->noa_start_time[i];
+                       if (p2pinfo->noa_count_type[i] != 1) {
+                               while (start_time <= (tsf_low+(50*1024))) {
+                                       start_time += p2pinfo->noa_interval[i];
+                                       if (p2pinfo->noa_count_type[i] != 255)
+                                               p2pinfo->noa_count_type[i]--;
+                               }
+                       }
+                       rtl_write_dword(rtlpriv, 0x5E8, start_time);
+                       rtl_write_dword(rtlpriv, 0x5EC,
+                                       p2pinfo->noa_count_type[i]);
+               }
+
+               if ((p2pinfo->opp_ps == 1) || (p2pinfo->noa_num > 0)) {
+                       /* rst p2p circuit */
+                       rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, BIT(4));
+
+                       p2p_ps_offload->offload_en = 1;
+
+                       if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) {
+                               p2p_ps_offload->role = 1;
+                               p2p_ps_offload->allstasleep = 0;
+                       } else {
+                               p2p_ps_offload->role = 0;
+                       }
+
+                       p2p_ps_offload->discovery = 0;
+               }
+               break;
+       case P2P_PS_SCAN:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN\n");
+               p2p_ps_offload->discovery = 1;
+               break;
+       case P2P_PS_SCAN_DONE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN_DONE\n");
+               p2p_ps_offload->discovery = 0;
+               p2pinfo->p2p_ps_state = P2P_PS_ENABLE;
+               break;
+       default:
+               break;
+       }
+
+       rtl92c_fill_h2c_cmd(hw, H2C_P2P_PS_OFFLOAD, 1, (u8 *)p2p_ps_offload);
+}
+EXPORT_SYMBOL_GPL(rtl92c_set_p2p_ps_offload_cmd);
index 780ea5b1e24c4ae37e5b281b6c9fad6492c2a773..15b2055e6212c4401347dbc873f9f49628fe2d3f 100644 (file)
@@ -67,6 +67,9 @@ enum rtl8192c_h2c_cmd {
        H2C_RSVDPAGE = 3,
        H2C_RSSI_REPORT = 5,
        H2C_RA_MASK = 6,
+       H2C_MACID_PS_MODE = 7,
+       H2C_P2P_PS_OFFLOAD = 8,
+       H2C_P2P_PS_CTW_CMD = 32,
        MAX_H2CCMD
 };
 
@@ -95,5 +98,6 @@ void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
 void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
 void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
 void usb_writeN_async(struct rtl_priv *rtlpriv, u32 addr, void *data, u16 len);
+void rtl92c_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state);
 
 #endif
index 1b65db7fd65156e8169f59d657723adc0dbb3ac5..a82b30a1996ca97cc5041ac47899171fef15e25e 100644 (file)
@@ -475,6 +475,9 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
 
                        break;
                }
+       case HW_VAR_H2C_FW_P2P_PS_OFFLOAD:
+               rtl92c_set_p2p_ps_offload_cmd(hw, (*(u8 *)val));
+               break;
        case HW_VAR_AID:{
                        u16 u2btmp;
                        u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT);
@@ -505,6 +508,40 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                        break;
 
                }
+       case HW_VAR_FW_LPS_ACTION: {
+                       bool enter_fwlps = *((bool *)val);
+                       u8 rpwm_val, fw_pwrmode;
+                       bool fw_current_inps;
+
+                       if (enter_fwlps) {
+                               rpwm_val = 0x02;        /* RF off */
+                               fw_current_inps = true;
+                               rtlpriv->cfg->ops->set_hw_reg(hw,
+                                               HW_VAR_FW_PSMODE_STATUS,
+                                               (u8 *)(&fw_current_inps));
+                               rtlpriv->cfg->ops->set_hw_reg(hw,
+                                               HW_VAR_H2C_FW_PWRMODE,
+                                               (u8 *)(&ppsc->fwctrl_psmode));
+
+                               rtlpriv->cfg->ops->set_hw_reg(hw,
+                                               HW_VAR_SET_RPWM,
+                                               (u8 *)(&rpwm_val));
+                       } else {
+                               rpwm_val = 0x0C;        /* RF on */
+                               fw_pwrmode = FW_PS_ACTIVE_MODE;
+                               fw_current_inps = false;
+                               rtlpriv->cfg->ops->set_hw_reg(hw,
+                                               HW_VAR_SET_RPWM,
+                                               (u8 *)(&rpwm_val));
+                               rtlpriv->cfg->ops->set_hw_reg(hw,
+                                               HW_VAR_H2C_FW_PWRMODE,
+                                               (u8 *)(&fw_pwrmode));
+
+                               rtlpriv->cfg->ops->set_hw_reg(hw,
+                                               HW_VAR_FW_PSMODE_STATUS,
+                                               (u8 *)(&fw_current_inps));
+                       }
+               break; }
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         "switch case not processed\n");
@@ -1105,7 +1142,8 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw,
            type == NL80211_IFTYPE_STATION) {
                _rtl92ce_stop_tx_beacon(hw);
                _rtl92ce_enable_bcn_sub_func(hw);
-       } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP) {
+       } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP ||
+                  type == NL80211_IFTYPE_MESH_POINT) {
                _rtl92ce_resume_tx_beacon(hw);
                _rtl92ce_disable_bcn_sub_func(hw);
        } else {
@@ -1137,6 +1175,11 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw,
                RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
                         "Set Network type to AP!\n");
                break;
+       case NL80211_IFTYPE_MESH_POINT:
+               bt_msr |= MSR_ADHOC;
+               RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+                        "Set Network type to Mesh Point!\n");
+               break;
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         "Network type %d not supported!\n", type);
@@ -1184,7 +1227,8 @@ int rtl92ce_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type)
                return -EOPNOTSUPP;
 
        if (rtlpriv->mac80211.link_state == MAC80211_LINKED) {
-               if (type != NL80211_IFTYPE_AP)
+               if (type != NL80211_IFTYPE_AP &&
+                   type != NL80211_IFTYPE_MESH_POINT)
                        rtl92ce_set_check_bssid(hw, true);
        } else {
                rtl92ce_set_check_bssid(hw, false);
@@ -1459,7 +1503,7 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                }
 
                for (i = 0; i < 14; i++) {
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF(%d)-Ch(%d) [CCK / HT40_1S / HT40_2S] = [0x%x / 0x%x / 0x%x]\n",
                                rf_path, i,
                                rtlefuse->txpwrlevel_cck[rf_path][i],
@@ -1500,11 +1544,11 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                                      & 0xf0) >> 4);
                        }
 
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF-%d pwrgroup_ht20[%d] = 0x%x\n",
                                rf_path, i,
                                rtlefuse->pwrgroup_ht20[rf_path][i]);
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF-%d pwrgroup_ht40[%d] = 0x%x\n",
                                rf_path, i,
                                rtlefuse->pwrgroup_ht40[rf_path][i]);
@@ -1545,19 +1589,19 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
            rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][7];
 
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-A Ht20 to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_ht20diff[RF90_PATH_A][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-A Legacy to Ht40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-B Ht20 to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_ht20diff[RF90_PATH_B][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-B Legacy to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]);
 
@@ -1565,7 +1609,7 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                rtlefuse->eeprom_regulatory = (hwinfo[RF_OPTION1] & 0x7);
        else
                rtlefuse->eeprom_regulatory = 0;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory);
 
        if (!autoload_fail) {
@@ -1575,7 +1619,7 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                rtlefuse->eeprom_tssi[RF90_PATH_A] = EEPROM_DEFAULT_TSSI;
                rtlefuse->eeprom_tssi[RF90_PATH_B] = EEPROM_DEFAULT_TSSI;
        }
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower, "TSSI_A = 0x%x, TSSI_B = 0x%x\n",
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER, "TSSI_A = 0x%x, TSSI_B = 0x%x\n",
                rtlefuse->eeprom_tssi[RF90_PATH_A],
                rtlefuse->eeprom_tssi[RF90_PATH_B]);
 
@@ -1589,7 +1633,7 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                rtlefuse->apk_thermalmeterignore = true;
 
        rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter);
 }
 
@@ -1629,6 +1673,21 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw)
        if (rtlefuse->autoload_failflag)
                return;
 
+       rtlefuse->eeprom_vid = *(u16 *)&hwinfo[EEPROM_VID];
+       rtlefuse->eeprom_did = *(u16 *)&hwinfo[EEPROM_DID];
+       rtlefuse->eeprom_svid = *(u16 *)&hwinfo[EEPROM_SVID];
+       rtlefuse->eeprom_smid = *(u16 *)&hwinfo[EEPROM_SMID];
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROMId = 0x%4x\n", eeprom_id);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM VID = 0x%4x\n", rtlefuse->eeprom_vid);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM DID = 0x%4x\n", rtlefuse->eeprom_did);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM SVID = 0x%4x\n", rtlefuse->eeprom_svid);
+       RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+                "EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid);
+
        for (i = 0; i < 6; i += 2) {
                usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i];
                *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue;
@@ -1766,6 +1825,9 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw,
                ratr_value = sta->supp_rates[1] << 4;
        else
                ratr_value = sta->supp_rates[0];
+       if (mac->opmode == NL80211_IFTYPE_ADHOC)
+               ratr_value = 0xfff;
+
        ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
                        sta->ht_cap.mcs.rx_mask[0] << 12);
        switch (wirelessmode) {
@@ -1860,7 +1922,8 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw,
 
        sta_entry = (struct rtl_sta_info *) sta->drv_priv;
        wirelessmode = sta_entry->wireless_mode;
-       if (mac->opmode == NL80211_IFTYPE_STATION)
+       if (mac->opmode == NL80211_IFTYPE_STATION ||
+           mac->opmode == NL80211_IFTYPE_MESH_POINT)
                curtxbw_40mhz = mac->bw_40;
        else if (mac->opmode == NL80211_IFTYPE_AP ||
                mac->opmode == NL80211_IFTYPE_ADHOC)
@@ -1870,6 +1933,8 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw,
                ratr_bitmap = sta->supp_rates[1] << 4;
        else
                ratr_bitmap = sta->supp_rates[0];
+       if (mac->opmode == NL80211_IFTYPE_ADHOC)
+               ratr_bitmap = 0xfff;
        ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
                        sta->ht_cap.mcs.rx_mask[0] << 12);
        switch (wirelessmode) {
@@ -2135,7 +2200,8 @@ void rtl92ce_set_key(struct ieee80211_hw *hw, u32 key_index,
                                macaddr = cam_const_broad;
                                entry_id = key_index;
                        } else {
-                               if (mac->opmode == NL80211_IFTYPE_AP) {
+                               if (mac->opmode == NL80211_IFTYPE_AP ||
+                                   mac->opmode == NL80211_IFTYPE_MESH_POINT) {
                                        entry_id = rtl_cam_get_free_entry(hw,
                                                                 p_macaddr);
                                        if (entry_id >=  TOTAL_CAM_ENTRY) {
@@ -2157,7 +2223,8 @@ void rtl92ce_set_key(struct ieee80211_hw *hw, u32 key_index,
                        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
                                 "delete one entry, entry_id is %d\n",
                                 entry_id);
-                       if (mac->opmode == NL80211_IFTYPE_AP)
+                       if (mac->opmode == NL80211_IFTYPE_AP ||
+                           mac->opmode == NL80211_IFTYPE_MESH_POINT)
                                rtl_cam_del_entry(hw, p_macaddr);
                        rtl_cam_delete_one_entry(hw, p_macaddr, entry_id);
                } else {
@@ -2338,3 +2405,24 @@ void rtl92ce_suspend(struct ieee80211_hw *hw)
 void rtl92ce_resume(struct ieee80211_hw *hw)
 {
 }
+
+/* Turn on AAP (RCR:bit 0) for promicuous mode. */
+void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw,
+       bool allow_all_da, bool write_into_reg)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       if (allow_all_da) {/* Set BIT0 */
+               rtlpci->receive_config |= RCR_AAP;
+       } else {/* Clear BIT0 */
+               rtlpci->receive_config &= ~RCR_AAP;
+       }
+
+       if (write_into_reg)
+               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
+
+       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
+                "receive_config=0x%08X, write_into_reg=%d\n",
+                rtlpci->receive_config, write_into_reg);
+}
index 52a3aea9b3deecf8052293010ab3d95947d5cfb9..2d063b0c77609a608adc8d831bde0d946263a7a1 100644 (file)
@@ -59,6 +59,8 @@ void rtl92ce_set_beacon_interval(struct ieee80211_hw *hw);
 void rtl92ce_update_interrupt_mask(struct ieee80211_hw *hw,
                                   u32 add_msr, u32 rm_msr);
 void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val);
+void rtl92ce_update_hal_rate_tbl(struct ieee80211_hw *hw,
+                                struct ieee80211_sta *sta, u8 rssi_level);
 void rtl92ce_update_hal_rate_tbl(struct ieee80211_hw *hw,
                                 struct ieee80211_sta *sta, u8 rssi_level);
 void rtl92ce_update_channel_access_setting(struct ieee80211_hw *hw);
@@ -74,5 +76,7 @@ void rtl8192ce_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw);
 void rtl92ce_suspend(struct ieee80211_hw *hw);
 void rtl92ce_resume(struct ieee80211_hw *hw);
+void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw,
+                               bool allow_all_da, bool write_into_reg);
 
 #endif
index e4d738f6166dae68f778f8faa13227c95220bcea..bd4aef74c0567929849c24008ead104b5a11b15d 100644 (file)
 #define        IMR_WLANOFF                             BIT(0)
 
 #define EFUSE_REAL_CONTENT_LEN                 512
+#define EFUSE_OOB_PROTECT_BYTES                        15
 
 #define        EEPROM_DEFAULT_TSSI                     0x0
 #define EEPROM_DEFAULT_TXPOWERDIFF             0x0
index 49f663bd93ff37c2d6069de4be0bc868427dcb0a..14203561b6ee5463d04841848a435cc9c51d59cd 100644 (file)
@@ -228,6 +228,7 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = {
        .enable_hw_sec = rtl92ce_enable_hw_security_config,
        .set_key = rtl92ce_set_key,
        .init_sw_leds = rtl92ce_init_sw_leds,
+       .allow_all_destaddr = rtl92ce_allow_all_destaddr,
        .get_bbreg = rtl92c_phy_query_bb_reg,
        .set_bbreg = rtl92c_phy_set_bb_reg,
        .set_rfreg = rtl92ce_phy_set_rf_reg,
@@ -278,6 +279,7 @@ static struct rtl_hal_cfg rtl92ce_hal_cfg = {
        .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE,
        .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION,
        .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN,
+       .maps[EFUSE_OOB_PROTECT_BYTES_LEN] = EFUSE_OOB_PROTECT_BYTES,
 
        .maps[RWCAM] = REG_CAMCMD,
        .maps[WCAMI] = REG_CAMWRITE,
@@ -309,7 +311,7 @@ static struct rtl_hal_cfg rtl92ce_hal_cfg = {
 
        .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW,
        .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT,
-       .maps[RTL_IMR_BcnInt] = IMR_BCNINT,
+       .maps[RTL_IMR_BCNINT] = IMR_BCNINT,
        .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW,
        .maps[RTL_IMR_RDU] = IMR_RDU,
        .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND,
index b9b1a6e0b16e87de460df9c434be0b0ad6bd851f..65bf5fb970023bbea4ecaadb7d62c1e83bbfbc83 100644 (file)
@@ -30,6 +30,7 @@
 #include "../wifi.h"
 #include "../pci.h"
 #include "../base.h"
+#include "../stats.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -42,7 +43,7 @@ static u8 _rtl92ce_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue)
 
        if (unlikely(ieee80211_is_beacon(fc)))
                return QSLT_BEACON;
-       if (ieee80211_is_mgmt(fc))
+       if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))
                return QSLT_MGNT;
 
        return skb->priority;
@@ -78,16 +79,6 @@ static u8 _rtl92c_evm_db_to_percentage(char value)
        return ret_val;
 }
 
-static long _rtl92ce_translate_todbm(struct ieee80211_hw *hw,
-                                    u8 signal_strength_index)
-{
-       long signal_power;
-
-       signal_power = (long)((signal_strength_index + 1) >> 1);
-       signal_power -= 95;
-       return signal_power;
-}
-
 static long _rtl92ce_signal_scale_mapping(struct ieee80211_hw *hw,
                long currsig)
 {
@@ -139,7 +130,6 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw,
        pstats->packet_toself = packet_toself;
        pstats->is_cck = is_cck_rate;
        pstats->packet_beacon = packet_beacon;
-       pstats->is_cck = is_cck_rate;
        pstats->rx_mimo_sig_qual[0] = -1;
        pstats->rx_mimo_sig_qual[1] = -1;
 
@@ -192,10 +182,30 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw,
                        }
                }
 
-               pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all);
+               pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all);
+               /* CCK gain is smaller than OFDM/MCS gain,
+                * so we add gain diff by experiences,
+                * the val is 6
+                */
+               pwdb_all += 6;
+               if (pwdb_all > 100)
+                       pwdb_all = 100;
+               /* modify the offset to make the same
+                * gain index with OFDM.
+                */
+               if (pwdb_all > 34 && pwdb_all <= 42)
+                       pwdb_all -= 2;
+               else if (pwdb_all > 26 && pwdb_all <= 34)
+                       pwdb_all -= 6;
+               else if (pwdb_all > 14 && pwdb_all <= 26)
+                       pwdb_all -= 8;
+               else if (pwdb_all > 4 && pwdb_all <= 14)
+                       pwdb_all -= 4;
+
                pstats->rx_pwdb_all = pwdb_all;
                pstats->recvsignalpower = rx_pwr_all;
 
+               /* (3) Get Signal Quality (EVM) */
                if (packet_match_bssid) {
                        u8 sq;
                        if (pstats->rx_pwdb_all > 40)
@@ -217,29 +227,38 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw,
        } else {
                rtlpriv->dm.rfpath_rxenable[0] =
                    rtlpriv->dm.rfpath_rxenable[1] = true;
+               /* (1)Get RSSI for HT rate */
                for (i = RF90_PATH_A; i < RF90_PATH_MAX; i++) {
+                       /* we will judge RF RX path now. */
                        if (rtlpriv->dm.rfpath_rxenable[i])
                                rf_rx_num++;
 
                        rx_pwr[i] =
                            ((p_drvinfo->gain_trsw[i] & 0x3f) * 2) - 110;
+                       /* Translate DBM to percentage. */
                        rssi = _rtl92c_query_rxpwrpercentage(rx_pwr[i]);
                        total_rssi += rssi;
+                       /* Get Rx snr value in DB */
                        rtlpriv->stats.rx_snr_db[i] =
                            (long)(p_drvinfo->rxsnr[i] / 2);
 
+                       /* Record Signal Strength for next packet */
                        if (packet_match_bssid)
                                pstats->rx_mimo_signalstrength[i] = (u8) rssi;
                }
 
+               /* (2)PWDB, Average PWDB cacluated by
+                * hardware (for rate adaptive)
+                */
                rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110;
                pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all);
                pstats->rx_pwdb_all = pwdb_all;
                pstats->rxpower = rx_pwr_all;
                pstats->recvsignalpower = rx_pwr_all;
 
-               if (pdesc->rxht && pdesc->rxmcs >= DESC92_RATEMCS8 &&
-                   pdesc->rxmcs <= DESC92_RATEMCS15)
+               /* (3)EVM of HT rate */
+               if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 &&
+                   pstats->rate <= DESC92_RATEMCS15)
                        max_spatial_stream = 2;
                else
                        max_spatial_stream = 1;
@@ -248,6 +267,9 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw,
                        evm = _rtl92c_evm_db_to_percentage(p_drvinfo->rxevm[i]);
 
                        if (packet_match_bssid) {
+                               /* Fill value in RFD, Get the first
+                                * spatial stream only
+                                */
                                if (i == 0)
                                        pstats->signalquality =
                                            (u8) (evm & 0xff);
@@ -256,6 +278,9 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw,
                }
        }
 
+       /* UI BSS List signal strength(in percentage),
+        * make it good looking, from 0~100.
+        */
        if (is_cck_rate)
                pstats->signalstrength =
                    (u8) (_rtl92ce_signal_scale_mapping(hw, pwdb_all));
@@ -265,215 +290,6 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw,
                          (hw, total_rssi /= rf_rx_num));
 }
 
-static void _rtl92ce_process_ui_rssi(struct ieee80211_hw *hw,
-               struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_phy *rtlphy = &(rtlpriv->phy);
-       u8 rfpath;
-       u32 last_rssi, tmpval;
-
-       if (pstats->packet_toself || pstats->packet_beacon) {
-               rtlpriv->stats.rssi_calculate_cnt++;
-
-               if (rtlpriv->stats.ui_rssi.total_num++ >=
-                   PHY_RSSI_SLID_WIN_MAX) {
-
-                       rtlpriv->stats.ui_rssi.total_num =
-                           PHY_RSSI_SLID_WIN_MAX;
-                       last_rssi =
-                           rtlpriv->stats.ui_rssi.elements[rtlpriv->
-                                                   stats.ui_rssi.index];
-                       rtlpriv->stats.ui_rssi.total_val -= last_rssi;
-               }
-
-               rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength;
-               rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi.
-                                               index++] =
-                   pstats->signalstrength;
-
-               if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX)
-                       rtlpriv->stats.ui_rssi.index = 0;
-
-               tmpval = rtlpriv->stats.ui_rssi.total_val /
-                   rtlpriv->stats.ui_rssi.total_num;
-               rtlpriv->stats.signal_strength =
-                   _rtl92ce_translate_todbm(hw, (u8) tmpval);
-               pstats->rssi = rtlpriv->stats.signal_strength;
-       }
-
-       if (!pstats->is_cck && pstats->packet_toself) {
-               for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath;
-                    rfpath++) {
-                       if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   pstats->rx_mimo_signalstrength[rfpath];
-
-                       }
-
-                       if (pstats->rx_mimo_signalstrength[rfpath] >
-                           rtlpriv->stats.rx_rssi_percentage[rfpath]) {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   ((rtlpriv->stats.
-                                     rx_rssi_percentage[rfpath] *
-                                     (RX_SMOOTH_FACTOR - 1)) +
-                                    (pstats->rx_mimo_signalstrength[rfpath])) /
-                                   (RX_SMOOTH_FACTOR);
-
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   rtlpriv->stats.rx_rssi_percentage[rfpath] +
-                                   1;
-                       } else {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   ((rtlpriv->stats.
-                                     rx_rssi_percentage[rfpath] *
-                                     (RX_SMOOTH_FACTOR - 1)) +
-                                    (pstats->rx_mimo_signalstrength[rfpath])) /
-                                   (RX_SMOOTH_FACTOR);
-                       }
-
-               }
-       }
-}
-
-static void _rtl92ce_update_rxsignalstatistics(struct ieee80211_hw *hw,
-                                              struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       int weighting = 0;
-
-       if (rtlpriv->stats.recv_signal_power == 0)
-               rtlpriv->stats.recv_signal_power = pstats->recvsignalpower;
-
-       if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power)
-               weighting = 5;
-
-       else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power)
-               weighting = (-5);
-
-       rtlpriv->stats.recv_signal_power =
-           (rtlpriv->stats.recv_signal_power * 5 +
-            pstats->recvsignalpower + weighting) / 6;
-}
-
-static void _rtl92ce_process_pwdb(struct ieee80211_hw *hw,
-               struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
-       long undec_sm_pwdb;
-
-       if (mac->opmode == NL80211_IFTYPE_ADHOC) {
-               return;
-       } else {
-               undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb;
-       }
-
-       if (pstats->packet_toself || pstats->packet_beacon) {
-               if (undec_sm_pwdb < 0)
-                       undec_sm_pwdb = pstats->rx_pwdb_all;
-
-               if (pstats->rx_pwdb_all > (u32) undec_sm_pwdb) {
-                       undec_sm_pwdb = (((undec_sm_pwdb) *
-                             (RX_SMOOTH_FACTOR - 1)) +
-                            (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR);
-
-                       undec_sm_pwdb += 1;
-               } else {
-                       undec_sm_pwdb = (((undec_sm_pwdb) *
-                             (RX_SMOOTH_FACTOR - 1)) +
-                            (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR);
-               }
-
-               rtlpriv->dm.undec_sm_pwdb = undec_sm_pwdb;
-               _rtl92ce_update_rxsignalstatistics(hw, pstats);
-       }
-}
-
-static void _rtl92ce_process_ui_link_quality(struct ieee80211_hw *hw,
-                                            struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       u32 last_evm, n_spatialstream, tmpval;
-
-       if (pstats->signalquality != 0) {
-               if (pstats->packet_toself || pstats->packet_beacon) {
-
-                       if (rtlpriv->stats.ui_link_quality.total_num++ >=
-                           PHY_LINKQUALITY_SLID_WIN_MAX) {
-                               rtlpriv->stats.ui_link_quality.total_num =
-                                   PHY_LINKQUALITY_SLID_WIN_MAX;
-                               last_evm =
-                                   rtlpriv->stats.
-                                   ui_link_quality.elements[rtlpriv->
-                                                         stats.ui_link_quality.
-                                                         index];
-                               rtlpriv->stats.ui_link_quality.total_val -=
-                                   last_evm;
-                       }
-
-                       rtlpriv->stats.ui_link_quality.total_val +=
-                           pstats->signalquality;
-                       rtlpriv->stats.ui_link_quality.elements[rtlpriv->stats.
-                                                               ui_link_quality.
-                                                               index++] =
-                           pstats->signalquality;
-
-                       if (rtlpriv->stats.ui_link_quality.index >=
-                           PHY_LINKQUALITY_SLID_WIN_MAX)
-                               rtlpriv->stats.ui_link_quality.index = 0;
-
-                       tmpval = rtlpriv->stats.ui_link_quality.total_val /
-                           rtlpriv->stats.ui_link_quality.total_num;
-                       rtlpriv->stats.signal_quality = tmpval;
-
-                       rtlpriv->stats.last_sigstrength_inpercent = tmpval;
-
-                       for (n_spatialstream = 0; n_spatialstream < 2;
-                            n_spatialstream++) {
-                               if (pstats->
-                                   rx_mimo_sig_qual[n_spatialstream] != -1) {
-                                       if (rtlpriv->stats.
-                                           rx_evm_percentage[n_spatialstream]
-                                           == 0) {
-                                               rtlpriv->stats.
-                                                  rx_evm_percentage
-                                                  [n_spatialstream] =
-                                                  pstats->rx_mimo_sig_qual
-                                                  [n_spatialstream];
-                                       }
-
-                                       rtlpriv->stats.
-                                           rx_evm_percentage[n_spatialstream] =
-                                           ((rtlpriv->
-                                             stats.rx_evm_percentage
-                                             [n_spatialstream] *
-                                             (RX_SMOOTH_FACTOR - 1)) +
-                                            (pstats->rx_mimo_sig_qual
-                                             [n_spatialstream] * 1)) /
-                                           (RX_SMOOTH_FACTOR);
-                               }
-                       }
-               }
-       } else {
-               ;
-       }
-}
-
-static void _rtl92ce_process_phyinfo(struct ieee80211_hw *hw,
-                                    u8 *buffer,
-                                    struct rtl_stats *pcurrent_stats)
-{
-
-       if (!pcurrent_stats->packet_matchbssid &&
-           !pcurrent_stats->packet_beacon)
-               return;
-
-       _rtl92ce_process_ui_rssi(hw, pcurrent_stats);
-       _rtl92ce_process_pwdb(hw, pcurrent_stats);
-       _rtl92ce_process_ui_link_quality(hw, pcurrent_stats);
-}
-
 static void _rtl92ce_translate_rx_signal_stuff(struct ieee80211_hw *hw,
                                               struct sk_buff *skb,
                                               struct rtl_stats *pstats,
@@ -516,7 +332,7 @@ static void _rtl92ce_translate_rx_signal_stuff(struct ieee80211_hw *hw,
                                   packet_matchbssid, packet_toself,
                                   packet_beacon);
 
-       _rtl92ce_process_phyinfo(hw, tmp_buf, pstats);
+       rtl_process_phyinfo(hw, tmp_buf, pstats);
 }
 
 bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
@@ -526,7 +342,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
 {
        struct rx_fwinfo_92c *p_drvinfo;
        struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc;
-
+       struct ieee80211_hdr *hdr;
        u32 phystatus = GET_RX_DESC_PHYST(pdesc);
        stats->length = (u16) GET_RX_DESC_PKT_LEN(pdesc);
        stats->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) *
@@ -539,37 +355,60 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
        stats->rate = (u8) GET_RX_DESC_RXMCS(pdesc);
        stats->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc);
        stats->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1);
-       stats->isampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1)
+       stats->isfirst_ampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1)
                                   && (GET_RX_DESC_FAGGR(pdesc) == 1));
        stats->timestamp_low = GET_RX_DESC_TSFL(pdesc);
        stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc);
+       stats->is_ht = (bool)GET_RX_DESC_RXHT(pdesc);
+
+       stats->is_cck = RX_HAL_IS_CCK_RATE(pdesc);
 
        rx_status->freq = hw->conf.channel->center_freq;
        rx_status->band = hw->conf.channel->band;
 
-       if (GET_RX_DESC_CRC32(pdesc))
-               rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+       hdr = (struct ieee80211_hdr *)(skb->data + stats->rx_drvinfo_size
+                       + stats->rx_bufshift);
 
-       if (!GET_RX_DESC_SWDEC(pdesc))
-               rx_status->flag |= RX_FLAG_DECRYPTED;
+       if (stats->crc)
+               rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
 
-       if (GET_RX_DESC_BW(pdesc))
+       if (stats->rx_is40Mhzpacket)
                rx_status->flag |= RX_FLAG_40MHZ;
 
-       if (GET_RX_DESC_RXHT(pdesc))
+       if (stats->is_ht)
                rx_status->flag |= RX_FLAG_HT;
 
        rx_status->flag |= RX_FLAG_MACTIME_START;
 
-       if (stats->decrypted)
-               rx_status->flag |= RX_FLAG_DECRYPTED;
-
+       /* hw will set stats->decrypted true, if it finds the
+        * frame is open data frame or mgmt frame.
+        * So hw will not decryption robust managment frame
+        * for IEEE80211w but still set status->decrypted
+        * true, so here we should set it back to undecrypted
+        * for IEEE80211w frame, and mac80211 sw will help
+        * to decrypt it
+        */
+       if (stats->decrypted) {
+               if (!hdr) {
+                       /* In testing, hdr was NULL here */
+                       return false;
+               }
+               if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+                   (ieee80211_has_protected(hdr->frame_control)))
+                       rx_status->flag &= ~RX_FLAG_DECRYPTED;
+               else
+                       rx_status->flag |= RX_FLAG_DECRYPTED;
+       }
+       /* rate_idx: index of data rate into band's
+        * supported rates or MCS index if HT rates
+        * are use (RX_FLAG_HT)
+        * Notice: this is diff with windows define
+        */
        rx_status->rate_idx = rtlwifi_rate_mapping(hw,
-                               (bool)GET_RX_DESC_RXHT(pdesc),
-                               (u8)GET_RX_DESC_RXMCS(pdesc),
-                               (bool)GET_RX_DESC_PAGGR(pdesc));
+                               stats->is_ht, stats->rate,
+                               stats->isfirst_ampdu);
 
-       rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
+       rx_status->mactime = stats->timestamp_low;
        if (phystatus) {
                p_drvinfo = (struct rx_fwinfo_92c *)(skb->data +
                                                     stats->rx_bufshift);
@@ -580,7 +419,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
        }
 
        /*rx_status->qual = stats->signal; */
-       rx_status->signal = stats->rssi + 10;
+       rx_status->signal = stats->recvsignalpower + 10;
        /*rx_status->noise = -stats->noise; */
 
        return true;
@@ -624,7 +463,8 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw,
        if (mac->opmode == NL80211_IFTYPE_STATION) {
                bw_40 = mac->bw_40;
        } else if (mac->opmode == NL80211_IFTYPE_AP ||
-               mac->opmode == NL80211_IFTYPE_ADHOC) {
+                  mac->opmode == NL80211_IFTYPE_ADHOC ||
+                  mac->opmode == NL80211_IFTYPE_MESH_POINT) {
                if (sta)
                        bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40;
        }
index c08d0f4c5f3d3fe8416195951ea6547a80429030..3d0498e69c8cf1b54a5474fe7bdc7f92d6687239 100644 (file)
@@ -202,7 +202,7 @@ static void _rtl92cu_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                        }
                }
                for (i = 0; i < 14; i++) {
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF(%d)-Ch(%d) [CCK / HT40_1S / HT40_2S] = [0x%x / 0x%x / 0x%x]\n", rf_path, i,
                                rtlefuse->txpwrlevel_cck[rf_path][i],
                                rtlefuse->txpwrlevel_ht40_1s[rf_path][i],
@@ -238,11 +238,11 @@ static void _rtl92cu_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                                    ((rtlefuse->eeprom_pwrlimit_ht40[index]
                                      & 0xf0) >> 4);
                        }
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF-%d pwrgroup_ht20[%d] = 0x%x\n",
                                rf_path, i,
                                rtlefuse->pwrgroup_ht20[rf_path][i]);
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF-%d pwrgroup_ht40[%d] = 0x%x\n",
                                rf_path, i,
                                rtlefuse->pwrgroup_ht40[rf_path][i]);
@@ -273,26 +273,26 @@ static void _rtl92cu_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
        rtlefuse->legacy_ht_txpowerdiff =
            rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][7];
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-A Ht20 to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_ht20diff[RF90_PATH_A][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-A Legacy to Ht40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-B Ht20 to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_ht20diff[RF90_PATH_B][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-B Legacy to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]);
        if (!autoload_fail)
                rtlefuse->eeprom_regulatory = (hwinfo[RF_OPTION1] & 0x7);
        else
                rtlefuse->eeprom_regulatory = 0;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory);
        if (!autoload_fail) {
                rtlefuse->eeprom_tssi[RF90_PATH_A] = hwinfo[EEPROM_TSSI_A];
@@ -301,7 +301,7 @@ static void _rtl92cu_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                rtlefuse->eeprom_tssi[RF90_PATH_A] = EEPROM_DEFAULT_TSSI;
                rtlefuse->eeprom_tssi[RF90_PATH_B] = EEPROM_DEFAULT_TSSI;
        }
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "TSSI_A = 0x%x, TSSI_B = 0x%x\n",
                rtlefuse->eeprom_tssi[RF90_PATH_A],
                rtlefuse->eeprom_tssi[RF90_PATH_B]);
@@ -316,7 +316,7 @@ static void _rtl92cu_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
        if (rtlefuse->eeprom_thermalmeter == 0x1f || autoload_fail)
                rtlefuse->apk_thermalmeterignore = true;
        rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter);
 }
 
index a73a17bc56dd068f9cb9d0042dff53b9c88a4a3b..23d640a4debdd7c1d336c9e2f57ab51fbe06fd5e 100644 (file)
@@ -223,7 +223,7 @@ static struct rtl_hal_cfg rtl92cu_hal_cfg = {
 
        .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW,
        .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT,
-       .maps[RTL_IMR_BcnInt] = IMR_BCNINT,
+       .maps[RTL_IMR_BCNINT] = IMR_BCNINT,
        .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW,
        .maps[RTL_IMR_RDU] = IMR_RDU,
        .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND,
index b6222eedb8350c8eb691cfe5fe1fa8eba8b7eaf3..710f7904ecdff632549d6763958960ad030fba05 100644 (file)
@@ -434,7 +434,7 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb)
                 (u32)hdr->addr1[2], (u32)hdr->addr1[3],
                 (u32)hdr->addr1[4], (u32)hdr->addr1[5]);
        memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status));
-       ieee80211_rx_irqsafe(hw, skb);
+       ieee80211_rx(hw, skb);
 }
 
 void  rtl8192cu_rx_hdl(struct ieee80211_hw *hw, struct sk_buff * skb)
index 5251fb8a111eee18ab04461c8f1eaed4c357a87d..19a765532603b06d4fa647a4b6ebb1651ae77f03 100644 (file)
@@ -171,8 +171,8 @@ static void rtl92d_dm_diginit(struct ieee80211_hw *hw)
        de_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
        de_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
        de_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-       de_digtable->rx_gain_range_max = DM_DIG_FA_UPPER;
-       de_digtable->rx_gain_range_min = DM_DIG_FA_LOWER;
+       de_digtable->rx_gain_max = DM_DIG_FA_UPPER;
+       de_digtable->rx_gain_min = DM_DIG_FA_LOWER;
        de_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
        de_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
        de_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
@@ -444,8 +444,8 @@ static void rtl92d_dm_dig(struct ieee80211_hw *hw)
                 "dm_DIG() Before: large_fa_hit=%d, forbidden_igi=%x\n",
                 de_digtable->large_fa_hit, de_digtable->forbidden_igi);
        RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
-                "dm_DIG() Before: Recover_cnt=%d, rx_gain_range_min=%x\n",
-                de_digtable->recover_cnt, de_digtable->rx_gain_range_min);
+                "dm_DIG() Before: Recover_cnt=%d, rx_gain_min=%x\n",
+                de_digtable->recover_cnt, de_digtable->rx_gain_min);
 
        /* deal with abnorally large false alarm */
        if (falsealm_cnt->cnt_all > 10000) {
@@ -459,9 +459,9 @@ static void rtl92d_dm_dig(struct ieee80211_hw *hw)
                }
                if (de_digtable->large_fa_hit >= 3) {
                        if ((de_digtable->forbidden_igi + 1) > DM_DIG_MAX)
-                               de_digtable->rx_gain_range_min = DM_DIG_MAX;
+                               de_digtable->rx_gain_min = DM_DIG_MAX;
                        else
-                               de_digtable->rx_gain_range_min =
+                               de_digtable->rx_gain_min =
                                    (de_digtable->forbidden_igi + 1);
                        de_digtable->recover_cnt = 3600;        /* 3600=2hr */
                }
@@ -475,12 +475,12 @@ static void rtl92d_dm_dig(struct ieee80211_hw *hw)
                                    DM_DIG_FA_LOWER) {
                                        de_digtable->forbidden_igi =
                                                         DM_DIG_FA_LOWER;
-                                       de_digtable->rx_gain_range_min =
+                                       de_digtable->rx_gain_min =
                                                         DM_DIG_FA_LOWER;
 
                                } else {
                                        de_digtable->forbidden_igi--;
-                                       de_digtable->rx_gain_range_min =
+                                       de_digtable->rx_gain_min =
                                            (de_digtable->forbidden_igi + 1);
                                }
                        } else if (de_digtable->large_fa_hit == 3) {
@@ -492,13 +492,13 @@ static void rtl92d_dm_dig(struct ieee80211_hw *hw)
                 "dm_DIG() After: large_fa_hit=%d, forbidden_igi=%x\n",
                 de_digtable->large_fa_hit, de_digtable->forbidden_igi);
        RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
-                "dm_DIG() After: recover_cnt=%d, rx_gain_range_min=%x\n",
-                de_digtable->recover_cnt, de_digtable->rx_gain_range_min);
+                "dm_DIG() After: recover_cnt=%d, rx_gain_min=%x\n",
+                de_digtable->recover_cnt, de_digtable->rx_gain_min);
 
        if (value_igi > DM_DIG_MAX)
                value_igi = DM_DIG_MAX;
-       else if (value_igi < de_digtable->rx_gain_range_min)
-               value_igi = de_digtable->rx_gain_range_min;
+       else if (value_igi < de_digtable->rx_gain_min)
+               value_igi = de_digtable->rx_gain_min;
        de_digtable->cur_igvalue = value_igi;
        rtl92d_dm_write_dig(hw);
        if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G)
@@ -1071,9 +1071,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
                        }
                        ele_d = (ofdmswing_table[(u8) ofdm_index[0]] &
                                                 0xFFC00000) >> 22;
-                       val_x = rtlphy->iqk_matrix_regsetting
+                       val_x = rtlphy->iqk_matrix
                                                [indexforchannel].value[0][0];
-                       val_y = rtlphy->iqk_matrix_regsetting
+                       val_y = rtlphy->iqk_matrix
                                                [indexforchannel].value[0][1];
                        if (val_x != 0) {
                                if ((val_x & 0x00000200) != 0)
@@ -1175,9 +1175,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
                        if (is2t) {
                                ele_d = (ofdmswing_table[(u8) ofdm_index[1]] &
                                                0xFFC00000) >> 22;
-                               val_x = rtlphy->iqk_matrix_regsetting
+                               val_x = rtlphy->iqk_matrix
                                                [indexforchannel].value[0][4];
-                               val_y = rtlphy->iqk_matrix_regsetting
+                               val_y = rtlphy->iqk_matrix
                                                [indexforchannel].value[0][5];
                                if (val_x != 0) {
                                        if ((val_x & 0x00000200) != 0)
index aa5b42521bb4072580efae606d84323a0e7977db..7dd8f6de0550f4f03424472f8926577a23b513c9 100644 (file)
@@ -1183,7 +1183,7 @@ void rtl92d_linked_set_reg(struct ieee80211_hw *hw)
        u8 channel = rtlphy->current_channel;
 
        indexforchannel = rtl92d_get_rightchnlplace_for_iqk(channel);
-       if (!rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done) {
+       if (!rtlphy->iqk_matrix[indexforchannel].iqk_done) {
                RT_TRACE(rtlpriv, COMP_SCAN | COMP_INIT, DBG_DMESG,
                         "Do IQK for channel:%d\n", channel);
                rtl92d_phy_iq_calibrate(hw);
index 33041bd4da81f03ffb50c273c7953c989072788a..840bac5fa2f80bfa224bd5470b6562585aec0a02 100644 (file)
@@ -2479,9 +2479,9 @@ void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw)
                                  rtlphy->current_channel);
 
                for (i = 0; i < IQK_MATRIX_REG_NUM; i++)
-                       rtlphy->iqk_matrix_regsetting[indexforchannel].
+                       rtlphy->iqk_matrix[indexforchannel].
                                value[0][i] = result[final_candidate][i];
-               rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done =
+               rtlphy->iqk_matrix[indexforchannel].iqk_done =
                        true;
 
                RT_TRACE(rtlpriv, COMP_SCAN | COMP_MLME, DBG_LOUD,
@@ -2501,8 +2501,8 @@ void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel)
        indexforchannel = rtl92d_get_rightchnlplace_for_iqk(channel);
        RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "indexforchannel %d done %d\n",
                 indexforchannel,
-                rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done);
-       if (0 && !rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done &&
+                rtlphy->iqk_matrix[indexforchannel].iqk_done);
+       if (0 && !rtlphy->iqk_matrix[indexforchannel].iqk_done &&
                rtlphy->need_iqk) {
                /* Re Do IQK. */
                RT_TRACE(rtlpriv, COMP_SCAN | COMP_INIT, DBG_LOUD,
@@ -2516,23 +2516,23 @@ void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel)
                        RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD,
                                 "Just Read IQK Matrix reg for channel:%d....\n",
                                 channel);
-                       if ((rtlphy->iqk_matrix_regsetting[indexforchannel].
+                       if ((rtlphy->iqk_matrix[indexforchannel].
                             value[0] != NULL)
                                /*&&(regea4 != 0) */)
                                _rtl92d_phy_patha_fill_iqk_matrix(hw, true,
-                                       rtlphy->iqk_matrix_regsetting[
+                                       rtlphy->iqk_matrix[
                                        indexforchannel].value, 0,
-                                       (rtlphy->iqk_matrix_regsetting[
+                                       (rtlphy->iqk_matrix[
                                        indexforchannel].value[0][2] == 0));
                        if (IS_92D_SINGLEPHY(rtlhal->version)) {
-                               if ((rtlphy->iqk_matrix_regsetting[
+                               if ((rtlphy->iqk_matrix[
                                        indexforchannel].value[0][4] != 0)
                                        /*&&(regec4 != 0) */)
                                        _rtl92d_phy_pathb_fill_iqk_matrix(hw,
                                                true,
-                                               rtlphy->iqk_matrix_regsetting[
+                                               rtlphy->iqk_matrix[
                                                indexforchannel].value, 0,
-                                               (rtlphy->iqk_matrix_regsetting[
+                                               (rtlphy->iqk_matrix[
                                                indexforchannel].value[0][6]
                                                == 0));
                        }
@@ -2830,20 +2830,20 @@ void rtl92d_phy_reset_iqk_result(struct ieee80211_hw *hw)
 
        RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
                 "settings regs %d default regs %d\n",
-                (int)(sizeof(rtlphy->iqk_matrix_regsetting) /
+                (int)(sizeof(rtlphy->iqk_matrix) /
                       sizeof(struct iqk_matrix_regs)),
                 IQK_MATRIX_REG_NUM);
        /* 0xe94, 0xe9c, 0xea4, 0xeac, 0xeb4, 0xebc, 0xec4, 0xecc */
        for (i = 0; i < IQK_MATRIX_SETTINGS_NUM; i++) {
-               rtlphy->iqk_matrix_regsetting[i].value[0][0] = 0x100;
-               rtlphy->iqk_matrix_regsetting[i].value[0][2] = 0x100;
-               rtlphy->iqk_matrix_regsetting[i].value[0][4] = 0x100;
-               rtlphy->iqk_matrix_regsetting[i].value[0][6] = 0x100;
-               rtlphy->iqk_matrix_regsetting[i].value[0][1] = 0x0;
-               rtlphy->iqk_matrix_regsetting[i].value[0][3] = 0x0;
-               rtlphy->iqk_matrix_regsetting[i].value[0][5] = 0x0;
-               rtlphy->iqk_matrix_regsetting[i].value[0][7] = 0x0;
-               rtlphy->iqk_matrix_regsetting[i].iqk_done = false;
+               rtlphy->iqk_matrix[i].value[0][0] = 0x100;
+               rtlphy->iqk_matrix[i].value[0][2] = 0x100;
+               rtlphy->iqk_matrix[i].value[0][4] = 0x100;
+               rtlphy->iqk_matrix[i].value[0][6] = 0x100;
+               rtlphy->iqk_matrix[i].value[0][1] = 0x0;
+               rtlphy->iqk_matrix[i].value[0][3] = 0x0;
+               rtlphy->iqk_matrix[i].value[0][5] = 0x0;
+               rtlphy->iqk_matrix[i].value[0][7] = 0x0;
+               rtlphy->iqk_matrix[i].iqk_done = false;
        }
 }
 
index ebb1d5f5e7b5c627e3f78566f23e7cbaeb90ba2e..b7498c5bafc56f25e887831e9e2f9640e811794c 100644 (file)
 #define        IMR_TIMEOUT1                    BIT(16)
 #define        IMR_TXFOVW                      BIT(15)
 #define        IMR_PSTIMEOUT                   BIT(14)
-#define        IMR_BcnInt                      BIT(13)
+#define        IMR_BCNINT                      BIT(13)
 #define        IMR_RXFOVW                      BIT(12)
 #define        IMR_RDU                         BIT(11)
 #define        IMR_ATIMEND                     BIT(10)
index 03c6d18b2e0755fa1bae1236180980eca1a4fb64..c18c04bf0c13e4ac98c050f205bf35ddd7d7d14a 100644 (file)
@@ -166,7 +166,7 @@ static int rtl92d_init_sw_vars(struct ieee80211_hw *hw)
                rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE;
 
        /* for early mode */
-       rtlpriv->rtlhal.earlymode_enable = true;
+       rtlpriv->rtlhal.earlymode_enable = false;
        for (tid = 0; tid < 8; tid++)
                skb_queue_head_init(&rtlpriv->mac80211.skb_waitq[tid]);
 
@@ -319,7 +319,7 @@ static struct rtl_hal_cfg rtl92de_hal_cfg = {
 
        .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW,
        .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT,
-       .maps[RTL_IMR_BcnInt] = IMR_BcnInt,
+       .maps[RTL_IMR_BCNINT] = IMR_BCNINT,
        .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW,
        .maps[RTL_IMR_RDU] = IMR_RDU,
        .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND,
@@ -333,7 +333,7 @@ static struct rtl_hal_cfg rtl92de_hal_cfg = {
        .maps[RTL_IMR_VIDOK] = IMR_VIDOK,
        .maps[RTL_IMR_VODOK] = IMR_VODOK,
        .maps[RTL_IMR_ROK] = IMR_ROK,
-       .maps[RTL_IBSS_INT_MASKS] = (IMR_BcnInt | IMR_TBDOK | IMR_TBDER),
+       .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER),
 
        .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
        .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
index 2d255e02d795b6cd2a403450f53dfd377664d176..83c98674bfd3176f8fc512f80b7a31be83ba87da 100644 (file)
@@ -36,9 +36,6 @@
 #define SHORT_SLOT_TIME                                9
 #define NON_SHORT_SLOT_TIME                    20
 
-/* Rx smooth factor */
-#define        RX_SMOOTH_FACTOR                        20
-
 /* Queue Select Value in TxDesc */
 #define QSLT_BK                                        0x2
 #define QSLT_BE                                        0x0
 #define QSLT_MGNT                              0x12
 #define QSLT_CMD                               0x13
 
-#define        PHY_RSSI_SLID_WIN_MAX                   100
-#define        PHY_LINKQUALITY_SLID_WIN_MAX            20
-#define        PHY_BEACON_RSSI_SLID_WIN_MAX            10
-
 /* Tx Desc */
 #define TX_DESC_SIZE_RTL8192S                  (16 * 4)
 #define TX_CMDDESC_SIZE_RTL8192S               (16 * 4)
index e551fe5f9ccded4242d75d2c19e4e0051c187dfb..b3a2d5ec59e653273c61fc4a990746f408361f84 100644 (file)
@@ -163,6 +163,7 @@ static void _rtl92s_dm_txpowertracking_callback_thermalmeter(
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
        u8 thermalvalue = 0;
+       u32 fw_cmd = 0;
 
        rtlpriv->dm.txpower_trackinginit = true;
 
@@ -175,7 +176,19 @@ static void _rtl92s_dm_txpowertracking_callback_thermalmeter(
 
        if (thermalvalue) {
                rtlpriv->dm.thermalvalue = thermalvalue;
-               rtl92s_phy_set_fw_cmd(hw, FW_CMD_TXPWR_TRACK_THERMAL);
+               if (hal_get_firmwareversion(rtlpriv) >= 0x35) {
+                       rtl92s_phy_set_fw_cmd(hw, FW_CMD_TXPWR_TRACK_THERMAL);
+               } else {
+                       fw_cmd = (FW_TXPWR_TRACK_THERMAL |
+                                (rtlpriv->efuse.thermalmeter[0] << 8) |
+                                (thermalvalue << 16));
+
+                       RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+                                "Write to FW Thermal Val = 0x%x\n", fw_cmd);
+
+                       rtl_write_dword(rtlpriv, WFM5, fw_cmd);
+                       rtl92s_phy_chk_fwcmd_iodone(hw);
+               }
        }
 
        rtlpriv->dm.txpowercount = 0;
@@ -217,11 +230,10 @@ static void _rtl92s_dm_refresh_rateadaptive_mask(struct ieee80211_hw *hw)
        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
        struct rate_adaptive *ra = &(rtlpriv->ra);
-
+       struct ieee80211_sta *sta = NULL;
        u32 low_rssi_thresh = 0;
        u32 middle_rssi_thresh = 0;
        u32 high_rssi_thresh = 0;
-       struct ieee80211_sta *sta = NULL;
 
        if (is_hal_stop(rtlhal))
                return;
@@ -229,14 +241,12 @@ static void _rtl92s_dm_refresh_rateadaptive_mask(struct ieee80211_hw *hw)
        if (!rtlpriv->dm.useramask)
                return;
 
-       if (!rtlpriv->dm.inform_fw_driverctrldm) {
+       if (hal_get_firmwareversion(rtlpriv) >= 61 &&
+           !rtlpriv->dm.inform_fw_driverctrldm) {
                rtl92s_phy_set_fw_cmd(hw, FW_CMD_CTRL_DM_BY_DRIVER);
                rtlpriv->dm.inform_fw_driverctrldm = true;
        }
 
-       rcu_read_lock();
-       if (mac->opmode == NL80211_IFTYPE_STATION)
-               sta = get_sta(hw, mac->vif, mac->bssid);
        if ((mac->link_state == MAC80211_LINKED) &&
            (mac->opmode == NL80211_IFTYPE_STATION)) {
                switch (ra->pre_ratr_state) {
@@ -285,12 +295,16 @@ static void _rtl92s_dm_refresh_rateadaptive_mask(struct ieee80211_hw *hw)
                                 rtlpriv->dm.undec_sm_pwdb, ra->ratr_state,
                                 ra->pre_ratr_state, ra->ratr_state);
 
-                       rtlpriv->cfg->ops->update_rate_tbl(hw, sta,
+                       rcu_read_lock();
+                       sta = rtl_find_sta(hw, mac->bssid);
+                       if (sta)
+                               rtlpriv->cfg->ops->update_rate_tbl(hw, sta,
                                                           ra->ratr_state);
+                       rcu_read_unlock();
+
                        ra->pre_ratr_state = ra->ratr_state;
                }
        }
-       rcu_read_unlock();
 }
 
 static void _rtl92s_dm_switch_baseband_mrc(struct ieee80211_hw *hw)
@@ -370,7 +384,8 @@ static void _rtl92s_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
        ra->ratr_state = DM_RATR_STA_MAX;
        ra->pre_ratr_state = DM_RATR_STA_MAX;
 
-       if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER)
+       if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER &&
+           hal_get_firmwareversion(rtlpriv) >= 60)
                rtlpriv->dm.useramask = true;
        else
                rtlpriv->dm.useramask = false;
@@ -457,13 +472,13 @@ static void _rtl92s_dm_initial_gain_sta_beforeconnect(struct ieee80211_hw *hw)
                                digtable->back_val = DM_DIG_BACKOFF;
 
                        if ((digtable->rssi_val + 10 - digtable->back_val) >
-                               digtable->rx_gain_range_max)
+                               digtable->rx_gain_max)
                                digtable->cur_igvalue =
-                                               digtable->rx_gain_range_max;
+                                               digtable->rx_gain_max;
                        else if ((digtable->rssi_val + 10 - digtable->back_val)
-                                < digtable->rx_gain_range_min)
+                                < digtable->rx_gain_min)
                                digtable->cur_igvalue =
-                                               digtable->rx_gain_range_min;
+                                               digtable->rx_gain_min;
                        else
                                digtable->cur_igvalue = digtable->rssi_val + 10
                                        - digtable->back_val;
@@ -475,7 +490,7 @@ static void _rtl92s_dm_initial_gain_sta_beforeconnect(struct ieee80211_hw *hw)
 
                        if (falsealm_cnt->cnt_all > 16000)
                                digtable->cur_igvalue =
-                                                digtable->rx_gain_range_max;
+                                                digtable->rx_gain_max;
                /* connected -> connected or disconnected -> disconnected  */
                } else {
                        /* Firmware control DIG, do nothing in driver dm */
@@ -677,9 +692,9 @@ static void _rtl92s_dm_init_dig(struct ieee80211_hw *hw)
        /* for dig debug rssi value */
        digtable->rssi_val = 50;
        digtable->back_val = DM_DIG_BACKOFF;
-       digtable->rx_gain_range_max = DM_DIG_MAX;
+       digtable->rx_gain_max = DM_DIG_MAX;
 
-       digtable->rx_gain_range_min = DM_DIG_MIN;
+       digtable->rx_gain_min = DM_DIG_MIN;
 
        digtable->backoffval_range_max = DM_DIG_BACKOFF_MAX;
        digtable->backoffval_range_min = DM_DIG_BACKOFF_MIN;
index 084e7773bce2a1f6585d2b115e7a525315dc7a39..4f461786a7eb24abb3ae49a1dbccc4311d54ea58 100644 (file)
@@ -400,6 +400,39 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
 
                        break;
                }
+       case HW_VAR_FW_LPS_ACTION: {
+               bool enter_fwlps = *((bool *)val);
+               u8 rpwm_val, fw_pwrmode;
+               bool fw_current_inps;
+
+               if (enter_fwlps) {
+                       rpwm_val = 0x02;        /* RF off */
+                       fw_current_inps = true;
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_FW_PSMODE_STATUS,
+                                       (u8 *)(&fw_current_inps));
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_H2C_FW_PWRMODE,
+                                       (u8 *)(&ppsc->fwctrl_psmode));
+
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_SET_RPWM,
+                                       (u8 *)(&rpwm_val));
+               } else {
+                       rpwm_val = 0x0C;        /* RF on */
+                       fw_pwrmode = FW_PS_ACTIVE_MODE;
+                       fw_current_inps = false;
+                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
+                                       (u8 *)(&rpwm_val));
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_H2C_FW_PWRMODE,
+                                       (u8 *)(&fw_pwrmode));
+
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_FW_PSMODE_STATUS,
+                                       (u8 *)(&fw_current_inps));
+               }
+               break; }
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         "switch case not processed\n");
@@ -438,7 +471,7 @@ void rtl92se_enable_hw_security_config(struct ieee80211_hw *hw)
 
 }
 
-static u8 _rtl92ce_halset_sysclk(struct ieee80211_hw *hw, u8 data)
+static u8 _rtl92se_halset_sysclk(struct ieee80211_hw *hw, u8 data)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        u8 waitcount = 100;
@@ -547,7 +580,7 @@ static void _rtl92se_macconfig_before_fwdownload(struct ieee80211_hw *hw)
                tmpu1b &= ~(BIT(6) | BIT(7));
 
                /* Set failed, return to prevent hang. */
-               if (!_rtl92ce_halset_sysclk(hw, tmpu1b))
+               if (!_rtl92se_halset_sysclk(hw, tmpu1b))
                        return;
        }
 
@@ -650,7 +683,7 @@ static void _rtl92se_macconfig_before_fwdownload(struct ieee80211_hw *hw)
 
        tmpu1b = rtl_read_byte(rtlpriv, (SYS_CLKR + 1));
        tmpu1b = ((tmpu1b | BIT(7)) & (~BIT(6)));
-       if (!_rtl92ce_halset_sysclk(hw, tmpu1b))
+       if (!_rtl92se_halset_sysclk(hw, tmpu1b))
                return; /* Set failed, return to prevent hang. */
 
        rtl_write_word(rtlpriv, CMDR, 0x07FC);
@@ -967,6 +1000,15 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
                return rtstatus;
        }
 
+       /* because last function modify RCR, so we update
+        * rcr var here, or TP will unstable for receive_config
+        * is wrong, RX RCR_ACRC32 will cause TP unstabel & Rx
+        * RCR_APP_ICV will cause mac80211 unassoc for cisco 1252
+        */
+       rtlpci->receive_config = rtl_read_dword(rtlpriv, RCR);
+       rtlpci->receive_config &= ~(RCR_ACRC32 | RCR_AICV);
+       rtl_write_dword(rtlpriv, RCR, rtlpci->receive_config);
+
        /* Make sure BB/RF write OK. We should prevent enter IPS. radio off. */
        /* We must set flag avoid BB/RF config period later!! */
        rtl_write_dword(rtlpriv, CMDR, 0x37FC);
@@ -982,25 +1024,6 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
 
        rtlphy->rf_mode = RF_OP_BY_SW_3WIRE;
 
-       /* RF Power Save */
-#if 0
-       /* H/W or S/W RF OFF before sleep. */
-       if (rtlpriv->psc.rfoff_reason > RF_CHANGE_BY_PS) {
-               u32 rfoffreason = rtlpriv->psc.rfoff_reason;
-
-               rtlpriv->psc.rfoff_reason = RF_CHANGE_BY_INIT;
-               rtlpriv->psc.rfpwr_state = ERFON;
-               /* FIXME: check spinlocks if this block is uncommented */
-               rtl_ps_set_rf_state(hw, ERFOFF, rfoffreason);
-       } else {
-               /* gpio radio on/off is out of adapter start */
-               if (rtlpriv->psc.hwradiooff == false) {
-                       rtlpriv->psc.rfpwr_state = ERFON;
-                       rtlpriv->psc.rfoff_reason = 0;
-               }
-       }
-#endif
-
        /* Before RF-R/W we must execute the IO from Scott's suggestion. */
        rtl_write_byte(rtlpriv, AFE_XTAL_CTRL + 1, 0xDB);
        if (rtlhal->version == VERSION_8192S_ACUT)
@@ -1058,7 +1081,22 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
 
        /* We enable high power and RA related mechanism after NIC
         * initialized. */
-       rtl92s_phy_set_fw_cmd(hw, FW_CMD_RA_INIT);
+       if (hal_get_firmwareversion(rtlpriv) >= 0x35) {
+               /* Fw v.53 and later. */
+               rtl92s_phy_set_fw_cmd(hw, FW_CMD_RA_INIT);
+       } else if (hal_get_firmwareversion(rtlpriv) == 0x34) {
+               /* Fw v.52. */
+               rtl_write_dword(rtlpriv, WFM5, FW_RA_INIT);
+               rtl92s_phy_chk_fwcmd_iodone(hw);
+       } else {
+               /* Compatible earlier FW version. */
+               rtl_write_dword(rtlpriv, WFM5, FW_RA_RESET);
+               rtl92s_phy_chk_fwcmd_iodone(hw);
+               rtl_write_dword(rtlpriv, WFM5, FW_RA_ACTIVE);
+               rtl92s_phy_chk_fwcmd_iodone(hw);
+               rtl_write_dword(rtlpriv, WFM5, FW_RA_REFRESH);
+               rtl92s_phy_chk_fwcmd_iodone(hw);
+       }
 
        /* Add to prevent ASPM bug. */
        /* Always enable hst and NIC clock request. */
@@ -1229,7 +1267,6 @@ void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
        synchronize_irq(rtlpci->pdev->irq);
 }
 
-
 static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1754,7 +1791,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
                }
 
                for (i = 0; i < 14; i++) {
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF(%d)-Ch(%d) [CCK / HT40_1S / HT40_2S] = [0x%x / 0x%x / 0x%x]\n",
                                rf_path, i,
                                rtlefuse->txpwrlevel_cck[rf_path][i],
@@ -1791,11 +1828,11 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
                                ((rtlefuse->eeprom_pwrgroup[rf_path][index] &
                                0xf0) >> 4);
 
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF-%d pwrgroup_ht20[%d] = 0x%x\n",
                                rf_path, i,
                                rtlefuse->pwrgroup_ht20[rf_path][i]);
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF-%d pwrgroup_ht40[%d] = 0x%x\n",
                                rf_path, i,
                                rtlefuse->pwrgroup_ht40[rf_path][i]);
@@ -1850,27 +1887,27 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
                        rtlefuse->eeprom_regulatory =
                                 (hwinfo[EEPROM_REGULATORY] & 0x1);
        }
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory);
 
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-A Ht20 to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_ht20diff[RF90_PATH_A][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-A Legacy to Ht40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-B Ht20 to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_ht20diff[RF90_PATH_B][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-B Legacy to HT40 Diff[%d] = 0x%x\n",
                        i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]);
 
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "TxPwrSafetyFlag = %d\n", rtlefuse->txpwr_safetyflag);
 
        /* Read RF-indication and Tx Power gain
@@ -1880,7 +1917,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
        rtlefuse->legacy_httxpowerdiff =
                rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][0];
 
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "TxPowerDiff = %#x\n", rtlefuse->eeprom_txpowerdiff);
 
        /* Get TSSI value for each path. */
@@ -1889,7 +1926,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
        usvalue = hwinfo[EEPROM_TSSI_B];
        rtlefuse->eeprom_tssi[RF90_PATH_B] = (u8)(usvalue & 0xff);
 
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower, "TSSI_A = 0x%x, TSSI_B = 0x%x\n",
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER, "TSSI_A = 0x%x, TSSI_B = 0x%x\n",
                rtlefuse->eeprom_tssi[RF90_PATH_A],
                rtlefuse->eeprom_tssi[RF90_PATH_B]);
 
@@ -1897,7 +1934,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
        /* and read ThermalMeter from EEPROM */
        tempval = hwinfo[EEPROM_THERMALMETER];
        rtlefuse->eeprom_thermalmeter = tempval;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter);
 
        /* ThermalMeter, BIT(0)~3 for RFIC1, BIT(4)~7 for RFIC2 */
@@ -1914,7 +1951,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw)
        /* Version ID, Channel plan */
        rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN];
        rtlefuse->txpwr_fromeprom = true;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "EEPROM ChannelPlan = 0x%4x\n", rtlefuse->eeprom_channelplan);
 
        /* Read Customer ID or Board Type!!! */
@@ -1999,6 +2036,8 @@ static void rtl92se_update_hal_rate_table(struct ieee80211_hw *hw,
                ratr_value = sta->supp_rates[1] << 4;
        else
                ratr_value = sta->supp_rates[0];
+       if (mac->opmode == NL80211_IFTYPE_ADHOC)
+               ratr_value = 0xfff;
        ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
                        sta->ht_cap.mcs.rx_mask[0] << 12);
        switch (wirelessmode) {
@@ -2112,6 +2151,8 @@ static void rtl92se_update_hal_rate_mask(struct ieee80211_hw *hw,
                ratr_bitmap = sta->supp_rates[1] << 4;
        else
                ratr_bitmap = sta->supp_rates[0];
+       if (mac->opmode == NL80211_IFTYPE_ADHOC)
+               ratr_bitmap = 0xfff;
        ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
                        sta->ht_cap.mcs.rx_mask[0] << 12);
        switch (wirelessmode) {
@@ -2200,6 +2241,7 @@ static void rtl92se_update_hal_rate_mask(struct ieee80211_hw *hw,
                        ratr_bitmap &= 0x0f8ff0ff;
                break;
        }
+       sta_entry->ratr_index = ratr_index;
 
        if (rtlpriv->rtlhal.version >= VERSION_8192S_BCUT)
                ratr_bitmap &= 0x0FFFFFFF;
@@ -2438,23 +2480,9 @@ void rtl92se_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr,
                                rtl_cam_del_entry(hw, p_macaddr);
                        rtl_cam_delete_one_entry(hw, p_macaddr, entry_id);
                } else {
-                       RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
-                                "The insert KEY length is %d\n",
-                                rtlpriv->sec.key_len[PAIRWISE_KEYIDX]);
-                       RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
-                                "The insert KEY is %x %x\n",
-                                rtlpriv->sec.key_buf[0][0],
-                                rtlpriv->sec.key_buf[0][1]);
-
                        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
                                 "add one entry\n");
                        if (is_pairwise) {
-                               RT_PRINT_DATA(rtlpriv, COMP_SEC, DBG_LOUD,
-                                             "Pairwise Key content",
-                                             rtlpriv->sec.pairwise_key,
-                                             rtlpriv->sec.
-                                             key_len[PAIRWISE_KEYIDX]);
-
                                RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
                                         "set Pairwise key\n");
 
@@ -2502,3 +2530,23 @@ void rtl92se_resume(struct ieee80211_hw *hw)
                pci_write_config_dword(rtlpci->pdev, 0x40,
                        val & 0xffff00ff);
 }
+
+/* Turn on AAP (RCR:bit 0) for promicuous mode. */
+void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw,
+                               bool allow_all_da, bool write_into_reg)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+       if (allow_all_da) /* Set BIT0 */
+               rtlpci->receive_config |= RCR_AAP;
+       else /* Clear BIT0 */
+               rtlpci->receive_config &= ~RCR_AAP;
+
+       if (write_into_reg)
+               rtl_write_dword(rtlpriv, RCR, rtlpci->receive_config);
+
+       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
+                "receive_config=0x%08X, write_into_reg=%d\n",
+                rtlpci->receive_config, write_into_reg);
+}
index a8e068c76e475dd74de6884db4e49b39945e8143..da48aa8cbe6f8cb8e97ca2943e643217bfaea170 100644 (file)
@@ -74,6 +74,7 @@ void rtl92se_set_key(struct ieee80211_hw *hw,
                     u8 enc_algo, bool is_wepkey, bool clear_all);
 void rtl92se_suspend(struct ieee80211_hw *hw);
 void rtl92se_resume(struct ieee80211_hw *hw);
+void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw,
+                               bool allow_all_da, bool write_into_reg);
 
 #endif
-
index 67404975e00b41318722a0df4ee05f47df6a44cf..9c092e6eb3fe85d5707e7c652dc647ac8c486dcb 100644 (file)
@@ -1307,6 +1307,8 @@ static void _rtl92s_phy_set_fwcmd_io(struct ieee80211_hw *hw)
        if (is_hal_stop(rtlhal))
                return;
 
+       if (hal_get_firmwareversion(rtlpriv) < 0x34)
+               goto skip;
        /* We re-map RA related CMD IO to combinational ones */
        /* if FW version is v.52 or later. */
        switch (rtlhal->current_fwcmd_io) {
@@ -1320,6 +1322,7 @@ static void _rtl92s_phy_set_fwcmd_io(struct ieee80211_hw *hw)
                break;
        }
 
+skip:
        switch (rtlhal->current_fwcmd_io) {
        case FW_CMD_RA_RESET:
                RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_RA_RESET\n");
@@ -1440,7 +1443,7 @@ bool rtl92s_phy_set_fw_cmd(struct ieee80211_hw *hw, enum fwcmd_iotype fw_cmdio)
        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
        u32     fw_param = FW_CMD_IO_PARA_QUERY(rtlpriv);
        u16     fw_cmdmap = FW_CMD_IO_QUERY(rtlpriv);
-       bool bPostProcessing = false;
+       bool postprocessing = false;
 
        RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
                 "Set FW Cmd(%#x), set_fwcmd_inprogress(%d)\n",
@@ -1449,15 +1452,24 @@ bool rtl92s_phy_set_fw_cmd(struct ieee80211_hw *hw, enum fwcmd_iotype fw_cmdio)
        do {
                /* We re-map to combined FW CMD ones if firmware version */
                /* is v.53 or later. */
-               switch (fw_cmdio) {
-               case FW_CMD_RA_REFRESH_N:
-                       fw_cmdio = FW_CMD_RA_REFRESH_N_COMB;
-                       break;
-               case FW_CMD_RA_REFRESH_BG:
-                       fw_cmdio = FW_CMD_RA_REFRESH_BG_COMB;
-                       break;
-               default:
-                       break;
+               if (hal_get_firmwareversion(rtlpriv) >= 0x35) {
+                       switch (fw_cmdio) {
+                       case FW_CMD_RA_REFRESH_N:
+                               fw_cmdio = FW_CMD_RA_REFRESH_N_COMB;
+                               break;
+                       case FW_CMD_RA_REFRESH_BG:
+                               fw_cmdio = FW_CMD_RA_REFRESH_BG_COMB;
+                               break;
+                       default:
+                               break;
+                       }
+               } else {
+                       if ((fw_cmdio == FW_CMD_IQK_ENABLE) ||
+                           (fw_cmdio == FW_CMD_RA_REFRESH_N) ||
+                           (fw_cmdio == FW_CMD_RA_REFRESH_BG)) {
+                               postprocessing = true;
+                               break;
+                       }
                }
 
                /* If firmware version is v.62 or later,
@@ -1588,19 +1600,19 @@ bool rtl92s_phy_set_fw_cmd(struct ieee80211_hw *hw, enum fwcmd_iotype fw_cmdio)
                                fw_cmdmap &= ~FW_DIG_ENABLE_CTL;
 
                        FW_CMD_IO_SET(rtlpriv, fw_cmdmap);
-                       bPostProcessing = true;
+                       postprocessing = true;
                        break;
                case FW_CMD_PAUSE_DM_BY_SCAN:
                        fw_cmdmap &= ~(FW_DIG_ENABLE_CTL |
                                       FW_HIGH_PWR_ENABLE_CTL |
                                       FW_SS_CTL);
                        FW_CMD_IO_SET(rtlpriv, fw_cmdmap);
-                       bPostProcessing = true;
+                       postprocessing = true;
                        break;
                case FW_CMD_HIGH_PWR_DISABLE:
                        fw_cmdmap &= ~FW_HIGH_PWR_ENABLE_CTL;
                        FW_CMD_IO_SET(rtlpriv, fw_cmdmap);
-                       bPostProcessing = true;
+                       postprocessing = true;
                        break;
                case FW_CMD_HIGH_PWR_ENABLE:
                        if (!(rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) &&
@@ -1608,7 +1620,7 @@ bool rtl92s_phy_set_fw_cmd(struct ieee80211_hw *hw, enum fwcmd_iotype fw_cmdio)
                                fw_cmdmap |= (FW_HIGH_PWR_ENABLE_CTL |
                                              FW_SS_CTL);
                                FW_CMD_IO_SET(rtlpriv, fw_cmdmap);
-                               bPostProcessing = true;
+                               postprocessing = true;
                        }
                        break;
                case FW_CMD_DIG_MODE_FA:
@@ -1629,14 +1641,15 @@ bool rtl92s_phy_set_fw_cmd(struct ieee80211_hw *hw, enum fwcmd_iotype fw_cmdio)
                default:
                        /* Pass to original FW CMD processing callback
                         * routine. */
-                       bPostProcessing = true;
+                       postprocessing = true;
                        break;
                }
        } while (false);
 
        /* We shall post processing these FW CMD if
-        * variable bPostProcessing is set. */
-       if (bPostProcessing && !rtlhal->set_fwcmd_inprogress) {
+        * variable postprocessing is set.
+        */
+       if (postprocessing && !rtlhal->set_fwcmd_inprogress) {
                rtlhal->set_fwcmd_inprogress = true;
                /* Update current FW Cmd for callback use. */
                rtlhal->current_fwcmd_io = fw_cmdio;
@@ -1697,8 +1710,18 @@ void rtl92s_phy_switch_ephy_parameter(struct ieee80211_hw *hw)
 
 }
 
-void rtl92s_phy_set_beacon_hwreg(struct ieee80211_hw *hw, u16 BeaconInterval)
+void rtl92s_phy_set_beacon_hwreg(struct ieee80211_hw *hw, u16 beaconinterval)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
-       rtl_write_dword(rtlpriv, WFM5, 0xF1000000 | (BeaconInterval << 8));
+       u32 new_bcn_num = 0;
+
+       if (hal_get_firmwareversion(rtlpriv) >= 0x33) {
+               /* Fw v.51 and later. */
+               rtl_write_dword(rtlpriv, WFM5, 0xF1000000 |
+                               (beaconinterval << 8));
+       } else {
+               new_bcn_num = beaconinterval * 32 - 64;
+               rtl_write_dword(rtlpriv, WFM3 + 4, new_bcn_num);
+               rtl_write_dword(rtlpriv, WFM3, 0xB026007C);
+       }
 }
index ac0387770630dfedb9dc7d682b898d2d2e78690c..8acf4765a7a6bad91269cc4471e1f120a13561e7 100644 (file)
@@ -39,6 +39,7 @@
 #define MAX_POSTCMD_CNT                        16
 
 #define RF90_PATH_MAX                  4
+#define RF6052_MAX_PATH                        2
 
 enum version_8192s {
        VERSION_8192S_ACUT,
index cecc377e9e6134ebb2fc709e34981228954485dc..2e8e6f8d2d513e18a77a3710542656c2e1953ec5 100644 (file)
@@ -290,6 +290,7 @@ static struct rtl_hal_ops rtl8192se_hal_ops = {
        .enable_hw_sec = rtl92se_enable_hw_security_config,
        .set_key = rtl92se_set_key,
        .init_sw_leds = rtl92se_init_sw_leds,
+       .allow_all_destaddr = rtl92se_allow_all_destaddr,
        .get_bbreg = rtl92s_phy_query_bb_reg,
        .set_bbreg = rtl92s_phy_set_bb_reg,
        .get_rfreg = rtl92s_phy_query_rf_reg,
@@ -366,7 +367,7 @@ static struct rtl_hal_cfg rtl92se_hal_cfg = {
 
        .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW,
        .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT,
-       .maps[RTL_IMR_BcnInt] = IMR_BCNINT,
+       .maps[RTL_IMR_BCNINT] = IMR_BCNINT,
        .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW,
        .maps[RTL_IMR_RDU] = IMR_RDU,
        .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND,
index 7b0a2e75b8b826dfeb6edf5a19e2136e76733e25..960bc28cc51e5227431b4ef154fa2c1f3372b0c9 100644 (file)
@@ -30,6 +30,7 @@
 #include "../wifi.h"
 #include "../pci.h"
 #include "../base.h"
+#include "../stats.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -43,7 +44,7 @@ static u8 _rtl92se_map_hwqueue_to_fwqueue(struct sk_buff *skb,        u8 skb_queue)
 
        if (unlikely(ieee80211_is_beacon(fc)))
                return QSLT_BEACON;
-       if (ieee80211_is_mgmt(fc))
+       if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))
                return QSLT_MGNT;
        if (ieee80211_is_nullfunc(fc))
                return QSLT_HIGH;
@@ -51,65 +52,6 @@ static u8 _rtl92se_map_hwqueue_to_fwqueue(struct sk_buff *skb,       u8 skb_queue)
        return skb->priority;
 }
 
-static u8 _rtl92s_query_rxpwrpercentage(char antpower)
-{
-       if ((antpower <= -100) || (antpower >= 20))
-               return 0;
-       else if (antpower >= 0)
-               return 100;
-       else
-               return 100 + antpower;
-}
-
-static u8 _rtl92s_evm_db_to_percentage(char value)
-{
-       char ret_val;
-       ret_val = value;
-
-       if (ret_val >= 0)
-               ret_val = 0;
-
-       if (ret_val <= -33)
-               ret_val = -33;
-
-       ret_val = 0 - ret_val;
-       ret_val *= 3;
-
-       if (ret_val == 99)
-               ret_val = 100;
-
-       return ret_val;
-}
-
-static long _rtl92se_translate_todbm(struct ieee80211_hw *hw,
-                                    u8 signal_strength_index)
-{
-       long signal_power;
-
-       signal_power = (long)((signal_strength_index + 1) >> 1);
-       signal_power -= 95;
-       return signal_power;
-}
-
-static long _rtl92se_signal_scale_mapping(struct ieee80211_hw *hw,
-               long currsig)
-{
-       long retsig = 0;
-
-       /* Step 1. Scale mapping. */
-       if (currsig > 47)
-               retsig = 100;
-       else if (currsig > 14 && currsig <= 47)
-               retsig = 100 - ((47 - currsig) * 3) / 2;
-       else if (currsig > 2 && currsig <= 14)
-               retsig = 48 - ((14 - currsig) * 15) / 7;
-       else if (currsig >= 0)
-               retsig = currsig * 9 + 1;
-
-       return retsig;
-}
-
-
 static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
                                       struct rtl_stats *pstats, u8 *pdesc,
                                       struct rx_fwinfo *p_drvinfo,
@@ -119,11 +61,11 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct phy_sts_cck_8192s_t *cck_buf;
+       struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv);
        s8 rx_pwr_all = 0, rx_pwr[4];
        u8 rf_rx_num = 0, evm, pwdb_all;
        u8 i, max_spatial_stream;
        u32 rssi, total_rssi = 0;
-       bool in_powersavemode = false;
        bool is_cck = pstats->is_cck;
 
        pstats->packet_matchbssid = packet_match_bssid;
@@ -136,7 +78,7 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
                u8 report, cck_highpwr;
                cck_buf = (struct phy_sts_cck_8192s_t *)p_drvinfo;
 
-               if (!in_powersavemode)
+               if (ppsc->rfpwr_state == ERFON)
                        cck_highpwr = (u8) rtl_get_bbreg(hw,
                                                RFPGA0_XA_HSSIPARAMETER2,
                                                0x200);
@@ -181,7 +123,7 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
                        }
                }
 
-               pwdb_all = _rtl92s_query_rxpwrpercentage(rx_pwr_all);
+               pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all);
 
                /* CCK gain is smaller than OFDM/MCS gain,  */
                /* so we add gain diff by experiences, the val is 6 */
@@ -222,13 +164,13 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
        } else {
                rtlpriv->dm.rfpath_rxenable[0] =
                    rtlpriv->dm.rfpath_rxenable[1] = true;
-               for (i = RF90_PATH_A; i < RF90_PATH_MAX; i++) {
+               for (i = RF90_PATH_A; i < RF6052_MAX_PATH; i++) {
                        if (rtlpriv->dm.rfpath_rxenable[i])
                                rf_rx_num++;
 
                        rx_pwr[i] = ((p_drvinfo->gain_trsw[i] &
                                    0x3f) * 2) - 110;
-                       rssi = _rtl92s_query_rxpwrpercentage(rx_pwr[i]);
+                       rssi = rtl_query_rxpwrpercentage(rx_pwr[i]);
                        total_rssi += rssi;
                        rtlpriv->stats.rx_snr_db[i] =
                                         (long)(p_drvinfo->rxsnr[i] / 2);
@@ -238,7 +180,7 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
                }
 
                rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110;
-               pwdb_all = _rtl92s_query_rxpwrpercentage(rx_pwr_all);
+               pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all);
                pstats->rx_pwdb_all = pwdb_all;
                pstats->rxpower = rx_pwr_all;
                pstats->recvsignalpower = rx_pwr_all;
@@ -250,7 +192,7 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
                        max_spatial_stream = 1;
 
                for (i = 0; i < max_spatial_stream; i++) {
-                       evm = _rtl92s_evm_db_to_percentage(p_drvinfo->rxevm[i]);
+                       evm = rtl_evm_db_to_percentage(p_drvinfo->rxevm[i]);
 
                        if (packet_match_bssid) {
                                if (i == 0)
@@ -262,212 +204,13 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw,
        }
 
        if (is_cck)
-               pstats->signalstrength = (u8)(_rtl92se_signal_scale_mapping(hw,
+               pstats->signalstrength = (u8)(rtl_signal_scale_mapping(hw,
                                         pwdb_all));
        else if (rf_rx_num != 0)
-               pstats->signalstrength = (u8) (_rtl92se_signal_scale_mapping(hw,
+               pstats->signalstrength = (u8) (rtl_signal_scale_mapping(hw,
                                total_rssi /= rf_rx_num));
 }
 
-static void _rtl92se_process_ui_rssi(struct ieee80211_hw *hw,
-                                    struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_phy *rtlphy = &(rtlpriv->phy);
-       u8 rfpath;
-       u32 last_rssi, tmpval;
-
-       if (pstats->packet_toself || pstats->packet_beacon) {
-               rtlpriv->stats.rssi_calculate_cnt++;
-
-               if (rtlpriv->stats.ui_rssi.total_num++ >=
-                   PHY_RSSI_SLID_WIN_MAX) {
-                       rtlpriv->stats.ui_rssi.total_num =
-                                        PHY_RSSI_SLID_WIN_MAX;
-                       last_rssi = rtlpriv->stats.ui_rssi.elements[
-                               rtlpriv->stats.ui_rssi.index];
-                       rtlpriv->stats.ui_rssi.total_val -= last_rssi;
-               }
-
-               rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength;
-               rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi.index++]
-                        = pstats->signalstrength;
-
-               if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX)
-                       rtlpriv->stats.ui_rssi.index = 0;
-
-               tmpval = rtlpriv->stats.ui_rssi.total_val /
-                       rtlpriv->stats.ui_rssi.total_num;
-               rtlpriv->stats.signal_strength = _rtl92se_translate_todbm(hw,
-                                                               (u8) tmpval);
-               pstats->rssi = rtlpriv->stats.signal_strength;
-       }
-
-       if (!pstats->is_cck && pstats->packet_toself) {
-               for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath;
-                    rfpath++) {
-                       if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   pstats->rx_mimo_signalstrength[rfpath];
-
-                       }
-
-                       if (pstats->rx_mimo_signalstrength[rfpath] >
-                           rtlpriv->stats.rx_rssi_percentage[rfpath]) {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   ((rtlpriv->stats.rx_rssi_percentage[rfpath]
-                                   * (RX_SMOOTH_FACTOR - 1)) +
-                                   (pstats->rx_mimo_signalstrength[rfpath])) /
-                                   (RX_SMOOTH_FACTOR);
-
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   rtlpriv->stats.rx_rssi_percentage[rfpath]
-                                   + 1;
-                       } else {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   ((rtlpriv->stats.rx_rssi_percentage[rfpath]
-                                   * (RX_SMOOTH_FACTOR - 1)) +
-                                   (pstats->rx_mimo_signalstrength[rfpath])) /
-                                   (RX_SMOOTH_FACTOR);
-                       }
-
-               }
-       }
-}
-
-static void _rtl92se_update_rxsignalstatistics(struct ieee80211_hw *hw,
-                                              struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       int weighting = 0;
-
-       if (rtlpriv->stats.recv_signal_power == 0)
-               rtlpriv->stats.recv_signal_power = pstats->recvsignalpower;
-
-       if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power)
-               weighting = 5;
-       else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power)
-               weighting = (-5);
-
-       rtlpriv->stats.recv_signal_power = (rtlpriv->stats.recv_signal_power * 5
-                                          + pstats->recvsignalpower +
-                                          weighting) / 6;
-}
-
-static void _rtl92se_process_pwdb(struct ieee80211_hw *hw,
-                                 struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
-       long undec_sm_pwdb = 0;
-
-       if (mac->opmode == NL80211_IFTYPE_ADHOC) {
-               return;
-       } else {
-               undec_sm_pwdb =
-                   rtlpriv->dm.undec_sm_pwdb;
-       }
-
-       if (pstats->packet_toself || pstats->packet_beacon) {
-               if (undec_sm_pwdb < 0)
-                       undec_sm_pwdb = pstats->rx_pwdb_all;
-
-               if (pstats->rx_pwdb_all > (u32) undec_sm_pwdb) {
-                       undec_sm_pwdb =
-                           (((undec_sm_pwdb) *
-                           (RX_SMOOTH_FACTOR - 1)) +
-                           (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR);
-
-                       undec_sm_pwdb = undec_sm_pwdb + 1;
-               } else {
-                       undec_sm_pwdb = (((undec_sm_pwdb) *
-                             (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) /
-                             (RX_SMOOTH_FACTOR);
-               }
-
-               rtlpriv->dm.undec_sm_pwdb = undec_sm_pwdb;
-               _rtl92se_update_rxsignalstatistics(hw, pstats);
-       }
-}
-
-static void rtl_92s_process_streams(struct ieee80211_hw *hw,
-                                   struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       u32 stream;
-
-       for (stream = 0; stream < 2; stream++) {
-               if (pstats->rx_mimo_sig_qual[stream] != -1) {
-                       if (rtlpriv->stats.rx_evm_percentage[stream] == 0) {
-                               rtlpriv->stats.rx_evm_percentage[stream] =
-                                   pstats->rx_mimo_sig_qual[stream];
-                       }
-
-                       rtlpriv->stats.rx_evm_percentage[stream] =
-                           ((rtlpriv->stats.rx_evm_percentage[stream] *
-                                       (RX_SMOOTH_FACTOR - 1)) +
-                            (pstats->rx_mimo_sig_qual[stream] *
-                                       1)) / (RX_SMOOTH_FACTOR);
-               }
-       }
-}
-
-static void _rtl92se_process_ui_link_quality(struct ieee80211_hw *hw,
-                                            struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       u32 last_evm = 0, tmpval;
-
-       if (pstats->signalquality != 0) {
-               if (pstats->packet_toself || pstats->packet_beacon) {
-
-                       if (rtlpriv->stats.ui_link_quality.total_num++ >=
-                           PHY_LINKQUALITY_SLID_WIN_MAX) {
-                               rtlpriv->stats.ui_link_quality.total_num =
-                                   PHY_LINKQUALITY_SLID_WIN_MAX;
-                               last_evm =
-                                   rtlpriv->stats.ui_link_quality.elements[
-                                   rtlpriv->stats.ui_link_quality.index];
-                               rtlpriv->stats.ui_link_quality.total_val -=
-                                   last_evm;
-                       }
-
-                       rtlpriv->stats.ui_link_quality.total_val +=
-                           pstats->signalquality;
-                       rtlpriv->stats.ui_link_quality.elements[
-                               rtlpriv->stats.ui_link_quality.index++] =
-                           pstats->signalquality;
-
-                       if (rtlpriv->stats.ui_link_quality.index >=
-                           PHY_LINKQUALITY_SLID_WIN_MAX)
-                               rtlpriv->stats.ui_link_quality.index = 0;
-
-                       tmpval = rtlpriv->stats.ui_link_quality.total_val /
-                           rtlpriv->stats.ui_link_quality.total_num;
-                       rtlpriv->stats.signal_quality = tmpval;
-
-                       rtlpriv->stats.last_sigstrength_inpercent = tmpval;
-
-                       rtl_92s_process_streams(hw, pstats);
-
-               }
-       }
-}
-
-static void _rtl92se_process_phyinfo(struct ieee80211_hw *hw,
-                                    u8 *buffer,
-                                    struct rtl_stats *pcurrent_stats)
-{
-
-       if (!pcurrent_stats->packet_matchbssid &&
-           !pcurrent_stats->packet_beacon)
-               return;
-
-       _rtl92se_process_ui_rssi(hw, pcurrent_stats);
-       _rtl92se_process_pwdb(hw, pcurrent_stats);
-       _rtl92se_process_ui_link_quality(hw, pcurrent_stats);
-}
-
 static void _rtl92se_translate_rx_signal_stuff(struct ieee80211_hw *hw,
                struct sk_buff *skb, struct rtl_stats *pstats,
                u8 *pdesc, struct rx_fwinfo *p_drvinfo)
@@ -505,7 +248,7 @@ static void _rtl92se_translate_rx_signal_stuff(struct ieee80211_hw *hw,
 
        _rtl92se_query_rxphystatus(hw, pstats, pdesc, p_drvinfo,
                        packet_matchbssid, packet_toself, packet_beacon);
-       _rtl92se_process_phyinfo(hw, tmp_buf, pstats);
+       rtl_process_phyinfo(hw, tmp_buf, pstats);
 }
 
 bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
@@ -541,9 +284,6 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
        rx_status->freq = hw->conf.channel->center_freq;
        rx_status->band = hw->conf.channel->band;
 
-       hdr = (struct ieee80211_hdr *)(skb->data + stats->rx_drvinfo_size
-             + stats->rx_bufshift);
-
        if (stats->crc)
                rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
 
@@ -563,6 +303,13 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
         * for IEEE80211w frame, and mac80211 sw will help
         * to decrypt it */
        if (stats->decrypted) {
+               hdr = (struct ieee80211_hdr *)(skb->data +
+                      stats->rx_drvinfo_size + stats->rx_bufshift);
+
+               if (!hdr) {
+                       /* during testing, hdr was NULL here */
+                       return false;
+               }
                if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
                        (ieee80211_has_protected(hdr->frame_control)))
                        rx_status->flag &= ~RX_FLAG_DECRYPTED;
@@ -630,6 +377,11 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw,
 
        CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE_RTL8192S);
 
+       if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) {
+               firstseg = true;
+               lastseg = true;
+       }
+
        if (firstseg) {
                if (rtlpriv->dm.useramask) {
                        /* set txdesc macId */
index 12e2a3cb07015f4c8b266ff3b747b4edb2aa2240..a36eee28f9e7f7fe092b3f443a205e67292ef52c 100644 (file)
@@ -166,8 +166,8 @@ static void rtl8723ae_dm_diginit(struct ieee80211_hw *hw)
        dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
        dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
        dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-       dm_digtable->rx_gain_range_max = DM_DIG_MAX;
-       dm_digtable->rx_gain_range_min = DM_DIG_MIN;
+       dm_digtable->rx_gain_max = DM_DIG_MAX;
+       dm_digtable->rx_gain_min = DM_DIG_MIN;
        dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
        dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
        dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
@@ -291,11 +291,11 @@ static void rtl92c_dm_ctrl_initgain_by_rssi(struct ieee80211_hw *hw)
        }
 
        if ((dgtbl->rssi_val_min + 10 - dgtbl->back_val) >
-           dgtbl->rx_gain_range_max)
-               dgtbl->cur_igvalue = dgtbl->rx_gain_range_max;
+           dgtbl->rx_gain_max)
+               dgtbl->cur_igvalue = dgtbl->rx_gain_max;
        else if ((dgtbl->rssi_val_min + 10 -
-                 dgtbl->back_val) < dgtbl->rx_gain_range_min)
-               dgtbl->cur_igvalue = dgtbl->rx_gain_range_min;
+                 dgtbl->back_val) < dgtbl->rx_gain_min)
+               dgtbl->cur_igvalue = dgtbl->rx_gain_min;
        else
                dgtbl->cur_igvalue = dgtbl->rssi_val_min + 10 - dgtbl->back_val;
 
@@ -707,6 +707,77 @@ void rtl8723ae_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
                rtlpriv->dm.useramask = false;
 }
 
+static void rtl8723ae_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rate_adaptive *p_ra = &(rtlpriv->ra);
+       u32 low_rssithresh_for_ra, high_rssithresh_for_ra;
+       struct ieee80211_sta *sta = NULL;
+
+       if (is_hal_stop(rtlhal)) {
+               RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                        " driver is going to unload\n");
+               return;
+       }
+
+       if (!rtlpriv->dm.useramask) {
+               RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                        " driver does not control rate adaptive mask\n");
+               return;
+       }
+
+       if (mac->link_state == MAC80211_LINKED &&
+           mac->opmode == NL80211_IFTYPE_STATION) {
+               switch (p_ra->pre_ratr_state) {
+               case DM_RATR_STA_HIGH:
+                       high_rssithresh_for_ra = 50;
+                       low_rssithresh_for_ra = 20;
+                       break;
+               case DM_RATR_STA_MIDDLE:
+                       high_rssithresh_for_ra = 55;
+                       low_rssithresh_for_ra = 20;
+                       break;
+               case DM_RATR_STA_LOW:
+                       high_rssithresh_for_ra = 50;
+                       low_rssithresh_for_ra = 25;
+                       break;
+               default:
+                       high_rssithresh_for_ra = 50;
+                       low_rssithresh_for_ra = 20;
+                       break;
+               }
+
+               if (rtlpriv->dm.undec_sm_pwdb > high_rssithresh_for_ra)
+                       p_ra->ratr_state = DM_RATR_STA_HIGH;
+               else if (rtlpriv->dm.undec_sm_pwdb > low_rssithresh_for_ra)
+                       p_ra->ratr_state = DM_RATR_STA_MIDDLE;
+               else
+                       p_ra->ratr_state = DM_RATR_STA_LOW;
+
+               if (p_ra->pre_ratr_state != p_ra->ratr_state) {
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "RSSI = %ld\n",
+                                rtlpriv->dm.undec_sm_pwdb);
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "RSSI_LEVEL = %d\n", p_ra->ratr_state);
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "PreState = %d, CurState = %d\n",
+                                p_ra->pre_ratr_state, p_ra->ratr_state);
+
+                       rcu_read_lock();
+                       sta = rtl_find_sta(hw, mac->bssid);
+                       if (sta)
+                               rtlpriv->cfg->ops->update_rate_tbl(hw, sta,
+                                                          p_ra->ratr_state);
+                       rcu_read_unlock();
+
+                       p_ra->pre_ratr_state = p_ra->ratr_state;
+               }
+       }
+}
+
 static void rtl8723ae_dm_init_dynamic_bpowersaving(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -853,6 +924,9 @@ void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw)
        rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FWLPS_RF_ON,
                                      (u8 *) (&fw_ps_awake));
 
+       if (ppsc->p2p_ps_info.p2p_ps_mode)
+               fw_ps_awake = false;
+
        if ((ppsc->rfpwr_state == ERFON) &&
            ((!fw_current_inpsmode) && fw_ps_awake) &&
            (!ppsc->rfchange_inprogress)) {
@@ -861,7 +935,7 @@ void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw)
                rtl8723ae_dm_false_alarm_counter_statistics(hw);
                rtl8723ae_dm_dynamic_bpowersaving(hw);
                rtl8723ae_dm_dynamic_txpower(hw);
-               /* rtl92c_dm_refresh_rate_adaptive_mask(hw); */
+               rtl8723ae_dm_refresh_rate_adaptive_mask(hw);
                rtl8723ae_dm_bt_coexist(hw);
                rtl8723ae_dm_check_edca_turbo(hw);
        }
index 39d2461962476029087fb23cb989941aef0b1a55..a372b0204456bbfee8f27205338697cd9dc8cba6 100644 (file)
 #define DM_DIG_BACKOFF_MIN                     -4
 #define DM_DIG_BACKOFF_DEFAULT                 10
 
+#define RXPATHSELECTION_SS_TH_LOW              30
+#define RXPATHSELECTION_DIFF_TH                        18
+
 #define DM_RATR_STA_INIT                       0
+#define DM_RATR_STA_HIGH                       1
+#define DM_RATR_STA_MIDDLE                     2
+#define DM_RATR_STA_LOW                                3
 
 #define TXHIGHPWRLEVEL_NORMAL                  0
 #define TXHIGHPWRLEVEL_LEVEL1                  1
index 35cb8f83eed4c2b4fcf9c008c49650bd65a85fa3..dedfa1ed3e02e66fa1b2ef687592709356c1e724 100644 (file)
@@ -494,7 +494,9 @@ void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
        RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
 
        SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode);
-       SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1);
+       SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode,
+                                        (rtlpriv->mac80211.p2p) ?
+                                        ppsc->smart_ps : 1);
        SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode,
                                              ppsc->reg_max_lps_awakeintvl);
 
@@ -741,3 +743,96 @@ void rtl8723ae_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus)
 
        rtl8723ae_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm);
 }
+
+static void rtl8723e_set_p2p_ctw_period_cmd(struct ieee80211_hw *hw,
+                                           u8 ctwindow)
+{
+       u8 u1_ctwindow_period[1] = {ctwindow};
+
+       rtl8723ae_fill_h2c_cmd(hw, H2C_P2P_PS_CTW_CMD, 1, u1_ctwindow_period);
+}
+
+void rtl8723ae_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_p2p_ps_info *p2pinfo = &(rtlps->p2p_ps_info);
+       struct p2p_ps_offload_t *p2p_ps_offload = &rtlhal->p2p_ps_offload;
+       u8      i;
+       u16     ctwindow;
+       u32     start_time, tsf_low;
+
+       switch (p2p_ps_state) {
+       case P2P_PS_DISABLE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_DISABLE\n");
+               memset(p2p_ps_offload, 0, sizeof(struct p2p_ps_offload_t));
+               break;
+       case P2P_PS_ENABLE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_ENABLE\n");
+               /* update CTWindow value. */
+               if (p2pinfo->ctwindow > 0) {
+                       p2p_ps_offload->ctwindow_en = 1;
+                       ctwindow = p2pinfo->ctwindow;
+                       rtl8723e_set_p2p_ctw_period_cmd(hw, ctwindow);
+               }
+
+               /* hw only support 2 set of NoA */
+               for (i = 0; i < p2pinfo->noa_num; i++) {
+                       /* To control the register setting for which NOA*/
+                       rtl_write_byte(rtlpriv, 0x5cf, (i << 4));
+                       if (i == 0)
+                               p2p_ps_offload->noa0_en = 1;
+                       else
+                               p2p_ps_offload->noa1_en = 1;
+
+                       /* config P2P NoA Descriptor Register */
+                       rtl_write_dword(rtlpriv, 0x5E0,
+                                       p2pinfo->noa_duration[i]);
+                       rtl_write_dword(rtlpriv, 0x5E4,
+                                       p2pinfo->noa_interval[i]);
+
+                       /*Get Current TSF value */
+                       tsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
+
+                       start_time = p2pinfo->noa_start_time[i];
+                       if (p2pinfo->noa_count_type[i] != 1) {
+                               while (start_time <= (tsf_low+(50*1024))) {
+                                       start_time += p2pinfo->noa_interval[i];
+                                       if (p2pinfo->noa_count_type[i] != 255)
+                                               p2pinfo->noa_count_type[i]--;
+                               }
+                       }
+                       rtl_write_dword(rtlpriv, 0x5E8, start_time);
+                       rtl_write_dword(rtlpriv, 0x5EC,
+                                       p2pinfo->noa_count_type[i]);
+               }
+               if ((p2pinfo->opp_ps == 1) || (p2pinfo->noa_num > 0)) {
+                       /* rst p2p circuit */
+                       rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, BIT(4));
+
+                       p2p_ps_offload->offload_en = 1;
+
+                       if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) {
+                               p2p_ps_offload->role = 1;
+                               p2p_ps_offload->allstasleep = 0;
+                       } else {
+                               p2p_ps_offload->role = 0;
+                       }
+                       p2p_ps_offload->discovery = 0;
+               }
+               break;
+       case P2P_PS_SCAN:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN\n");
+               p2p_ps_offload->discovery = 1;
+               break;
+       case P2P_PS_SCAN_DONE:
+               RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN_DONE\n");
+               p2p_ps_offload->discovery = 0;
+               p2pinfo->p2p_ps_state = P2P_PS_ENABLE;
+               break;
+       default:
+               break;
+       }
+       rtl8723ae_fill_h2c_cmd(hw, H2C_P2P_PS_OFFLOAD, 1, (u8 *)p2p_ps_offload);
+}
index 89994e16dc83eb5c435468bbe1fbf7464c921141..ed3b795e6980781a738f5883cb63401e28458970 100644 (file)
@@ -70,8 +70,10 @@ enum rtl8192c_h2c_cmd {
        H2C_SETPWRMODE = 1,
        H2C_JOINBSSRPT = 2,
        H2C_RSVDPAGE = 3,
-       H2C_RSSI_REPORT = 5,
-       H2C_RA_MASK = 6,
+       H2C_RSSI_REPORT = 4,
+       H2C_P2P_PS_CTW_CMD = 5,
+       H2C_P2P_PS_OFFLOAD = 6,
+       H2C_RA_MASK = 7,
        MAX_H2CCMD
 };
 
@@ -97,5 +99,6 @@ void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw);
 void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
 void rtl8723ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
 void rtl8723ae_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
+void rtl8723ae_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state);
 
 #endif
index 9a0c71c2e15ecdf4fa60a345391ecabba8d2fad1..c333dfd116b868a8aff3dcf780e04611860d7951 100644 (file)
@@ -449,6 +449,9 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                rtl8723ae_set_fw_joinbss_report_cmd(hw, (*(u8 *) val));
 
                break; }
+       case HW_VAR_H2C_FW_P2P_PS_OFFLOAD:
+               rtl8723ae_set_p2p_ps_offload_cmd(hw, (*(u8 *)val));
+               break;
        case HW_VAR_AID:{
                u16 u2btmp;
                u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT);
@@ -474,6 +477,39 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                if (btype_ibss == true)
                        _rtl8723ae_resume_tx_beacon(hw);
                break; }
+       case HW_VAR_FW_LPS_ACTION: {
+               bool enter_fwlps = *((bool *)val);
+               u8 rpwm_val, fw_pwrmode;
+               bool fw_current_inps;
+
+               if (enter_fwlps) {
+                       rpwm_val = 0x02;        /* RF off */
+                       fw_current_inps = true;
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_FW_PSMODE_STATUS,
+                                       (u8 *)(&fw_current_inps));
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_H2C_FW_PWRMODE,
+                                       (u8 *)(&ppsc->fwctrl_psmode));
+
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_SET_RPWM,
+                                       (u8 *)(&rpwm_val));
+               } else {
+                       rpwm_val = 0x0C;        /* RF on */
+                       fw_pwrmode = FW_PS_ACTIVE_MODE;
+                       fw_current_inps = false;
+                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
+                                       (u8 *)(&rpwm_val));
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_H2C_FW_PWRMODE,
+                                       (u8 *)(&fw_pwrmode));
+
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                       HW_VAR_FW_PSMODE_STATUS,
+                                       (u8 *)(&fw_current_inps));
+               }
+               break; }
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         "switch case not processed\n");
@@ -1379,7 +1415,7 @@ static void _rtl8723ae_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                }
 
                for (i = 0; i < 14; i++) {
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF(%d)-Ch(%d) [CCK / HT40_1S / HT40_2S] = "
                                "[0x%x / 0x%x / 0x%x]\n", rf_path, i,
                                rtlefuse->txpwrlevel_cck[rf_path][i],
@@ -1420,10 +1456,10 @@ static void _rtl8723ae_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                                    0xf0) >> 4);
                        }
 
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF-%d pwrgroup_ht20[%d] = 0x%x\n", rf_path, i,
                                rtlefuse->pwrgroup_ht20[rf_path][i]);
-                       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+                       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                                "RF-%d pwrgroup_ht40[%d] = 0x%x\n", rf_path, i,
                                rtlefuse->pwrgroup_ht40[rf_path][i]);
                }
@@ -1463,19 +1499,19 @@ static void _rtl8723ae_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
            rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][7];
 
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-A Ht20 to HT40 Diff[%d] = 0x%x\n", i,
                        rtlefuse->txpwr_ht20diff[RF90_PATH_A][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-A Legacy to Ht40 Diff[%d] = 0x%x\n", i,
                        rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-B Ht20 to HT40 Diff[%d] = 0x%x\n", i,
                        rtlefuse->txpwr_ht20diff[RF90_PATH_B][i]);
        for (i = 0; i < 14; i++)
-               RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+               RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                        "RF-B Legacy to HT40 Diff[%d] = 0x%x\n", i,
                        rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]);
 
@@ -1483,14 +1519,14 @@ static void _rtl8723ae_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                rtlefuse->eeprom_regulatory = (hwinfo[RF_OPTION1] & 0x7);
        else
                rtlefuse->eeprom_regulatory = 0;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory);
 
        if (!autoload_fail)
                rtlefuse->eeprom_tssi[RF90_PATH_A] = hwinfo[EEPROM_TSSI_A];
        else
                rtlefuse->eeprom_tssi[RF90_PATH_A] = EEPROM_DEFAULT_TSSI;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "TSSI_A = 0x%x, TSSI_B = 0x%x\n",
                rtlefuse->eeprom_tssi[RF90_PATH_A],
                rtlefuse->eeprom_tssi[RF90_PATH_B]);
@@ -1505,7 +1541,7 @@ static void _rtl8723ae_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
                rtlefuse->apk_thermalmeterignore = true;
 
        rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter;
-       RTPRINT(rtlpriv, FINIT, INIT_TxPower,
+       RTPRINT(rtlpriv, FINIT, INIT_TXPOWER,
                "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter);
 }
 
@@ -1713,19 +1749,7 @@ static void _rtl8723ae_hal_customized_behavior(struct ieee80211_hw *hw)
        struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 
-       switch (rtlhal->oem_id) {
-       case RT_CID_819x_HP:
-               pcipriv->ledctl.led_opendrain = true;
-               break;
-       case RT_CID_819x_Lenovo:
-       case RT_CID_DEFAULT:
-       case RT_CID_TOSHIBA:
-       case RT_CID_CCX:
-       case RT_CID_819x_Acer:
-       case RT_CID_WHQL:
-       default:
-               break;
-       }
+       pcipriv->ledctl.led_opendrain = true;
        RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
                 "RT Customized ID: 0x%02X\n", rtlhal->oem_id);
 }
index 9c4e1d81118720b0bf89c1be90a15f2f7b1161e9..061526fe6e2d1222b4fb8050fc6dc38fa63013e7 100644 (file)
@@ -54,8 +54,9 @@ void rtl8723ae_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
        case LED_PIN_GPIO0:
                break;
        case LED_PIN_LED0:
+               ledcfg &= ~BIT(6);
                rtl_write_byte(rtlpriv,
-                              REG_LEDCFG2, (ledcfg & 0xf0) | BIT(5) | BIT(6));
+                              REG_LEDCFG2, (ledcfg & 0xf0) | BIT(5));
                break;
        case LED_PIN_LED1:
                rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0x0f) | BIT(5));
@@ -84,16 +85,21 @@ void rtl8723ae_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
                break;
        case LED_PIN_LED0:
                ledcfg &= 0xf0;
-               if (pcipriv->ledctl.led_opendrain)
+               if (pcipriv->ledctl.led_opendrain) {
+                       ledcfg &= 0x90;
+                       rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg|BIT(3)));
+                       ledcfg = rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG);
+                       ledcfg &= 0xFE;
+                       rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, ledcfg);
+               } else {
+                       ledcfg &= ~BIT(6);
                        rtl_write_byte(rtlpriv, REG_LEDCFG2,
-                                      (ledcfg | BIT(1) | BIT(5) | BIT(6)));
-               else
-                       rtl_write_byte(rtlpriv, REG_LEDCFG2,
-                                      (ledcfg | BIT(3) | BIT(5) | BIT(6)));
+                                      (ledcfg | BIT(3) | BIT(5)));
+               }
                break;
        case LED_PIN_LED1:
-               ledcfg &= 0x0f;
-               rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3)));
+               ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1) & 0x10;
+               rtl_write_byte(rtlpriv, REG_LEDCFG1, (ledcfg | BIT(3)));
                break;
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
index bb7cc90bafb22f8c5bdbc61672b527a78442a715..e4c4cdc3eb67f6d992c29b026d98f244400e696b 100644 (file)
@@ -305,7 +305,7 @@ static struct rtl_hal_cfg rtl8723ae_hal_cfg = {
 
        .maps[RTL_IMR_TXFOVW] = PHIMR_TXFOVW,
        .maps[RTL_IMR_PSTIMEOUT] = PHIMR_PSTIMEOUT,
-       .maps[RTL_IMR_BcnInt] = PHIMR_BCNDMAINT0,
+       .maps[RTL_IMR_BCNINT] = PHIMR_BCNDMAINT0,
        .maps[RTL_IMR_RXFOVW] = PHIMR_RXFOVW,
        .maps[RTL_IMR_RDU] = PHIMR_RDU,
        .maps[RTL_IMR_ATIMEND] = PHIMR_ATIMEND_E,
index ac081297db50ddd454bbaa9963bddbfce487b653..6c64365308d3780f1bd84e1f6c4dcb5177fb9be1 100644 (file)
@@ -307,9 +307,6 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw,
        rx_status->freq = hw->conf.channel->center_freq;
        rx_status->band = hw->conf.channel->band;
 
-       hdr = (struct ieee80211_hdr *)(skb->data + status->rx_drvinfo_size
-               + status->rx_bufshift);
-
        if (status->crc)
                rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
 
@@ -330,6 +327,13 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw,
         * to decrypt it
         */
        if (status->decrypted) {
+               hdr = (struct ieee80211_hdr *)(skb->data +
+                      status->rx_drvinfo_size + status->rx_bufshift);
+
+               if (!hdr) {
+                       /* during testing, hdr could be NULL here */
+                       return false;
+               }
                if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
                        (ieee80211_has_protected(hdr->frame_control)))
                        rx_status->flag &= ~RX_FLAG_DECRYPTED;
index 5847d6d0881e7ce30180d26cc275425c55eda76b..83915dcd0e58505ca8970352e2fbac35dd1d48a8 100644 (file)
@@ -224,10 +224,9 @@ static void _usb_writeN_sync(struct rtl_priv *rtlpriv, u32 addr, void *data,
        u8 *buffer;
 
        wvalue = (u16)(addr & 0x0000ffff);
-       buffer = kmalloc(len, GFP_ATOMIC);
+       buffer = kmemdup(data, len, GFP_ATOMIC);
        if (!buffer)
                return;
-       memcpy(buffer, data, len);
        usb_control_msg(udev, pipe, request, reqtype, wvalue,
                        index, buffer, len, 50);
 
@@ -309,6 +308,8 @@ static int _rtl_usb_init_tx(struct ieee80211_hw *hw)
        return 0;
 }
 
+static void _rtl_rx_work(unsigned long param);
+
 static int _rtl_usb_init_rx(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -325,6 +326,12 @@ static int _rtl_usb_init_rx(struct ieee80211_hw *hw)
        pr_info("rx_max_size %d, rx_urb_num %d, in_ep %d\n",
                rtlusb->rx_max_size, rtlusb->rx_urb_num, rtlusb->in_ep);
        init_usb_anchor(&rtlusb->rx_submitted);
+       init_usb_anchor(&rtlusb->rx_cleanup_urbs);
+
+       skb_queue_head_init(&rtlusb->rx_queue);
+       rtlusb->rx_work_tasklet.func = _rtl_rx_work;
+       rtlusb->rx_work_tasklet.data = (unsigned long)rtlusb;
+
        return 0;
 }
 
@@ -406,40 +413,30 @@ static void rtl_usb_init_sw(struct ieee80211_hw *hw)
        rtlusb->disableHWSM =  true;
 }
 
-#define __RADIO_TAP_SIZE_RSV   32
-
 static void _rtl_rx_completed(struct urb *urb);
 
-static struct sk_buff *_rtl_prep_rx_urb(struct ieee80211_hw *hw,
-                                       struct rtl_usb *rtlusb,
-                                       struct urb *urb,
-                                       gfp_t gfp_mask)
+static int _rtl_prep_rx_urb(struct ieee80211_hw *hw, struct rtl_usb *rtlusb,
+                             struct urb *urb, gfp_t gfp_mask)
 {
-       struct sk_buff *skb;
        struct rtl_priv *rtlpriv = rtl_priv(hw);
+       void *buf;
 
-       skb = __dev_alloc_skb((rtlusb->rx_max_size + __RADIO_TAP_SIZE_RSV),
-                              gfp_mask);
-       if (!skb) {
+       buf = usb_alloc_coherent(rtlusb->udev, rtlusb->rx_max_size, gfp_mask,
+                                &urb->transfer_dma);
+       if (!buf) {
                RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG,
-                        "Failed to __dev_alloc_skb!!\n");
-               return ERR_PTR(-ENOMEM);
+                        "Failed to usb_alloc_coherent!!\n");
+               return -ENOMEM;
        }
 
-       /* reserve some space for mac80211's radiotap */
-       skb_reserve(skb, __RADIO_TAP_SIZE_RSV);
        usb_fill_bulk_urb(urb, rtlusb->udev,
                          usb_rcvbulkpipe(rtlusb->udev, rtlusb->in_ep),
-                         skb->data, min(skb_tailroom(skb),
-                         (int)rtlusb->rx_max_size),
-                         _rtl_rx_completed, skb);
+                         buf, rtlusb->rx_max_size, _rtl_rx_completed, rtlusb);
+       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
-       _rtl_install_trx_info(rtlusb, skb, rtlusb->in_ep);
-       return skb;
+       return 0;
 }
 
-#undef __RADIO_TAP_SIZE_RSV
-
 static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw,
                                    struct sk_buff *skb)
 {
@@ -523,22 +520,11 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw,
                        if (unicast)
                                rtlpriv->link_info.num_rx_inperiod++;
                }
-               if (likely(rtl_action_proc(hw, skb, false))) {
-                       struct sk_buff *uskb = NULL;
-                       u8 *pdata;
-
-                       uskb = dev_alloc_skb(skb->len + 128);
-                       if (uskb) {     /* drop packet on allocation failure */
-                               memcpy(IEEE80211_SKB_RXCB(uskb), &rx_status,
-                                      sizeof(rx_status));
-                               pdata = (u8 *)skb_put(uskb, skb->len);
-                               memcpy(pdata, skb->data, skb->len);
-                               ieee80211_rx_irqsafe(hw, uskb);
-                       }
-                       dev_kfree_skb_any(skb);
-               } else {
+
+               if (likely(rtl_action_proc(hw, skb, false)))
+                       ieee80211_rx(hw, skb);
+               else
                        dev_kfree_skb_any(skb);
-               }
        }
 }
 
@@ -555,15 +541,70 @@ static void _rtl_rx_pre_process(struct ieee80211_hw *hw, struct sk_buff *skb)
        while (!skb_queue_empty(&rx_queue)) {
                _skb = skb_dequeue(&rx_queue);
                _rtl_usb_rx_process_agg(hw, _skb);
-               ieee80211_rx_irqsafe(hw, _skb);
+               ieee80211_rx(hw, _skb);
        }
 }
 
+#define __RX_SKB_MAX_QUEUED    32
+
+static void _rtl_rx_work(unsigned long param)
+{
+       struct rtl_usb *rtlusb = (struct rtl_usb *)param;
+       struct ieee80211_hw *hw = usb_get_intfdata(rtlusb->intf);
+       struct sk_buff *skb;
+
+       while ((skb = skb_dequeue(&rtlusb->rx_queue))) {
+               if (unlikely(IS_USB_STOP(rtlusb))) {
+                       dev_kfree_skb_any(skb);
+                       continue;
+               }
+
+               if (likely(!rtlusb->usb_rx_segregate_hdl)) {
+                       _rtl_usb_rx_process_noagg(hw, skb);
+               } else {
+                       /* TO DO */
+                       _rtl_rx_pre_process(hw, skb);
+                       pr_err("rx agg not supported\n");
+               }
+       }
+}
+
+static unsigned int _rtl_rx_get_padding(struct ieee80211_hdr *hdr,
+                                       unsigned int len)
+{
+       unsigned int padding = 0;
+
+       /* make function no-op when possible */
+       if (NET_IP_ALIGN == 0 || len < sizeof(*hdr))
+               return 0;
+
+       /* alignment calculation as in lbtf_rx() / carl9170_rx_copy_data() */
+       /* TODO: deduplicate common code, define helper function instead? */
+
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+
+               padding ^= NET_IP_ALIGN;
+
+               /* Input might be invalid, avoid accessing memory outside
+                * the buffer.
+                */
+               if ((unsigned long)qc - (unsigned long)hdr < len &&
+                   *qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
+                       padding ^= NET_IP_ALIGN;
+       }
+
+       if (ieee80211_has_a4(hdr->frame_control))
+               padding ^= NET_IP_ALIGN;
+
+       return padding;
+}
+
+#define __RADIO_TAP_SIZE_RSV   32
+
 static void _rtl_rx_completed(struct urb *_urb)
 {
-       struct sk_buff *skb = (struct sk_buff *)_urb->context;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       struct rtl_usb *rtlusb = (struct rtl_usb *)info->rate_driver_data[0];
+       struct rtl_usb *rtlusb = (struct rtl_usb *)_urb->context;
        struct ieee80211_hw *hw = usb_get_intfdata(rtlusb->intf);
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        int err = 0;
@@ -572,28 +613,50 @@ static void _rtl_rx_completed(struct urb *_urb)
                goto free;
 
        if (likely(0 == _urb->status)) {
-               /* If this code were moved to work queue, would CPU
-                * utilization be improved?  NOTE: We shall allocate another skb
-                * and reuse the original one.
-                */
-               skb_put(skb, _urb->actual_length);
+               unsigned int padding;
+               struct sk_buff *skb;
+               unsigned int qlen;
+               unsigned int size = _urb->actual_length;
+               struct ieee80211_hdr *hdr;
 
-               if (likely(!rtlusb->usb_rx_segregate_hdl)) {
-                       struct sk_buff *_skb;
-                       _rtl_usb_rx_process_noagg(hw, skb);
-                       _skb = _rtl_prep_rx_urb(hw, rtlusb, _urb, GFP_ATOMIC);
-                       if (IS_ERR(_skb)) {
-                               err = PTR_ERR(_skb);
-                               RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG,
-                                        "Can't allocate skb for bulk IN!\n");
-                               return;
-                       }
-                       skb = _skb;
-               } else{
-                       /* TO DO */
-                       _rtl_rx_pre_process(hw, skb);
-                       pr_err("rx agg not supported\n");
+               if (size < RTL_RX_DESC_SIZE + sizeof(struct ieee80211_hdr)) {
+                       RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG,
+                                "Too short packet from bulk IN! (len: %d)\n",
+                                size);
+                       goto resubmit;
+               }
+
+               qlen = skb_queue_len(&rtlusb->rx_queue);
+               if (qlen >= __RX_SKB_MAX_QUEUED) {
+                       RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG,
+                                "Pending RX skbuff queue full! (qlen: %d)\n",
+                                qlen);
+                       goto resubmit;
                }
+
+               hdr = (void *)(_urb->transfer_buffer + RTL_RX_DESC_SIZE);
+               padding = _rtl_rx_get_padding(hdr, size - RTL_RX_DESC_SIZE);
+
+               skb = dev_alloc_skb(size + __RADIO_TAP_SIZE_RSV + padding);
+               if (!skb) {
+                       RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG,
+                                "Can't allocate skb for bulk IN!\n");
+                       goto resubmit;
+               }
+
+               _rtl_install_trx_info(rtlusb, skb, rtlusb->in_ep);
+
+               /* Make sure the payload data is 4 byte aligned. */
+               skb_reserve(skb, padding);
+
+               /* reserve some space for mac80211's radiotap */
+               skb_reserve(skb, __RADIO_TAP_SIZE_RSV);
+
+               memcpy(skb_put(skb, size), _urb->transfer_buffer, size);
+
+               skb_queue_tail(&rtlusb->rx_queue, skb);
+               tasklet_schedule(&rtlusb->rx_work_tasklet);
+
                goto resubmit;
        }
 
@@ -609,9 +672,6 @@ static void _rtl_rx_completed(struct urb *_urb)
        }
 
 resubmit:
-       skb_reset_tail_pointer(skb);
-       skb_trim(skb, 0);
-
        usb_anchor_urb(_urb, &rtlusb->rx_submitted);
        err = usb_submit_urb(_urb, GFP_ATOMIC);
        if (unlikely(err)) {
@@ -621,13 +681,34 @@ resubmit:
        return;
 
 free:
-       dev_kfree_skb_irq(skb);
+       /* On some architectures, usb_free_coherent must not be called from
+        * hardirq context. Queue urb to cleanup list.
+        */
+       usb_anchor_urb(_urb, &rtlusb->rx_cleanup_urbs);
+}
+
+#undef __RADIO_TAP_SIZE_RSV
+
+static void _rtl_usb_cleanup_rx(struct ieee80211_hw *hw)
+{
+       struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));
+       struct urb *urb;
+
+       usb_kill_anchored_urbs(&rtlusb->rx_submitted);
+
+       tasklet_kill(&rtlusb->rx_work_tasklet);
+       skb_queue_purge(&rtlusb->rx_queue);
+
+       while ((urb = usb_get_from_anchor(&rtlusb->rx_cleanup_urbs))) {
+               usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+                               urb->transfer_buffer, urb->transfer_dma);
+               usb_free_urb(urb);
+       }
 }
 
 static int _rtl_usb_receive(struct ieee80211_hw *hw)
 {
        struct urb *urb;
-       struct sk_buff *skb;
        int err;
        int i;
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -646,11 +727,10 @@ static int _rtl_usb_receive(struct ieee80211_hw *hw)
                        goto err_out;
                }
 
-               skb = _rtl_prep_rx_urb(hw, rtlusb, urb, GFP_KERNEL);
-               if (IS_ERR(skb)) {
+               err = _rtl_prep_rx_urb(hw, rtlusb, urb, GFP_KERNEL);
+               if (err < 0) {
                        RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG,
                                 "Failed to prep_rx_urb!!\n");
-                       err = PTR_ERR(skb);
                        usb_free_urb(urb);
                        goto err_out;
                }
@@ -665,6 +745,7 @@ static int _rtl_usb_receive(struct ieee80211_hw *hw)
 
 err_out:
        usb_kill_anchored_urbs(&rtlusb->rx_submitted);
+       _rtl_usb_cleanup_rx(hw);
        return err;
 }
 
@@ -706,7 +787,7 @@ static void rtl_usb_cleanup(struct ieee80211_hw *hw)
        SET_USB_STOP(rtlusb);
 
        /* clean up rx stuff. */
-       usb_kill_anchored_urbs(&rtlusb->rx_submitted);
+       _rtl_usb_cleanup_rx(hw);
 
        /* clean up tx stuff */
        for (i = 0; i < RTL_USB_MAX_EP_NUM; i++) {
index fb986f98d1dfabf0eae94bb539f26f0a20b91b8f..685273ca95612e7ff9c9fb393b48b9c65d1744aa 100644 (file)
@@ -136,11 +136,14 @@ struct rtl_usb {
        void (*usb_tx_cleanup)(struct ieee80211_hw *, struct sk_buff *);
 
        /* Rx */
-       u8 in_ep_nums ;
+       u8 in_ep_nums;
        u32 in_ep;              /* Bulk IN endpoint number */
        u32 rx_max_size;        /* Bulk IN max buffer size */
        u32 rx_urb_num;         /* How many Bulk INs are submitted to host. */
        struct usb_anchor       rx_submitted;
+       struct usb_anchor       rx_cleanup_urbs;
+       struct tasklet_struct   rx_work_tasklet;
+       struct sk_buff_head     rx_queue;
        void (*usb_rx_segregate_hdl)(struct ieee80211_hw *, struct sk_buff *,
                                     struct sk_buff_head *);
        void (*usb_rx_hdl)(struct ieee80211_hw *, struct sk_buff *);
index f13258a8d9956be1be84eae9eecbcec3f103fb94..44328baa63898750ac957a6275de09ed7290f0f7 100644 (file)
 #define        CHANNEL_GROUP_MAX_5G            9
 #define CHANNEL_MAX_NUMBER_2G          14
 #define AVG_THERMAL_NUM                        8
+#define AVG_THERMAL_NUM_88E            4
 #define MAX_TID_COUNT                  9
 
 /* for early mode */
 #define FCS_LEN                                4
 #define EM_HDR_LEN                     8
+
+#define MAX_TX_COUNT                   4
+#define        MAX_RF_PATH                     4
+#define        MAX_CHNL_GROUP_24G              6
+#define        MAX_CHNL_GROUP_5G               14
+
+struct txpower_info_2g {
+       u8 index_cck_base[MAX_RF_PATH][MAX_CHNL_GROUP_24G];
+       u8 index_bw40_base[MAX_RF_PATH][MAX_CHNL_GROUP_24G];
+       /*If only one tx, only BW20 and OFDM are used.*/
+       u8 cck_diff[MAX_RF_PATH][MAX_TX_COUNT];
+       u8 ofdm_diff[MAX_RF_PATH][MAX_TX_COUNT];
+       u8 bw20_diff[MAX_RF_PATH][MAX_TX_COUNT];
+       u8 bw40_diff[MAX_RF_PATH][MAX_TX_COUNT];
+};
+
+struct txpower_info_5g {
+       u8 index_bw40_base[MAX_RF_PATH][MAX_CHNL_GROUP_5G];
+       /*If only one tx, only BW20, OFDM, BW80 and BW160 are used.*/
+       u8 ofdm_diff[MAX_RF_PATH][MAX_TX_COUNT];
+       u8 bw20_diff[MAX_RF_PATH][MAX_TX_COUNT];
+       u8 bw40_diff[MAX_RF_PATH][MAX_TX_COUNT];
+};
+
 enum intf_type {
        INTF_PCI = 0,
        INTF_USB = 1,
@@ -137,6 +162,7 @@ enum hardware_type {
        HARDWARE_TYPE_RTL8192DU,
        HARDWARE_TYPE_RTL8723AE,
        HARDWARE_TYPE_RTL8723U,
+       HARDWARE_TYPE_RTL8188EE,
 
        /* keep it last */
        HARDWARE_TYPE_NUM
@@ -263,7 +289,7 @@ enum hw_variables {
        HW_VAR_RATR_0,
        HW_VAR_RRSR,
        HW_VAR_CPU_RST,
-       HW_VAR_CECHK_BSSID,
+       HW_VAR_CHECK_BSSID,
        HW_VAR_LBK_MODE,
        HW_VAR_AES_11N_FIX,
        HW_VAR_USB_RX_AGGR,
@@ -278,7 +304,10 @@ enum hw_variables {
        HW_VAR_SET_RPWM,
        HW_VAR_H2C_FW_PWRMODE,
        HW_VAR_H2C_FW_JOINBSSRPT,
+       HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
        HW_VAR_FW_PSMODE_STATUS,
+       HW_VAR_RESUME_CLK_ON,
+       HW_VAR_FW_LPS_ACTION,
        HW_VAR_1X1_RECV_COMBINE,
        HW_VAR_STOP_SEND_BEACON,
        HW_VAR_TSF_TIMER,
@@ -305,6 +334,7 @@ enum hw_variables {
        HW_VAR_INT_AC,
        HW_VAR_RF_TIMING,
 
+       HAL_DEF_WOWLAN,
        HW_VAR_MRC,
 
        HW_VAR_MGT_FILTER,
@@ -461,6 +491,7 @@ enum rtl_var_map {
        EFUSE_MAX_SECTION_MAP,
        EFUSE_REAL_CONTENT_SIZE,
        EFUSE_OOB_PROTECT_BYTES_LEN,
+       EFUSE_ACCESS,
 
        /*CAM map */
        RWCAM,
@@ -493,7 +524,7 @@ enum rtl_var_map {
        RTL_IMR_TIMEOUT1,       /*Timeout interrupt 1 */
        RTL_IMR_TXFOVW,         /*Transmit FIFO Overflow */
        RTL_IMR_PSTIMEOUT,      /*Power save time out interrupt */
-       RTL_IMR_BcnInt,         /*Beacon DMA Interrupt 0 */
+       RTL_IMR_BCNINT,         /*Beacon DMA Interrupt 0 */
        RTL_IMR_RXFOVW,         /*Receive FIFO Overflow */
        RTL_IMR_RDU,            /*Receive Descriptor Unavailable */
        RTL_IMR_ATIMEND,        /*For 92C,ATIM Window End Interrupt */
@@ -508,7 +539,7 @@ enum rtl_var_map {
        RTL_IMR_VIDOK,          /*AC_VI DMA OK Interrupt */
        RTL_IMR_VODOK,          /*AC_VO DMA Interrupt */
        RTL_IMR_ROK,            /*Receive DMA OK Interrupt */
-       RTL_IBSS_INT_MASKS,     /*(RTL_IMR_BcnInt | RTL_IMR_TBDOK |
+       RTL_IBSS_INT_MASKS,     /*(RTL_IMR_BCNINT | RTL_IMR_TBDOK |
                                 * RTL_IMR_TBDER) */
        RTL_IMR_C2HCMD,         /*fw interrupt*/
 
@@ -742,6 +773,11 @@ struct false_alarm_statistics {
        u32 cnt_ofdm_fail;
        u32 cnt_cck_fail;
        u32 cnt_all;
+       u32 cnt_ofdm_cca;
+       u32 cnt_cck_cca;
+       u32 cnt_cca_all;
+       u32 cnt_bw_usc;
+       u32 cnt_bw_lsc;
 };
 
 struct init_gain {
@@ -826,8 +862,67 @@ struct rtl_rfkill {
        bool rfkill_state;      /*0 is off, 1 is on */
 };
 
+/*for P2P PS**/
+#define        P2P_MAX_NOA_NUM         2
+
+enum p2p_role {
+       P2P_ROLE_DISABLE = 0,
+       P2P_ROLE_DEVICE = 1,
+       P2P_ROLE_CLIENT = 2,
+       P2P_ROLE_GO = 3
+};
+
+enum p2p_ps_state {
+       P2P_PS_DISABLE = 0,
+       P2P_PS_ENABLE = 1,
+       P2P_PS_SCAN = 2,
+       P2P_PS_SCAN_DONE = 3,
+       P2P_PS_ALLSTASLEEP = 4, /* for P2P GO */
+};
+
+enum p2p_ps_mode {
+       P2P_PS_NONE = 0,
+       P2P_PS_CTWINDOW = 1,
+       P2P_PS_NOA       = 2,
+       P2P_PS_MIX = 3, /* CTWindow and NoA */
+};
+
+struct rtl_p2p_ps_info {
+       enum p2p_ps_mode p2p_ps_mode; /* indicate p2p ps mode */
+       enum p2p_ps_state p2p_ps_state; /*  indicate p2p ps state */
+       u8 noa_index; /*  Identifies instance of Notice of Absence timing. */
+       /*  Client traffic window. A period of time in TU after TBTT. */
+       u8 ctwindow;
+       u8 opp_ps; /*  opportunistic power save. */
+       u8 noa_num; /*  number of NoA descriptor in P2P IE. */
+       /*  Count for owner, Type of client. */
+       u8 noa_count_type[P2P_MAX_NOA_NUM];
+       /*  Max duration for owner, preferred or min acceptable duration
+        * for client.
+        */
+       u32 noa_duration[P2P_MAX_NOA_NUM];
+       /*  Length of interval for owner, preferred or max acceptable intervali
+        * of client.
+        */
+       u32 noa_interval[P2P_MAX_NOA_NUM];
+       /*  schedule in terms of the lower 4 bytes of the TSF timer. */
+       u32 noa_start_time[P2P_MAX_NOA_NUM];
+};
+
+struct p2p_ps_offload_t {
+       u8 offload_en:1;
+       u8 role:1; /* 1: Owner, 0: Client */
+       u8 ctwindow_en:1;
+       u8 noa0_en:1;
+       u8 noa1_en:1;
+       u8 allstasleep:1;
+       u8 discovery:1;
+       u8 reserved:1;
+};
+
 #define IQK_MATRIX_REG_NUM     8
 #define IQK_MATRIX_SETTINGS_NUM        (1 + 24 + 21)
+
 struct iqk_matrix_regs {
        bool iqk_done;
        long value[1][IQK_MATRIX_REG_NUM];
@@ -889,7 +984,7 @@ struct rtl_phy {
 
        /* Dual mac */
        bool need_iqk;
-       struct iqk_matrix_regs iqk_matrix_regsetting[IQK_MATRIX_SETTINGS_NUM];
+       struct iqk_matrix_regs iqk_matrix[IQK_MATRIX_SETTINGS_NUM];
 
        bool rfpi_enable;
 
@@ -902,6 +997,8 @@ struct rtl_phy {
        /* the current Tx power level */
        u8 cur_cck_txpwridx;
        u8 cur_ofdm24g_txpwridx;
+       u8 cur_bw20_txpwridx;
+       u8 cur_bw40_txpwridx;
 
        u32 rfreg_chnlval[2];
        bool apk_done;
@@ -940,20 +1037,21 @@ struct rtl_ht_agg {
        u8 rx_agg_state;
 };
 
+struct rssi_sta {
+       long undec_sm_pwdb;
+};
+
 struct rtl_tid_data {
        u16 seq_number;
        struct rtl_ht_agg agg;
 };
 
-struct rssi_sta {
-       long undec_sm_pwdb;
-};
-
 struct rtl_sta_info {
        struct list_head list;
        u8 ratr_index;
        u8 wireless_mode;
        u8 mimo_ps;
+       u8 mac_addr[ETH_ALEN];
        struct rtl_tid_data tids[MAX_TID_COUNT];
 
        /* just used for ap adhoc or mesh*/
@@ -1005,6 +1103,8 @@ struct rtl_mac {
        int n_bitrates;
 
        bool offchan_delay;
+       u8 p2p; /*using p2p role*/
+       bool p2p_in_use;
 
        /*filters */
        u32 rx_conf;
@@ -1014,11 +1114,11 @@ struct rtl_mac {
 
        bool act_scanning;
        u8 cnt_after_linked;
+       bool skip_scan;
 
        /* early mode */
        /* skb wait queue */
        struct sk_buff_head skb_waitq[MAX_TID_COUNT];
-       u8 earlymode_threshold;
 
        /*RDG*/
        bool rdg_en;
@@ -1042,6 +1142,7 @@ struct rtl_mac {
        u8 retry_short;
        u8 retry_long;
        u16 assoc_id;
+       bool hiddenssid;
 
        /*IBSS*/
        int beacon_interval;
@@ -1111,10 +1212,13 @@ struct bt_coexist_8723 {
 
 struct rtl_hal {
        struct ieee80211_hw *hw;
-       struct bt_coexist_8723 hal_coex_8723;
+       bool driver_is_goingto_unload;
        bool up_first_time;
+       bool first_init;
        bool being_init_adapter;
        bool bbrf_ready;
+       bool mac_func_enable;
+       struct bt_coexist_8723 hal_coex_8723;
 
        enum intf_type interface;
        u16 hw_type;            /*92c or 92d or 92s and so on */
@@ -1122,6 +1226,7 @@ struct rtl_hal {
        u8 oem_id;
        u32 version;            /*version of chip */
        u8 state;               /*stop 0, start 1 */
+       u8 board_type;
 
        /*firmware */
        u32 fwsize;
@@ -1141,6 +1246,10 @@ struct rtl_hal {
        bool set_fwcmd_inprogress;
        u8 current_fwcmd_io;
 
+       struct p2p_ps_offload_t p2p_ps_offload;
+       bool fw_clk_change_in_progress;
+       bool allow_sw_to_change_hwclc;
+       u8 fw_ps_state;
        /**/
        bool driver_going2unload;
 
@@ -1157,6 +1266,7 @@ struct rtl_hal {
        /* just for DualMac S3S4 */
        u8 macphyctl_reg;
        bool earlymode_enable;
+       u8 max_earlymode_num;
        /* Dual mac*/
        bool during_mac0init_radiob;
        bool during_mac1init_radioa;
@@ -1193,6 +1303,29 @@ struct rtl_security {
        u8 *pairwise_key;
 };
 
+#define ASSOCIATE_ENTRY_NUM    33
+
+struct fast_ant_training {
+       u8      bssid[6];
+       u8      antsel_rx_keep_0;
+       u8      antsel_rx_keep_1;
+       u8      antsel_rx_keep_2;
+       u32     ant_sum[7];
+       u32     ant_cnt[7];
+       u32     ant_ave[7];
+       u8      fat_state;
+       u32     train_idx;
+       u8      antsel_a[ASSOCIATE_ENTRY_NUM];
+       u8      antsel_b[ASSOCIATE_ENTRY_NUM];
+       u8      antsel_c[ASSOCIATE_ENTRY_NUM];
+       u32     main_ant_sum[ASSOCIATE_ENTRY_NUM];
+       u32     aux_ant_sum[ASSOCIATE_ENTRY_NUM];
+       u32     main_ant_cnt[ASSOCIATE_ENTRY_NUM];
+       u32     aux_ant_cnt[ASSOCIATE_ENTRY_NUM];
+       u8      rx_idle_ant;
+       bool    becomelinked;
+};
+
 struct rtl_dm {
        /*PHY status for Dynamic Management */
        long entry_min_undec_sm_pwdb;
@@ -1229,9 +1362,24 @@ struct rtl_dm {
        bool disable_tx_int;
        char ofdm_index[2];
        char cck_index;
+       char delta_power_index;
+       char delta_power_index_last;
+       char power_index_offset;
+
+       /*88e tx power tracking*/
+       u8      swing_idx_ofdm[2];
+       u8      swing_idx_ofdm_cur;
+       u8      swing_idx_ofdm_base;
+       bool    swing_flag_ofdm;
+       u8      swing_idx_cck;
+       u8      swing_idx_cck_cur;
+       u8      swing_idx_cck_base;
+       bool    swing_flag_cck;
 
        /* DMSP */
        bool supp_phymode_switch;
+
+       struct fast_ant_training fat_table;
 };
 
 #define        EFUSE_MAX_LOGICAL_SIZE                  256
@@ -1264,6 +1412,9 @@ struct rtl_efuse {
        u8 external_pa;
 
        u8 dev_addr[6];
+       u8 wowlan_enable;
+       u8 antenna_div_cfg;
+       u8 antenna_div_type;
 
        bool txpwr_fromeprom;
        u8 eeprom_crystalcap;
@@ -1319,14 +1470,12 @@ struct rtl_ps_ctl {
        bool rfchange_inprogress;
        bool swrf_processing;
        bool hwradiooff;
-
        /*
         * just for PCIE ASPM
         * If it supports ASPM, Offset[560h] = 0x40,
         * otherwise Offset[560h] = 0x00.
         * */
        bool support_aspm;
-
        bool support_backdoor;
 
        /*for LPS */
@@ -1341,6 +1490,7 @@ struct rtl_ps_ctl {
        bool fw_current_inpsmode;
        u8 reg_max_lps_awakeintvl;
        bool report_linked;
+       bool low_power_enable;/*for 32k*/
 
        /*for IPS */
        bool inactiveps;
@@ -1373,6 +1523,11 @@ struct rtl_ps_ctl {
        unsigned long last_beacon;
        unsigned long last_action;
        unsigned long last_slept;
+
+       /*For P2P PS */
+       struct rtl_p2p_ps_info p2p_ps_info;
+       u8 pwr_mode;
+       u8 smart_ps;
 };
 
 struct rtl_stats {
@@ -1381,7 +1536,7 @@ struct rtl_stats {
        s8 rssi;
        u8 signal;
        u8 noise;
-       u16 rate;               /*in 100 kbps */
+       u8 rate;                /* hw desc rate */
        u8 received_channel;
        u8 control;
        u8 mask;
@@ -1423,8 +1578,16 @@ struct rtl_stats {
        bool packet_toself;
        bool packet_beacon;     /*for rssi */
        char cck_adc_pwdb[4];   /*for rx path selection */
+
+       u8 packet_report_type;
+
+       u32 macid;
+       u8 wake_match;
+       u32 bt_rx_rssi_percentage;
+       u32 macid_valid_entry[2];
 };
 
+
 struct rt_link_detect {
        /* count for roaming */
        u32 bcn_rx_inperiod;
@@ -1477,7 +1640,8 @@ struct rtl_tcb_desc {
        /* early mode */
        u8 empkt_num;
        /* The max value by HW */
-       u32 empkt_len[5];
+       u32 empkt_len[10];
+       bool btx_enable_sw_calc_duration;
 };
 
 struct rtl_hal_ops {
@@ -1553,7 +1717,7 @@ struct rtl_hal_ops {
        void (*allow_all_destaddr)(struct ieee80211_hw *hw,
                bool allow_all_da, bool write_into_reg);
        void (*linked_set_reg) (struct ieee80211_hw *hw);
-       void (*check_switch_to_dmdp) (struct ieee80211_hw *hw);
+       void (*chk_switch_dmdp) (struct ieee80211_hw *hw);
        void (*dualmac_easy_concurrent) (struct ieee80211_hw *hw);
        void (*dualmac_switch_to_dmdp) (struct ieee80211_hw *hw);
        bool (*phy_rf6052_config) (struct ieee80211_hw *hw);
@@ -1662,6 +1826,8 @@ struct rtl_locks {
        /*spin lock */
        spinlock_t ips_lock;
        spinlock_t irq_th_lock;
+       spinlock_t irq_pci_lock;
+       spinlock_t tx_lock;
        spinlock_t h2c_lock;
        spinlock_t rf_ps_lock;
        spinlock_t rf_lock;
@@ -1670,6 +1836,9 @@ struct rtl_locks {
        spinlock_t entry_list_lock;
        spinlock_t usb_lock;
 
+       /*FW clock change */
+       spinlock_t fw_ps_lock;
+
        /*Dual mac*/
        spinlock_t cck_and_rw_pagea_lock;
 
@@ -1683,7 +1852,8 @@ struct rtl_works {
        /*timer */
        struct timer_list watchdog_timer;
        struct timer_list dualmac_easyconcurrent_retrytimer;
-
+       struct timer_list fw_clockoff_timer;
+       struct timer_list fast_antenna_training_timer;
        /*task */
        struct tasklet_struct irq_tasklet;
        struct tasklet_struct irq_prepare_bcn_tasklet;
@@ -1696,8 +1866,9 @@ struct rtl_works {
        /* For SW LPS */
        struct delayed_work ps_work;
        struct delayed_work ps_rfon_wq;
+       struct delayed_work fwevt_wq;
 
-       struct work_struct lps_leave_work;
+       struct work_struct lps_change_work;
 };
 
 struct rtl_debug {
@@ -1767,10 +1938,12 @@ struct dig_t {
        char back_val;
        char back_range_max;
        char back_range_min;
-       u8 rx_gain_range_max;
-       u8 rx_gain_range_min;
+       u8 rx_gain_max;
+       u8 rx_gain_min;
        u8 min_undec_pwdb_for_dm;
        u8 rssi_val_min;
+       u8 pre_cck_cca_thres;
+       u8 cur_cck_cca_thres;
        u8 pre_cck_pd_state;
        u8 cur_cck_pd_state;
        u8 pre_cck_fa_state;
@@ -1792,6 +1965,13 @@ struct dig_t {
        u8 backoff_enable_flag;
        char backoffval_range_max;
        char backoffval_range_min;
+       u8 dig_min_0;
+       u8 dig_min_1;
+       bool media_connect_0;
+       bool media_connect_1;
+
+       u32 antdiv_rssi_max;
+       u32 rssi_max;
 };
 
 struct rtl_global_var {
@@ -1802,6 +1982,7 @@ struct rtl_global_var {
 };
 
 struct rtl_priv {
+       struct ieee80211_hw *hw;
        struct completion firmware_loading_complete;
        struct list_head list;
        struct rtl_priv *buddy_priv;
@@ -1866,6 +2047,7 @@ struct rtl_priv {
                        bool bt_operation_on;
                };
        };
+       bool enter_ps;  /* true when entering PS */
 
        /*This must be the last item so
           that it points to the data allocated
@@ -2127,9 +2309,7 @@ value to host byte ordering.*/
 #define WLAN_FC_GET_TYPE(fc)   (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE)
 #define WLAN_FC_GET_STYPE(fc)  (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE)
 #define WLAN_FC_MORE_DATA(fc)  (le16_to_cpu(fc) & IEEE80211_FCTL_MOREDATA)
-#define SEQ_TO_SN(seq)         (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn)         (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN                 ((IEEE80211_SCTL_SEQ) >> 4)
+#define rtl_dm(rtlpriv)                (&((rtlpriv)->dm))
 
 #define        RT_RF_OFF_LEVL_ASPM             BIT(0)  /*PCI ASPM */
 #define        RT_RF_OFF_LEVL_CLK_REQ          BIT(1)  /*PCI clock request */
index e57ee48edff6d3d788626b618633caab096e0427..e2b3d9c541e830f23fdc3703a6ddc5d48c276de0 100644 (file)
@@ -186,8 +186,10 @@ static int wl1251_sdio_set_power(struct wl1251 *wl, bool enable)
                        wl->set_power(true);
 
                ret = pm_runtime_get_sync(&func->dev);
-               if (ret < 0)
+               if (ret < 0) {
+                       pm_runtime_put_sync(&func->dev);
                        goto out;
+               }
 
                sdio_claim_host(func);
                sdio_enable_func(func);
index 3b266d3231a36a538a6c17673c3d9ae18696795f..4c67c2f9ea716a1c8d9430bcd51284867a20a4c8 100644 (file)
@@ -257,7 +257,7 @@ static int wl1251_spi_probe(struct spi_device *spi)
        wl = hw->priv;
 
        SET_IEEE80211_DEV(hw, &spi->dev);
-       dev_set_drvdata(&spi->dev, wl);
+       spi_set_drvdata(spi, wl);
        wl->if_priv = spi;
        wl->if_ops = &wl1251_spi_ops;
 
@@ -311,7 +311,7 @@ static int wl1251_spi_probe(struct spi_device *spi)
 
 static int wl1251_spi_remove(struct spi_device *spi)
 {
-       struct wl1251 *wl = dev_get_drvdata(&spi->dev);
+       struct wl1251 *wl = spi_get_drvdata(spi);
 
        free_irq(wl->irq, wl);
        wl1251_free_hw(wl);
index 09694e39bb147dff54622c16983595029a79e4a0..1c627da85083949c9dd9837e09b53b5ca8a9ac74 100644 (file)
@@ -723,6 +723,7 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
        wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
        wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
        wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;
+       wl->ba_rx_session_count_max = WL12XX_RX_BA_MAX_SESSIONS;
 out:
        return ret;
 }
index d4552857480ca92d6c72461067ec54c813c421d8..222d03540200498a9400c9dc867159780bdf9311 100644 (file)
@@ -63,6 +63,8 @@
 
 #define WL12XX_NUM_MAC_ADDRESSES 2
 
+#define WL12XX_RX_BA_MAX_SESSIONS 3
+
 struct wl127x_rx_mem_pool_addr {
        u32 addr;
        u32 addr_extra;
index da3ef1b10a9cd55dbb3a64f423dfdc4918bb7a74..9fa692d110250bde779c627e66af87e10ba4f5f2 100644 (file)
@@ -678,6 +678,7 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
        wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
        wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC;
        wl->max_channels_5 = WL18XX_MAX_CHANNELS_5GHZ;
+       wl->ba_rx_session_count_max = WL18XX_RX_BA_MAX_SESSIONS;
 out:
        return ret;
 }
@@ -1144,6 +1145,7 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
 static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
 {
        u32 fuse;
+       s8 rom = 0, metal = 0, pg_ver = 0, rdl_ver = 0;
        int ret;
 
        ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
@@ -1154,8 +1156,29 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
        if (ret < 0)
                goto out;
 
+       pg_ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET;
+       rom = (fuse & WL18XX_ROM_VER_MASK) >> WL18XX_ROM_VER_OFFSET;
+
+       if (rom <= 0xE)
+               metal = (fuse & WL18XX_METAL_VER_MASK) >>
+                       WL18XX_METAL_VER_OFFSET;
+       else
+               metal = (fuse & WL18XX_NEW_METAL_VER_MASK) >>
+                       WL18XX_NEW_METAL_VER_OFFSET;
+
+       ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_2_3, &fuse);
+       if (ret < 0)
+               goto out;
+
+       rdl_ver = (fuse & WL18XX_RDL_VER_MASK) >> WL18XX_RDL_VER_OFFSET;
+       if (rdl_ver > RDL_MAX)
+               rdl_ver = RDL_NONE;
+
+       wl1271_info("wl18xx HW: RDL %d, %s, PG %x.%x (ROM %x)",
+                   rdl_ver, rdl_names[rdl_ver], pg_ver, metal, rom);
+
        if (ver)
-               *ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET;
+               *ver = pg_ver;
 
        ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
 
index 937b71d8783ff84a19ae52a04df4d25c1d98b6f9..6306e04cd2580676e6683ae62a64cecc3578c719 100644 (file)
 #define WL18XX_REG_FUSE_DATA_1_3       0xA0260C
 #define WL18XX_PG_VER_MASK             0x70
 #define WL18XX_PG_VER_OFFSET           4
+#define WL18XX_ROM_VER_MASK            0x3
+#define WL18XX_ROM_VER_OFFSET          0
+#define WL18XX_METAL_VER_MASK          0xC
+#define WL18XX_METAL_VER_OFFSET                2
+#define WL18XX_NEW_METAL_VER_MASK      0x180
+#define WL18XX_NEW_METAL_VER_OFFSET    7
+
+#define WL18XX_REG_FUSE_DATA_2_3       0xA02614
+#define WL18XX_RDL_VER_MASK            0x1f00
+#define WL18XX_RDL_VER_OFFSET          8
 
 #define WL18XX_REG_FUSE_BD_ADDR_1      0xA02602
 #define WL18XX_REG_FUSE_BD_ADDR_2      0xA02606
@@ -188,4 +198,23 @@ enum {
        NUM_BOARD_TYPES,
 };
 
+enum {
+       RDL_NONE        = 0,
+       RDL_1_HP        = 1,
+       RDL_2_SP        = 2,
+       RDL_3_HP        = 3,
+       RDL_4_SP        = 4,
+
+       _RDL_LAST,
+       RDL_MAX = _RDL_LAST - 1,
+};
+
+static const char * const rdl_names[] = {
+       [RDL_NONE]      = "",
+       [RDL_1_HP]      = "1853 SISO",
+       [RDL_2_SP]      = "1857 MIMO",
+       [RDL_3_HP]      = "1893 SISO",
+       [RDL_4_SP]      = "1897 MIMO",
+};
+
 #endif /* __REG_H__ */
index b6739e79efcfa33bc45c18ad9f205bcdbc2dce5a..9204e07ee432fe483a343f008ee1ce91f2752149 100644 (file)
@@ -29,7 +29,7 @@
 #define WL18XX_IFTYPE_VER      5
 #define WL18XX_MAJOR_VER       WLCORE_FW_VER_IGNORE
 #define WL18XX_SUBTYPE_VER     WLCORE_FW_VER_IGNORE
-#define WL18XX_MINOR_VER       28
+#define WL18XX_MINOR_VER       39
 
 #define WL18XX_CMD_MAX_SIZE          740
 
@@ -40,6 +40,8 @@
 
 #define WL18XX_NUM_MAC_ADDRESSES 3
 
+#define WL18XX_RX_BA_MAX_SESSIONS 5
+
 struct wl18xx_priv {
        /* buffer for sending commands to FW */
        u8 cmd_buf[WL18XX_CMD_MAX_SIZE];
index c79654323396cb6fd73b5dff82b44f72e1148d08..7a970cd9c5551cc538ec0177758f023589c9e281 100644 (file)
@@ -1736,6 +1736,35 @@ out:
 
 }
 
+int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           s8 *avg_rssi)
+{
+       struct acx_roaming_stats *acx;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_ACX, "acx roaming statistics");
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->role_id = wlvif->role_id;
+       ret = wl1271_cmd_interrogate(wl, ACX_ROAMING_STATISTICS_TBL,
+                                    acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("acx roaming statistics failed: %d", ret);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       *avg_rssi = acx->rssi_beacon;
+out:
+       kfree(acx);
+       return ret;
+}
+
 #ifdef CONFIG_PM
 /* Set the global behaviour of RX filters - On/Off + default action */
 int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable,
index 126536c6a3933929f683940de3cc8670988caaea..6dcfad9b04729a44d60044d41bb1ac0a9ac042cb 100644 (file)
@@ -728,8 +728,6 @@ struct wl1271_acx_ht_information {
        u8 padding[2];
 } __packed;
 
-#define RX_BA_MAX_SESSIONS 3
-
 struct wl1271_acx_ba_initiator_policy {
        struct acx_header header;
 
@@ -955,6 +953,18 @@ struct acx_rx_filter_cfg {
        u8 fields[0];
 } __packed;
 
+struct acx_roaming_stats {
+       struct acx_header header;
+
+       u8      role_id;
+       u8      pad[3];
+       u32     missed_beacons;
+       u8      snr_data;
+       u8      snr_bacon;
+       s8      rssi_data;
+       s8      rssi_beacon;
+} __packed;
+
 enum {
        ACX_WAKE_UP_CONDITIONS           = 0x0000,
        ACX_MEM_CFG                      = 0x0001,
@@ -1112,6 +1122,8 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
 int wl1271_acx_fm_coex(struct wl1271 *wl);
 int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
 int wl12xx_acx_config_hangover(struct wl1271 *wl);
+int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           s8 *avg_rssi);
 
 #ifdef CONFIG_PM
 int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable,
index 6331f9e1cb3975c9d8f87de0fe1f02d94a607784..c9e060795d13760befc72a9190b008b65b6f1b41 100644 (file)
@@ -327,6 +327,14 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        wl->links[link].prev_freed_pkts =
                        wl->fw_status_2->counters.tx_lnk_free_pkts[link];
        wl->links[link].wlvif = wlvif;
+
+       /*
+        * Take saved value for total freed packets from wlvif, in case this is
+        * recovery/resume
+        */
+       if (wlvif->bss_type != BSS_TYPE_AP_BSS)
+               wl->links[link].total_freed_pkts = wlvif->total_freed_pkts;
+
        *hlid = link;
 
        wl->active_link_count++;
@@ -358,6 +366,26 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        wl1271_tx_reset_link_queues(wl, *hlid);
        wl->links[*hlid].wlvif = NULL;
 
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+           (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+            *hlid == wlvif->ap.bcast_hlid)) {
+               /*
+                * save the total freed packets in the wlvif, in case this is
+                * recovery or suspend
+                */
+               wlvif->total_freed_pkts = wl->links[*hlid].total_freed_pkts;
+
+               /*
+                * increment the initial seq number on recovery to account for
+                * transmitted packets that we haven't yet got in the FW status
+                */
+               if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+                       wlvif->total_freed_pkts +=
+                                       WL1271_TX_SQN_POST_RECOVERY_PADDING;
+       }
+
+       wl->links[*hlid].total_freed_pkts = 0;
+
        *hlid = WL12XX_INVALID_LINK_ID;
        wl->active_link_count--;
        WARN_ON_ONCE(wl->active_link_count < 0);
@@ -609,6 +637,10 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if (ret < 0)
                goto out_free_global;
 
+       /* use the previous security seq, if this is a recovery/resume */
+       wl->links[wlvif->ap.bcast_hlid].total_freed_pkts =
+                                               wlvif->total_freed_pkts;
+
        cmd->role_id = wlvif->role_id;
        cmd->ap.aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period);
        cmd->ap.bss_index = WL1271_AP_BSS_INDEX;
index db4bf5a68ce208c1fa3e98fbe848221051ce0ec5..0420bd45e4ee4f9e16c2de51f4240995cbf45254 100644 (file)
@@ -89,25 +89,24 @@ extern u32 wl12xx_debug_level;
        } while (0)
 #endif
 
-/* TODO: use pr_debug_hex_dump when it becomes available */
-#define wl1271_dump(level, prefix, buf, len)   \
-       do { \
-               if (level & wl12xx_debug_level) \
-                       print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
-                                      DUMP_PREFIX_OFFSET, 16, 1,       \
-                                      buf,                             \
-                                      min_t(size_t, len, DEBUG_DUMP_LIMIT), \
-                                      0);                              \
+#define wl1271_dump(level, prefix, buf, len)                                 \
+       do {                                                                  \
+               if (level & wl12xx_debug_level)                               \
+                       print_hex_dump_debug(DRIVER_PREFIX prefix,            \
+                                       DUMP_PREFIX_OFFSET, 16, 1,            \
+                                       buf,                                  \
+                                       min_t(size_t, len, DEBUG_DUMP_LIMIT), \
+                                       0);                                   \
        } while (0)
 
-#define wl1271_dump_ascii(level, prefix, buf, len)     \
-       do { \
-               if (level & wl12xx_debug_level) \
-                       print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
-                                      DUMP_PREFIX_OFFSET, 16, 1,       \
-                                      buf,                             \
-                                      min_t(size_t, len, DEBUG_DUMP_LIMIT), \
-                                      true);                           \
+#define wl1271_dump_ascii(level, prefix, buf, len)                           \
+       do {                                                                  \
+               if (level & wl12xx_debug_level)                               \
+                       print_hex_dump_debug(DRIVER_PREFIX prefix,            \
+                                       DUMP_PREFIX_OFFSET, 16, 1,            \
+                                       buf,                                  \
+                                       min_t(size_t, len, DEBUG_DUMP_LIMIT), \
+                                       true);                                \
        } while (0)
 
 #endif /* __DEBUG_H__ */
index e70a7c864865e0decdaff4e307285235e4622b30..c3e1f79c785697d95dfc9ccfac1e49410f263d8f 100644 (file)
@@ -598,8 +598,7 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                VIF_STATE_PRINT_INT(last_rssi_event);
                VIF_STATE_PRINT_INT(ba_support);
                VIF_STATE_PRINT_INT(ba_allowed);
-               VIF_STATE_PRINT_LLHEX(tx_security_seq);
-               VIF_STATE_PRINT_INT(tx_security_last_seq_lsb);
+               VIF_STATE_PRINT_LLHEX(total_freed_pkts);
        }
 
 #undef VIF_STATE_PRINT_INT
index 70f289aa1bc6dd8f7991fae5eacf958e35b6d4f2..67f61689b49edcba1e54198db538eb277aa4bda0 100644 (file)
@@ -237,6 +237,14 @@ void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
                    !test_bit(wlvif->role_id , &roles_bitmap))
                        continue;
 
+               vif = wl12xx_wlvif_to_vif(wlvif);
+
+               /* don't attempt roaming in case of p2p */
+               if (wlvif->p2p) {
+                       ieee80211_connection_loss(vif);
+                       continue;
+               }
+
                /*
                 * if the work is already queued, it should take place.
                 * We don't want to delay the connection loss
@@ -246,7 +254,6 @@ void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
                                             &wlvif->connection_loss_work,
                                             msecs_to_jiffies(delay));
 
-               vif = wl12xx_wlvif_to_vif(wlvif);
                ieee80211_cqm_rssi_notify(
                                vif,
                                NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
index 2c2ff3e1f849047de636a7ff6da4149b26380755..d10954c0c181a9d793c735728a28d88c771e077d 100644 (file)
@@ -108,8 +108,7 @@ static void wl1271_reg_notify(struct wiphy *wiphy,
 
        }
 
-       if (likely(wl->state == WLCORE_STATE_ON))
-               wlcore_regdomain_config(wl);
+       wlcore_regdomain_config(wl);
 }
 
 static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
@@ -332,10 +331,9 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
                                        struct wl12xx_vif *wlvif,
                                        u8 hlid, u8 tx_pkts)
 {
-       bool fw_ps, single_link;
+       bool fw_ps;
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
-       single_link = (wl->active_link_count == 1);
 
        /*
         * Wake up from high level PS if the STA is asleep with too little
@@ -348,8 +346,13 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
         * Start high-level PS if the STA is asleep with enough blocks in FW.
         * Make an exception if this is the only connected link. In this
         * case FW-memory congestion is less of a problem.
+        * Note that a single connected STA means 3 active links, since we must
+        * account for the global and broadcast AP links. The "fw_ps" check
+        * assures us the third link is a STA connected to the AP. Otherwise
+        * the FW would not set the PSM bit.
         */
-       else if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+       else if (wl->active_link_count > 3 && fw_ps &&
+                tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
@@ -414,13 +417,21 @@ static int wlcore_fw_status(struct wl1271 *wl,
 
 
        for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+               u8 diff;
                lnk = &wl->links[i];
+
                /* prevent wrap-around in freed-packets counter */
-               lnk->allocated_pkts -=
-                       (status_2->counters.tx_lnk_free_pkts[i] -
-                        lnk->prev_freed_pkts) & 0xff;
+               diff = (status_2->counters.tx_lnk_free_pkts[i] -
+                      lnk->prev_freed_pkts) & 0xff;
+
+               if (diff == 0)
+                       continue;
 
+               lnk->allocated_pkts -= diff;
                lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+
+               /* accumulate the prev_freed_pkts counter */
+               lnk->total_freed_pkts += diff;
        }
 
        /* prevent wrap-around in total blocks counter */
@@ -640,6 +651,25 @@ static irqreturn_t wlcore_irq(int irq, void *cookie)
        unsigned long flags;
        struct wl1271 *wl = cookie;
 
+       /* complete the ELP completion */
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
+       if (wl->elp_compl) {
+               complete(wl->elp_compl);
+               wl->elp_compl = NULL;
+       }
+
+       if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
+               /* don't enqueue a work right now. mark it as pending */
+               set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
+               wl1271_debug(DEBUG_IRQ, "should not enqueue work");
+               disable_irq_nosync(wl->irq);
+               pm_wakeup_event(wl->dev, 0);
+               spin_unlock_irqrestore(&wl->wl_lock, flags);
+               return IRQ_HANDLED;
+       }
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+
        /* TX might be handled here, avoid redundant work */
        set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
        cancel_work_sync(&wl->tx_work);
@@ -919,18 +949,6 @@ static void wl1271_recovery_work(struct work_struct *work)
                goto out_unlock;
        }
 
-       /*
-        * Advance security sequence number to overcome potential progress
-        * in the firmware during recovery. This doens't hurt if the network is
-        * not encrypted.
-        */
-       wl12xx_for_each_wlvif(wl, wlvif) {
-               if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
-                   test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
-                       wlvif->tx_security_seq +=
-                               WL1271_TX_SQN_POST_RECOVERY_PADDING;
-       }
-
        /* Prevent spurious TX during FW restart */
        wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
 
@@ -2523,6 +2541,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
                wl1271_ps_elp_sleep(wl);
        }
 deinit:
+       wl12xx_tx_reset_wlvif(wl, wlvif);
+
        /* clear all hlids (except system_hlid) */
        wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
 
@@ -2546,7 +2566,6 @@ deinit:
 
        dev_kfree_skb(wlvif->probereq);
        wlvif->probereq = NULL;
-       wl12xx_tx_reset_wlvif(wl, wlvif);
        if (wl->last_wlvif == wlvif)
                wl->last_wlvif = NULL;
        list_del(&wlvif->list);
@@ -2860,10 +2879,6 @@ static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                                     wlvif->sta.klv_template_id,
                                     ACX_KEEP_ALIVE_TPL_INVALID);
 
-       /* reset TX security counters on a clean disconnect */
-       wlvif->tx_security_last_seq_lsb = 0;
-       wlvif->tx_security_seq = 0;
-
        return 0;
 }
 
@@ -3262,6 +3277,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
        u32 tx_seq_32 = 0;
        u16 tx_seq_16 = 0;
        u8 key_type;
+       u8 hlid;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
 
@@ -3271,6 +3287,22 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                     key_conf->keylen, key_conf->flags);
        wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
 
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               if (sta) {
+                       struct wl1271_station *wl_sta = (void *)sta->drv_priv;
+                       hlid = wl_sta->hlid;
+               } else {
+                       hlid = wlvif->ap.bcast_hlid;
+               }
+       else
+               hlid = wlvif->sta.hlid;
+
+       if (hlid != WL12XX_INVALID_LINK_ID) {
+               u64 tx_seq = wl->links[hlid].total_freed_pkts;
+               tx_seq_32 = WL1271_TX_SECURITY_HI32(tx_seq);
+               tx_seq_16 = WL1271_TX_SECURITY_LO16(tx_seq);
+       }
+
        switch (key_conf->cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104:
@@ -3280,22 +3312,14 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                break;
        case WLAN_CIPHER_SUITE_TKIP:
                key_type = KEY_TKIP;
-
                key_conf->hw_key_idx = key_conf->keyidx;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
                key_type = KEY_AES;
-
                key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        case WL1271_CIPHER_SUITE_GEM:
                key_type = KEY_GEM;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        default:
                wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
@@ -3358,6 +3382,10 @@ void wlcore_regdomain_config(struct wl1271 *wl)
                return;
 
        mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
@@ -4499,6 +4527,9 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
                return -EBUSY;
        }
 
+       /* use the previous security seq, if this is a recovery/resume */
+       wl->links[wl_sta->hlid].total_freed_pkts = wl_sta->total_freed_pkts;
+
        set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
        memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
        wl->active_sta_count++;
@@ -4507,12 +4538,37 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
 
 void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
 {
+       struct wl1271_station *wl_sta;
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
        if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
                return;
 
        clear_bit(hlid, wlvif->ap.sta_hlid_map);
        __clear_bit(hlid, &wl->ap_ps_map);
        __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+
+       /*
+        * save the last used PN in the private part of iee80211_sta,
+        * in case of recovery/suspend
+        */
+       rcu_read_lock();
+       sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
+       if (sta) {
+               wl_sta = (void *)sta->drv_priv;
+               wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
+
+               /*
+                * increment the initial seq number on recovery to account for
+                * transmitted packets that we haven't yet got in the FW status
+                */
+               if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+                       wl_sta->total_freed_pkts +=
+                                       WL1271_TX_SQN_POST_RECOVERY_PADDING;
+       }
+       rcu_read_unlock();
+
        wl12xx_free_link(wl, wlvif, &hlid);
        wl->active_sta_count--;
 
@@ -4616,13 +4672,11 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
                                   enum ieee80211_sta_state new_state)
 {
        struct wl1271_station *wl_sta;
-       u8 hlid;
        bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
        bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
        int ret;
 
        wl_sta = (struct wl1271_station *)sta->drv_priv;
-       hlid = wl_sta->hlid;
 
        /* Add station (AP mode) */
        if (is_ap &&
@@ -4648,12 +4702,12 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
        /* Authorize station (AP mode) */
        if (is_ap &&
            new_state == IEEE80211_STA_AUTHORIZED) {
-               ret = wl12xx_cmd_set_peer_state(wl, wlvif, hlid);
+               ret = wl12xx_cmd_set_peer_state(wl, wlvif, wl_sta->hlid);
                if (ret < 0)
                        return ret;
 
                ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
-                                                    hlid);
+                                                    wl_sta->hlid);
                if (ret)
                        return ret;
 
@@ -4784,7 +4838,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
                        break;
                }
 
-               if (wl->ba_rx_session_count >= RX_BA_MAX_SESSIONS) {
+               if (wl->ba_rx_session_count >= wl->ba_rx_session_count_max) {
                        ret = -EBUSY;
                        wl1271_error("exceeded max RX BA sessions");
                        break;
@@ -4946,7 +5000,7 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
-static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
+static void wlcore_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct wl1271 *wl = hw->priv;
 
@@ -4956,7 +5010,8 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
 static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       struct ieee80211_channel *chan,
-                                      int duration)
+                                      int duration,
+                                      enum ieee80211_roc_type type)
 {
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        struct wl1271 *wl = hw->priv;
@@ -5091,6 +5146,39 @@ static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
        wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
 }
 
+static int wlcore_op_get_rssi(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_sta *sta,
+                              s8 *rssi_dbm)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int ret = 0;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_sleep;
+
+       ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm);
+       if (ret < 0)
+               goto out_sleep;
+
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
 static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
 {
        struct wl1271 *wl = hw->priv;
@@ -5290,6 +5378,7 @@ static const struct ieee80211_ops wl1271_ops = {
        .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
        .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
        .sta_rc_update = wlcore_op_sta_rc_update,
+       .get_rssi = wlcore_op_get_rssi,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 
@@ -5929,35 +6018,6 @@ int wlcore_free_hw(struct wl1271 *wl)
 }
 EXPORT_SYMBOL_GPL(wlcore_free_hw);
 
-static irqreturn_t wl12xx_hardirq(int irq, void *cookie)
-{
-       struct wl1271 *wl = cookie;
-       unsigned long flags;
-
-       wl1271_debug(DEBUG_IRQ, "IRQ");
-
-       /* complete the ELP completion */
-       spin_lock_irqsave(&wl->wl_lock, flags);
-       set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
-       if (wl->elp_compl) {
-               complete(wl->elp_compl);
-               wl->elp_compl = NULL;
-       }
-
-       if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
-               /* don't enqueue a work right now. mark it as pending */
-               set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
-               wl1271_debug(DEBUG_IRQ, "should not enqueue work");
-               disable_irq_nosync(wl->irq);
-               pm_wakeup_event(wl->dev, 0);
-               spin_unlock_irqrestore(&wl->wl_lock, flags);
-               return IRQ_HANDLED;
-       }
-       spin_unlock_irqrestore(&wl->wl_lock, flags);
-
-       return IRQ_WAKE_THREAD;
-}
-
 static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
        struct wl1271 *wl = context;
@@ -5999,9 +6059,8 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        else
                irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
 
-       ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wlcore_irq,
-                                  irqflags,
-                                  pdev->name, wl);
+       ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
+                                  irqflags, pdev->name, wl);
        if (ret < 0) {
                wl1271_error("request_irq() failed: %d", ret);
                goto out_free_nvs;
index 9b7b6e2e4fbcef50adaab008cfeb1867bf4a266d..9654577efd012e0faaa7e785fe3a4f15cc1ae5ba 100644 (file)
@@ -29,6 +29,7 @@
 #define WL1271_WAKEUP_TIMEOUT 500
 
 #define ELP_ENTRY_DELAY  30
+#define ELP_ENTRY_DELAY_FORCE_PS  5
 
 void wl1271_elp_work(struct work_struct *work)
 {
@@ -98,7 +99,8 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
                        return;
        }
 
-       timeout = ELP_ENTRY_DELAY;
+       timeout = wl->conf.conn.forced_ps ?
+                       ELP_ENTRY_DELAY_FORCE_PS : ELP_ENTRY_DELAY;
        ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
                                     msecs_to_jiffies(timeout));
 }
index ece392c54d9c858bd255ab78eb9e79d054337d52..004d02e71f01a25eb3abf9496fa752f79af3df7d 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/etherdevice.h>
+#include <linux/spinlock.h>
 
 #include "wlcore.h"
 #include "debug.h"
@@ -104,7 +105,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
                                    struct wl12xx_vif *wlvif,
                                    u8 hlid)
 {
-       bool fw_ps, single_link;
+       bool fw_ps;
        u8 tx_pkts;
 
        if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
@@ -112,15 +113,19 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
        tx_pkts = wl->links[hlid].allocated_pkts;
-       single_link = (wl->active_link_count == 1);
 
        /*
         * if in FW PS and there is enough data in FW we can put the link
         * into high-level PS and clean out its TX queues.
         * Make an exception if this is the only connected link. In this
         * case FW-memory congestion is less of a problem.
+        * Note that a single connected STA means 3 active links, since we must
+        * account for the global and broadcast AP links. The "fw_ps" check
+        * assures us the third link is a STA connected to the AP. Otherwise
+        * the FW would not set the PSM bit.
         */
-       if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+       if (wl->active_link_count > 3 && fw_ps &&
+           tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
@@ -639,6 +644,7 @@ next:
 
        }
 
+out:
        if (!skb &&
            test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
                int q;
@@ -652,7 +658,6 @@ next:
                spin_unlock_irqrestore(&wl->wl_lock, flags);
        }
 
-out:
        return skb;
 }
 
@@ -928,25 +933,6 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
 
        wl->stats.retry_count += result->ack_failures;
 
-       /*
-        * update sequence number only when relevant, i.e. only in
-        * sessions of TKIP, AES and GEM (not in open or WEP sessions)
-        */
-       if (info->control.hw_key &&
-           (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP ||
-            info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP ||
-            info->control.hw_key->cipher == WL1271_CIPHER_SUITE_GEM)) {
-               u8 fw_lsb = result->tx_security_sequence_number_lsb;
-               u8 cur_lsb = wlvif->tx_security_last_seq_lsb;
-
-               /*
-                * update security sequence number, taking care of potential
-                * wrap-around
-                */
-               wlvif->tx_security_seq += (fw_lsb - cur_lsb) & 0xff;
-               wlvif->tx_security_last_seq_lsb = fw_lsb;
-       }
-
        /* remove private header from packet */
        skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
 
@@ -1061,7 +1047,8 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 
        /* TX failure */
        for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
-               if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
+               if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+                   i != wlvif->ap.bcast_hlid && i != wlvif->ap.global_hlid) {
                        /* this calls wl12xx_free_link */
                        wl1271_free_sta(wl, wlvif, i);
                } else {
@@ -1304,7 +1291,7 @@ bool wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
 {
        int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
 
-       WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock));
+       assert_spin_locked(&wl->wl_lock);
        return test_bit(reason, &wl->queue_stop_reasons[hwq]);
 }
 
@@ -1313,6 +1300,6 @@ bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 {
        int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
 
-       WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock));
+       assert_spin_locked(&wl->wl_lock);
        return !!wl->queue_stop_reasons[hwq];
 }
index af9fecaefc30801cd96a35942ca2a4a48b900f49..0034979e97cbaa0316dfd2a62db2e5be59b7f058 100644 (file)
@@ -390,6 +390,9 @@ struct wl1271 {
        /* number of currently active RX BA sessions */
        int ba_rx_session_count;
 
+       /* Maximum number of supported RX BA sessions */
+       int ba_rx_session_count_max;
+
        /* AP-mode - number of currently connected stations */
        int active_sta_count;
 
index 508f5b0f8a709d7fd2e416aaf20c18cfc5faa98e..e5e146435fe77e34bc04ac7bca36a1e8e553a17a 100644 (file)
@@ -274,6 +274,13 @@ struct wl1271_link {
 
        /* The wlvif this link belongs to. Might be null for global links */
        struct wl12xx_vif *wlvif;
+
+       /*
+        * total freed FW packets on the link - used for tracking the
+        * AES/TKIP PN across recoveries. Re-initialized each time
+        * from the wl1271_station structure.
+        */
+       u64 total_freed_pkts;
 };
 
 #define WL1271_MAX_RX_FILTERS 5
@@ -318,6 +325,13 @@ struct wl12xx_rx_filter {
 struct wl1271_station {
        u8 hlid;
        bool in_connection;
+
+       /*
+        * total freed FW packets on the link to the STA - used for tracking the
+        * AES/TKIP PN across recoveries. Re-initialized each time from the
+        * wl1271_station structure.
+        */
+       u64 total_freed_pkts;
 };
 
 struct wl12xx_vif {
@@ -449,16 +463,15 @@ struct wl12xx_vif {
         */
        struct {
                u8 persistent[0];
+
                /*
-                * Security sequence number
-                *     bits 0-15: lower 16 bits part of sequence number
-                *     bits 16-47: higher 32 bits part of sequence number
-                *     bits 48-63: not in use
+                * total freed FW packets on the link - used for
+                * storing the AES/TKIP PN during recovery, as this
+                * structure is not zeroed out.
+                * For STA this holds the PN of the link to the AP.
+                * For AP this holds the PN of the broadcast link.
                 */
-               u64 tx_security_seq;
-
-               /* 8 bits of the last sequence number in use */
-               u8 tx_security_last_seq_lsb;
+               u64 total_freed_pkts;
        };
 };
 
index cd49ba9496361cccfceeaa4dbf8251cf3657e763..a2865f17c6673837ea982b0ac2a983c455a62cad 100644 (file)
 #include <asm/xen/hypercall.h>
 #include <asm/xen/page.h>
 
+/*
+ * This is the maximum slots a skb can have. If a guest sends a skb
+ * which exceeds this limit it is considered malicious.
+ */
+#define MAX_SKB_SLOTS_DEFAULT 20
+static unsigned int max_skb_slots = MAX_SKB_SLOTS_DEFAULT;
+module_param(max_skb_slots, uint, 0444);
+
+typedef unsigned int pending_ring_idx_t;
+#define INVALID_PENDING_RING_IDX (~0U)
+
 struct pending_tx_info {
-       struct xen_netif_tx_request req;
+       struct xen_netif_tx_request req; /* coalesced tx request */
        struct xenvif *vif;
+       pending_ring_idx_t head; /* head != INVALID_PENDING_RING_IDX
+                                 * if it is head of one or more tx
+                                 * reqs
+                                 */
 };
-typedef unsigned int pending_ring_idx_t;
 
 struct netbk_rx_meta {
        int id;
@@ -102,7 +116,11 @@ struct xen_netbk {
        atomic_t netfront_count;
 
        struct pending_tx_info pending_tx_info[MAX_PENDING_REQS];
-       struct gnttab_copy tx_copy_ops[MAX_PENDING_REQS];
+       /* Coalescing tx requests before copying makes number of grant
+        * copy ops greater or equal to number of slots required. In
+        * worst case a tx request consumes 2 gnttab_copy.
+        */
+       struct gnttab_copy tx_copy_ops[2*MAX_PENDING_REQS];
 
        u16 pending_ring[MAX_PENDING_REQS];
 
@@ -118,6 +136,16 @@ struct xen_netbk {
 static struct xen_netbk *xen_netbk;
 static int xen_netbk_group_nr;
 
+/*
+ * If head != INVALID_PENDING_RING_IDX, it means this tx request is head of
+ * one or more merged tx requests, otherwise it is the continuation of
+ * previous tx request.
+ */
+static inline int pending_tx_is_head(struct xen_netbk *netbk, RING_IDX idx)
+{
+       return netbk->pending_tx_info[idx].head != INVALID_PENDING_RING_IDX;
+}
+
 void xen_netbk_add_xenvif(struct xenvif *vif)
 {
        int i;
@@ -250,6 +278,7 @@ static int max_required_rx_slots(struct xenvif *vif)
 {
        int max = DIV_ROUND_UP(vif->dev->mtu, PAGE_SIZE);
 
+       /* XXX FIXME: RX path dependent on MAX_SKB_FRAGS */
        if (vif->can_sg || vif->gso || vif->gso_prefix)
                max += MAX_SKB_FRAGS + 1; /* extra_info + frags */
 
@@ -657,6 +686,7 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk)
                __skb_queue_tail(&rxq, skb);
 
                /* Filled the batch queue? */
+               /* XXX FIXME: RX path dependent on MAX_SKB_FRAGS */
                if (count + MAX_SKB_FRAGS >= XEN_NETIF_RX_RING_SIZE)
                        break;
        }
@@ -898,51 +928,91 @@ static void netbk_fatal_tx_err(struct xenvif *vif)
 
 static int netbk_count_requests(struct xenvif *vif,
                                struct xen_netif_tx_request *first,
+                               RING_IDX first_idx,
                                struct xen_netif_tx_request *txp,
                                int work_to_do)
 {
        RING_IDX cons = vif->tx.req_cons;
-       int frags = 0;
+       int slots = 0;
+       int drop_err = 0;
 
        if (!(first->flags & XEN_NETTXF_more_data))
                return 0;
 
        do {
-               if (frags >= work_to_do) {
-                       netdev_err(vif->dev, "Need more frags\n");
+               if (slots >= work_to_do) {
+                       netdev_err(vif->dev,
+                                  "Asked for %d slots but exceeds this limit\n",
+                                  work_to_do);
                        netbk_fatal_tx_err(vif);
                        return -ENODATA;
                }
 
-               if (unlikely(frags >= MAX_SKB_FRAGS)) {
-                       netdev_err(vif->dev, "Too many frags\n");
+               /* This guest is really using too many slots and
+                * considered malicious.
+                */
+               if (unlikely(slots >= max_skb_slots)) {
+                       netdev_err(vif->dev,
+                                  "Malicious frontend using %d slots, threshold %u\n",
+                                  slots, max_skb_slots);
                        netbk_fatal_tx_err(vif);
                        return -E2BIG;
                }
 
-               memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + frags),
+               /* Xen network protocol had implicit dependency on
+                * MAX_SKB_FRAGS. XEN_NETIF_NR_SLOTS_MIN is set to the
+                * historical MAX_SKB_FRAGS value 18 to honor the same
+                * behavior as before. Any packet using more than 18
+                * slots but less than max_skb_slots slots is dropped
+                */
+               if (!drop_err && slots >= XEN_NETIF_NR_SLOTS_MIN) {
+                       if (net_ratelimit())
+                               netdev_dbg(vif->dev,
+                                          "Too many slots (%d) exceeding limit (%d), dropping packet\n",
+                                          slots, XEN_NETIF_NR_SLOTS_MIN);
+                       drop_err = -E2BIG;
+               }
+
+               memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + slots),
                       sizeof(*txp));
-               if (txp->size > first->size) {
-                       netdev_err(vif->dev, "Frag is bigger than frame.\n");
-                       netbk_fatal_tx_err(vif);
-                       return -EIO;
+
+               /* If the guest submitted a frame >= 64 KiB then
+                * first->size overflowed and following slots will
+                * appear to be larger than the frame.
+                *
+                * This cannot be fatal error as there are buggy
+                * frontends that do this.
+                *
+                * Consume all slots and drop the packet.
+                */
+               if (!drop_err && txp->size > first->size) {
+                       if (net_ratelimit())
+                               netdev_dbg(vif->dev,
+                                          "Invalid tx request, slot size %u > remaining size %u\n",
+                                          txp->size, first->size);
+                       drop_err = -EIO;
                }
 
                first->size -= txp->size;
-               frags++;
+               slots++;
 
                if (unlikely((txp->offset + txp->size) > PAGE_SIZE)) {
-                       netdev_err(vif->dev, "txp->offset: %x, size: %u\n",
+                       netdev_err(vif->dev, "Cross page boundary, txp->offset: %x, size: %u\n",
                                 txp->offset, txp->size);
                        netbk_fatal_tx_err(vif);
                        return -EINVAL;
                }
        } while ((txp++)->flags & XEN_NETTXF_more_data);
-       return frags;
+
+       if (drop_err) {
+               netbk_tx_err(vif, first, first_idx + slots);
+               return drop_err;
+       }
+
+       return slots;
 }
 
 static struct page *xen_netbk_alloc_page(struct xen_netbk *netbk,
-                                        struct sk_buff *skb,
                                         u16 pending_idx)
 {
        struct page *page;
@@ -963,48 +1033,114 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
        struct skb_shared_info *shinfo = skb_shinfo(skb);
        skb_frag_t *frags = shinfo->frags;
        u16 pending_idx = *((u16 *)skb->data);
-       int i, start;
+       u16 head_idx = 0;
+       int slot, start;
+       struct page *page;
+       pending_ring_idx_t index, start_idx = 0;
+       uint16_t dst_offset;
+       unsigned int nr_slots;
+       struct pending_tx_info *first = NULL;
+
+       /* At this point shinfo->nr_frags is in fact the number of
+        * slots, which can be as large as XEN_NETIF_NR_SLOTS_MIN.
+        */
+       nr_slots = shinfo->nr_frags;
 
        /* Skip first skb fragment if it is on same page as header fragment. */
        start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx);
 
-       for (i = start; i < shinfo->nr_frags; i++, txp++) {
-               struct page *page;
-               pending_ring_idx_t index;
+       /* Coalesce tx requests, at this point the packet passed in
+        * should be <= 64K. Any packets larger than 64K have been
+        * handled in netbk_count_requests().
+        */
+       for (shinfo->nr_frags = slot = start; slot < nr_slots;
+            shinfo->nr_frags++) {
                struct pending_tx_info *pending_tx_info =
                        netbk->pending_tx_info;
 
-               index = pending_index(netbk->pending_cons++);
-               pending_idx = netbk->pending_ring[index];
-               page = xen_netbk_alloc_page(netbk, skb, pending_idx);
+               page = alloc_page(GFP_KERNEL|__GFP_COLD);
                if (!page)
                        goto err;
 
-               gop->source.u.ref = txp->gref;
-               gop->source.domid = vif->domid;
-               gop->source.offset = txp->offset;
-
-               gop->dest.u.gmfn = virt_to_mfn(page_address(page));
-               gop->dest.domid = DOMID_SELF;
-               gop->dest.offset = txp->offset;
-
-               gop->len = txp->size;
-               gop->flags = GNTCOPY_source_gref;
+               dst_offset = 0;
+               first = NULL;
+               while (dst_offset < PAGE_SIZE && slot < nr_slots) {
+                       gop->flags = GNTCOPY_source_gref;
+
+                       gop->source.u.ref = txp->gref;
+                       gop->source.domid = vif->domid;
+                       gop->source.offset = txp->offset;
+
+                       gop->dest.domid = DOMID_SELF;
+
+                       gop->dest.offset = dst_offset;
+                       gop->dest.u.gmfn = virt_to_mfn(page_address(page));
+
+                       if (dst_offset + txp->size > PAGE_SIZE) {
+                               /* This page can only merge a portion
+                                * of tx request. Do not increment any
+                                * pointer / counter here. The txp
+                                * will be dealt with in future
+                                * rounds, eventually hitting the
+                                * `else` branch.
+                                */
+                               gop->len = PAGE_SIZE - dst_offset;
+                               txp->offset += gop->len;
+                               txp->size -= gop->len;
+                               dst_offset += gop->len; /* quit loop */
+                       } else {
+                               /* This tx request can be merged in the page */
+                               gop->len = txp->size;
+                               dst_offset += gop->len;
+
+                               index = pending_index(netbk->pending_cons++);
+
+                               pending_idx = netbk->pending_ring[index];
+
+                               memcpy(&pending_tx_info[pending_idx].req, txp,
+                                      sizeof(*txp));
+                               xenvif_get(vif);
+
+                               pending_tx_info[pending_idx].vif = vif;
+
+                               /* Poison these fields, corresponding
+                                * fields for head tx req will be set
+                                * to correct values after the loop.
+                                */
+                               netbk->mmap_pages[pending_idx] = (void *)(~0UL);
+                               pending_tx_info[pending_idx].head =
+                                       INVALID_PENDING_RING_IDX;
+
+                               if (!first) {
+                                       first = &pending_tx_info[pending_idx];
+                                       start_idx = index;
+                                       head_idx = pending_idx;
+                               }
+
+                               txp++;
+                               slot++;
+                       }
 
-               gop++;
+                       gop++;
+               }
 
-               memcpy(&pending_tx_info[pending_idx].req, txp, sizeof(*txp));
-               xenvif_get(vif);
-               pending_tx_info[pending_idx].vif = vif;
-               frag_set_pending_idx(&frags[i], pending_idx);
+               first->req.offset = 0;
+               first->req.size = dst_offset;
+               first->head = start_idx;
+               set_page_ext(page, netbk, head_idx);
+               netbk->mmap_pages[head_idx] = page;
+               frag_set_pending_idx(&frags[shinfo->nr_frags], head_idx);
        }
 
+       BUG_ON(shinfo->nr_frags > MAX_SKB_FRAGS);
+
        return gop;
 err:
        /* Unwind, freeing all pages and sending error responses. */
-       while (i-- > start) {
-               xen_netbk_idx_release(netbk, frag_get_pending_idx(&frags[i]),
-                                     XEN_NETIF_RSP_ERROR);
+       while (shinfo->nr_frags-- > start) {
+               xen_netbk_idx_release(netbk,
+                               frag_get_pending_idx(&frags[shinfo->nr_frags]),
+                               XEN_NETIF_RSP_ERROR);
        }
        /* The head too, if necessary. */
        if (start)
@@ -1020,8 +1156,10 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
        struct gnttab_copy *gop = *gopp;
        u16 pending_idx = *((u16 *)skb->data);
        struct skb_shared_info *shinfo = skb_shinfo(skb);
+       struct pending_tx_info *tx_info;
        int nr_frags = shinfo->nr_frags;
        int i, err, start;
+       u16 peek; /* peek into next tx request */
 
        /* Check status of header. */
        err = gop->status;
@@ -1033,11 +1171,20 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
 
        for (i = start; i < nr_frags; i++) {
                int j, newerr;
+               pending_ring_idx_t head;
 
                pending_idx = frag_get_pending_idx(&shinfo->frags[i]);
+               tx_info = &netbk->pending_tx_info[pending_idx];
+               head = tx_info->head;
 
                /* Check error status: if okay then remember grant handle. */
-               newerr = (++gop)->status;
+               do {
+                       newerr = (++gop)->status;
+                       if (newerr)
+                               break;
+                       peek = netbk->pending_ring[pending_index(++head)];
+               } while (!pending_tx_is_head(netbk, peek));
+
                if (likely(!newerr)) {
                        /* Had a previous error? Invalidate this fragment. */
                        if (unlikely(err))
@@ -1157,7 +1304,6 @@ static int netbk_set_skb_gso(struct xenvif *vif,
 static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
 {
        struct iphdr *iph;
-       unsigned char *th;
        int err = -EPROTO;
        int recalculate_partial_csum = 0;
 
@@ -1181,27 +1327,26 @@ static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
                goto out;
 
        iph = (void *)skb->data;
-       th = skb->data + 4 * iph->ihl;
-       if (th >= skb_tail_pointer(skb))
-               goto out;
-
-       skb->csum_start = th - skb->head;
        switch (iph->protocol) {
        case IPPROTO_TCP:
-               skb->csum_offset = offsetof(struct tcphdr, check);
+               if (!skb_partial_csum_set(skb, 4 * iph->ihl,
+                                         offsetof(struct tcphdr, check)))
+                       goto out;
 
                if (recalculate_partial_csum) {
-                       struct tcphdr *tcph = (struct tcphdr *)th;
+                       struct tcphdr *tcph = tcp_hdr(skb);
                        tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
                                                         skb->len - iph->ihl*4,
                                                         IPPROTO_TCP, 0);
                }
                break;
        case IPPROTO_UDP:
-               skb->csum_offset = offsetof(struct udphdr, check);
+               if (!skb_partial_csum_set(skb, 4 * iph->ihl,
+                                         offsetof(struct udphdr, check)))
+                       goto out;
 
                if (recalculate_partial_csum) {
-                       struct udphdr *udph = (struct udphdr *)th;
+                       struct udphdr *udph = udp_hdr(skb);
                        udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
                                                         skb->len - iph->ihl*4,
                                                         IPPROTO_UDP, 0);
@@ -1215,9 +1360,6 @@ static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
                goto out;
        }
 
-       if ((th + skb->csum_offset + 2) > skb_tail_pointer(skb))
-               goto out;
-
        err = 0;
 
 out:
@@ -1262,11 +1404,12 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
        struct sk_buff *skb;
        int ret;
 
-       while (((nr_pending_reqs(netbk) + MAX_SKB_FRAGS) < MAX_PENDING_REQS) &&
+       while ((nr_pending_reqs(netbk) + XEN_NETIF_NR_SLOTS_MIN
+               < MAX_PENDING_REQS) &&
                !list_empty(&netbk->net_schedule_list)) {
                struct xenvif *vif;
                struct xen_netif_tx_request txreq;
-               struct xen_netif_tx_request txfrags[MAX_SKB_FRAGS];
+               struct xen_netif_tx_request txfrags[max_skb_slots];
                struct page *page;
                struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1];
                u16 pending_idx;
@@ -1327,7 +1470,8 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
                                continue;
                }
 
-               ret = netbk_count_requests(vif, &txreq, txfrags, work_to_do);
+               ret = netbk_count_requests(vif, &txreq, idx,
+                                          txfrags, work_to_do);
                if (unlikely(ret < 0))
                        continue;
 
@@ -1354,7 +1498,7 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
                pending_idx = netbk->pending_ring[index];
 
                data_len = (txreq.size > PKT_PROT_LEN &&
-                           ret < MAX_SKB_FRAGS) ?
+                           ret < XEN_NETIF_NR_SLOTS_MIN) ?
                        PKT_PROT_LEN : txreq.size;
 
                skb = alloc_skb(data_len + NET_SKB_PAD + NET_IP_ALIGN,
@@ -1381,7 +1525,7 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
                }
 
                /* XXX could copy straight to head */
-               page = xen_netbk_alloc_page(netbk, skb, pending_idx);
+               page = xen_netbk_alloc_page(netbk, pending_idx);
                if (!page) {
                        kfree_skb(skb);
                        netbk_tx_err(vif, &txreq, idx);
@@ -1404,6 +1548,7 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
                memcpy(&netbk->pending_tx_info[pending_idx].req,
                       &txreq, sizeof(txreq));
                netbk->pending_tx_info[pending_idx].vif = vif;
+               netbk->pending_tx_info[pending_idx].head = index;
                *((u16 *)skb->data) = pending_idx;
 
                __skb_put(skb, data_len);
@@ -1496,6 +1641,7 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk)
 
                skb->dev      = vif->dev;
                skb->protocol = eth_type_trans(skb, skb->dev);
+               skb_reset_network_header(skb);
 
                if (checksum_setup(vif, skb)) {
                        netdev_dbg(vif->dev,
@@ -1504,6 +1650,8 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk)
                        continue;
                }
 
+               skb_probe_transport_header(skb, 0);
+
                vif->dev->stats.rx_bytes += skb->len;
                vif->dev->stats.rx_packets++;
 
@@ -1531,7 +1679,10 @@ static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx,
 {
        struct xenvif *vif;
        struct pending_tx_info *pending_tx_info;
-       pending_ring_idx_t index;
+       pending_ring_idx_t head;
+       u16 peek; /* peek into next tx request */
+
+       BUG_ON(netbk->mmap_pages[pending_idx] == (void *)(~0UL));
 
        /* Already complete? */
        if (netbk->mmap_pages[pending_idx] == NULL)
@@ -1540,19 +1691,40 @@ static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx,
        pending_tx_info = &netbk->pending_tx_info[pending_idx];
 
        vif = pending_tx_info->vif;
+       head = pending_tx_info->head;
+
+       BUG_ON(!pending_tx_is_head(netbk, head));
+       BUG_ON(netbk->pending_ring[pending_index(head)] != pending_idx);
+
+       do {
+               pending_ring_idx_t index;
+               pending_ring_idx_t idx = pending_index(head);
+               u16 info_idx = netbk->pending_ring[idx];
 
-       make_tx_response(vif, &pending_tx_info->req, status);
+               pending_tx_info = &netbk->pending_tx_info[info_idx];
+               make_tx_response(vif, &pending_tx_info->req, status);
 
-       index = pending_index(netbk->pending_prod++);
-       netbk->pending_ring[index] = pending_idx;
+               /* Setting any number other than
+                * INVALID_PENDING_RING_IDX indicates this slot is
+                * starting a new packet / ending a previous packet.
+                */
+               pending_tx_info->head = 0;
 
-       xenvif_put(vif);
+               index = pending_index(netbk->pending_prod++);
+               netbk->pending_ring[index] = netbk->pending_ring[info_idx];
+
+               xenvif_put(vif);
+
+               peek = netbk->pending_ring[pending_index(++head)];
+
+       } while (!pending_tx_is_head(netbk, peek));
 
        netbk->mmap_pages[pending_idx]->mapping = 0;
        put_page(netbk->mmap_pages[pending_idx]);
        netbk->mmap_pages[pending_idx] = NULL;
 }
 
+
 static void make_tx_response(struct xenvif *vif,
                             struct xen_netif_tx_request *txp,
                             s8       st)
@@ -1605,8 +1777,9 @@ static inline int rx_work_todo(struct xen_netbk *netbk)
 static inline int tx_work_todo(struct xen_netbk *netbk)
 {
 
-       if (((nr_pending_reqs(netbk) + MAX_SKB_FRAGS) < MAX_PENDING_REQS) &&
-                       !list_empty(&netbk->net_schedule_list))
+       if ((nr_pending_reqs(netbk) + XEN_NETIF_NR_SLOTS_MIN
+            < MAX_PENDING_REQS) &&
+            !list_empty(&netbk->net_schedule_list))
                return 1;
 
        return 0;
@@ -1689,6 +1862,13 @@ static int __init netback_init(void)
        if (!xen_domain())
                return -ENODEV;
 
+       if (max_skb_slots < XEN_NETIF_NR_SLOTS_MIN) {
+               printk(KERN_INFO
+                      "xen-netback: max_skb_slots too small (%d), bump it to XEN_NETIF_NR_SLOTS_MIN (%d)\n",
+                      max_skb_slots, XEN_NETIF_NR_SLOTS_MIN);
+               max_skb_slots = XEN_NETIF_NR_SLOTS_MIN;
+       }
+
        xen_netbk_group_nr = num_online_cpus();
        xen_netbk = vzalloc(sizeof(struct xen_netbk) * xen_netbk_group_nr);
        if (!xen_netbk)
index 7ffa43bd7cf9e9f3b1fc43f83d0ec4c53b355765..1db101415069726fc59d26443f773fc0432f52a6 100644 (file)
@@ -36,7 +36,7 @@
 #include <linux/skbuff.h>
 #include <linux/ethtool.h>
 #include <linux/if_ether.h>
-#include <linux/tcp.h>
+#include <net/tcp.h>
 #include <linux/udp.h>
 #include <linux/moduleparam.h>
 #include <linux/mm.h>
@@ -537,7 +537,6 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct netfront_info *np = netdev_priv(dev);
        struct netfront_stats *stats = this_cpu_ptr(np->stats);
        struct xen_netif_tx_request *tx;
-       struct xen_netif_extra_info *extra;
        char *data = skb->data;
        RING_IDX i;
        grant_ref_t ref;
@@ -548,6 +547,16 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int len = skb_headlen(skb);
        unsigned long flags;
 
+       /* If skb->len is too big for wire format, drop skb and alert
+        * user about misconfiguration.
+        */
+       if (unlikely(skb->len > XEN_NETIF_MAX_TX_SIZE)) {
+               net_alert_ratelimited(
+                       "xennet: skb->len = %u, too big for wire format\n",
+                       skb->len);
+               goto drop;
+       }
+
        slots = DIV_ROUND_UP(offset + len, PAGE_SIZE) +
                xennet_count_skb_frag_slots(skb);
        if (unlikely(slots > MAX_SKB_FRAGS + 1)) {
@@ -581,7 +590,6 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        tx->gref = np->grant_tx_ref[id] = ref;
        tx->offset = offset;
        tx->size = len;
-       extra = NULL;
 
        tx->flags = 0;
        if (skb->ip_summed == CHECKSUM_PARTIAL)
@@ -597,10 +605,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                gso = (struct xen_netif_extra_info *)
                        RING_GET_REQUEST(&np->tx, ++i);
 
-               if (extra)
-                       extra->flags |= XEN_NETIF_EXTRA_FLAG_MORE;
-               else
-                       tx->flags |= XEN_NETTXF_extra_info;
+               tx->flags |= XEN_NETTXF_extra_info;
 
                gso->u.gso.size = skb_shinfo(skb)->gso_size;
                gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4;
@@ -609,7 +614,6 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
                gso->type = XEN_NETIF_EXTRA_TYPE_GSO;
                gso->flags = 0;
-               extra = gso;
        }
 
        np->tx.req_prod_pvt = i + 1;
@@ -718,7 +722,7 @@ static int xennet_get_responses(struct netfront_info *np,
        struct sk_buff *skb = xennet_get_rx_skb(np, cons);
        grant_ref_t ref = xennet_get_rx_ref(np, cons);
        int max = MAX_SKB_FRAGS + (rx->status <= RX_COPY_THRESHOLD);
-       int frags = 1;
+       int slots = 1;
        int err = 0;
        unsigned long ret;
 
@@ -741,7 +745,7 @@ static int xennet_get_responses(struct netfront_info *np,
                /*
                 * This definitely indicates a bug, either in this driver or in
                 * the backend driver. In future this should flag the bad
-                * situation to the system controller to reboot the backed.
+                * situation to the system controller to reboot the backend.
                 */
                if (ref == GRANT_INVALID_REF) {
                        if (net_ratelimit())
@@ -762,27 +766,27 @@ next:
                if (!(rx->flags & XEN_NETRXF_more_data))
                        break;
 
-               if (cons + frags == rp) {
+               if (cons + slots == rp) {
                        if (net_ratelimit())
-                               dev_warn(dev, "Need more frags\n");
+                               dev_warn(dev, "Need more slots\n");
                        err = -ENOENT;
                        break;
                }
 
-               rx = RING_GET_RESPONSE(&np->rx, cons + frags);
-               skb = xennet_get_rx_skb(np, cons + frags);
-               ref = xennet_get_rx_ref(np, cons + frags);
-               frags++;
+               rx = RING_GET_RESPONSE(&np->rx, cons + slots);
+               skb = xennet_get_rx_skb(np, cons + slots);
+               ref = xennet_get_rx_ref(np, cons + slots);
+               slots++;
        }
 
-       if (unlikely(frags > max)) {
+       if (unlikely(slots > max)) {
                if (net_ratelimit())
-                       dev_warn(dev, "Too many frags\n");
+                       dev_warn(dev, "Too many slots\n");
                err = -E2BIG;
        }
 
        if (unlikely(err))
-               np->rx.rsp_cons = cons + frags;
+               np->rx.rsp_cons = cons + slots;
 
        return err;
 }
@@ -1064,7 +1068,8 @@ err:
 
 static int xennet_change_mtu(struct net_device *dev, int mtu)
 {
-       int max = xennet_can_sg(dev) ? 65535 - ETH_HLEN : ETH_DATA_LEN;
+       int max = xennet_can_sg(dev) ?
+               XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER : ETH_DATA_LEN;
 
        if (mtu > max)
                return -EINVAL;
@@ -1368,6 +1373,8 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
        SET_ETHTOOL_OPS(netdev, &xennet_ethtool_ops);
        SET_NETDEV_DEV(netdev, &dev->dev);
 
+       netif_set_gso_max_size(netdev, XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER);
+
        np->netdev = netdev;
 
        netif_carrier_off(netdev);
index e3a8b22ef9dd04fcdefb80e34b4cfb1a92fb1382..23049aeca662c7a1944daa127a2fc77c3dcd4410 100644 (file)
@@ -34,7 +34,10 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 {
        struct phy_device *phy;
        struct device_node *child;
-       int rc, i;
+       const __be32 *paddr;
+       u32 addr;
+       bool is_c45, scanphys = false;
+       int rc, i, len;
 
        /* Mask out all PHYs from auto probing.  Instead the PHYs listed in
         * the device tree are populated after the bus has been registered */
@@ -54,14 +57,10 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 
        /* Loop over the child nodes and register a phy_device for each one */
        for_each_available_child_of_node(np, child) {
-               const __be32 *paddr;
-               u32 addr;
-               int len;
-               bool is_c45;
-
                /* A PHY must have a reg property in the range [0-31] */
                paddr = of_get_property(child, "reg", &len);
                if (!paddr || len < sizeof(*paddr)) {
+                       scanphys = true;
                        dev_err(&mdio->dev, "%s has invalid PHY address\n",
                                child->full_name);
                        continue;
@@ -111,6 +110,59 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
                        child->name, addr);
        }
 
+       if (!scanphys)
+               return 0;
+
+       /* auto scan for PHYs with empty reg property */
+       for_each_available_child_of_node(np, child) {
+               /* Skip PHYs with reg property set */
+               paddr = of_get_property(child, "reg", &len);
+               if (paddr)
+                       continue;
+
+               is_c45 = of_device_is_compatible(child,
+                                                "ethernet-phy-ieee802.3-c45");
+
+               for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
+                       /* skip already registered PHYs */
+                       if (mdio->phy_map[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);
+
+                       phy = get_phy_device(mdio, addr, is_c45);
+                       if (!phy || IS_ERR(phy))
+                               continue;
+
+                       if (mdio->irq) {
+                               mdio->irq[addr] =
+                                       irq_of_parse_and_map(child, 0);
+                               if (!mdio->irq[addr])
+                                       mdio->irq[addr] = PHY_POLL;
+                       }
+
+                       /* Associate the OF node with the device structure so it
+                        * can be looked up later */
+                       of_node_get(child);
+                       phy->dev.of_node = child;
+
+                       /* All data is now stored in the phy struct;
+                        * register it */
+                       rc = phy_device_register(phy);
+                       if (rc) {
+                               phy_device_free(phy);
+                               of_node_put(child);
+                               continue;
+                       }
+
+                       dev_info(&mdio->dev, "registered phy %s at address %i\n",
+                                child->name, addr);
+                       break;
+               }
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(of_mdiobus_register);
index 79f4bce061bd289d5f01a35601ad151f73ee8d80..4a8c388364ca3bd0df16113a1b5f355cc6ca22f3 100644 (file)
@@ -17,7 +17,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-#include <linux/bitops.h>
+#include <linux/idr.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -32,7 +32,6 @@
 #include "ptp_private.h"
 
 #define PTP_MAX_ALARMS 4
-#define PTP_MAX_CLOCKS 8
 #define PTP_PPS_DEFAULTS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT)
 #define PTP_PPS_EVENT PPS_CAPTUREASSERT
 #define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC)
@@ -42,8 +41,7 @@
 static dev_t ptp_devt;
 static struct class *ptp_class;
 
-static DECLARE_BITMAP(ptp_clocks_map, PTP_MAX_CLOCKS);
-static DEFINE_MUTEX(ptp_clocks_mutex); /* protects 'ptp_clocks_map' */
+static DEFINE_IDA(ptp_clocks_map);
 
 /* time stamp event queue operations */
 
@@ -171,12 +169,7 @@ static void delete_ptp_clock(struct posix_clock *pc)
        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
 
        mutex_destroy(&ptp->tsevq_mux);
-
-       /* Remove the clock from the bit map. */
-       mutex_lock(&ptp_clocks_mutex);
-       clear_bit(ptp->index, ptp_clocks_map);
-       mutex_unlock(&ptp_clocks_mutex);
-
+       ida_simple_remove(&ptp_clocks_map, ptp->index);
        kfree(ptp);
 }
 
@@ -191,21 +184,18 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
        if (info->n_alarm > PTP_MAX_ALARMS)
                return ERR_PTR(-EINVAL);
 
-       /* Find a free clock slot and reserve it. */
-       err = -EBUSY;
-       mutex_lock(&ptp_clocks_mutex);
-       index = find_first_zero_bit(ptp_clocks_map, PTP_MAX_CLOCKS);
-       if (index < PTP_MAX_CLOCKS)
-               set_bit(index, ptp_clocks_map);
-       else
-               goto no_slot;
-
        /* Initialize a clock structure. */
        err = -ENOMEM;
        ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
        if (ptp == NULL)
                goto no_memory;
 
+       index = ida_simple_get(&ptp_clocks_map, 0, MINORMASK + 1, GFP_KERNEL);
+       if (index < 0) {
+               err = index;
+               goto no_slot;
+       }
+
        ptp->clock.ops = ptp_clock_ops;
        ptp->clock.release = delete_ptp_clock;
        ptp->info = info;
@@ -248,7 +238,6 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
                goto no_clock;
        }
 
-       mutex_unlock(&ptp_clocks_mutex);
        return ptp;
 
 no_clock:
@@ -260,11 +249,9 @@ no_sysfs:
        device_destroy(ptp_class, ptp->devid);
 no_device:
        mutex_destroy(&ptp->tsevq_mux);
+no_slot:
        kfree(ptp);
 no_memory:
-       clear_bit(index, ptp_clocks_map);
-no_slot:
-       mutex_unlock(&ptp_clocks_mutex);
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL(ptp_clock_register);
@@ -323,7 +310,8 @@ EXPORT_SYMBOL(ptp_clock_index);
 static void __exit ptp_exit(void)
 {
        class_destroy(ptp_class);
-       unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS);
+       unregister_chrdev_region(ptp_devt, MINORMASK + 1);
+       ida_destroy(&ptp_clocks_map);
 }
 
 static int __init ptp_init(void)
@@ -336,7 +324,7 @@ static int __init ptp_init(void)
                return PTR_ERR(ptp_class);
        }
 
-       err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp");
+       err = alloc_chrdev_region(&ptp_devt, 0, MINORMASK + 1, "ptp");
        if (err < 0) {
                pr_err("ptp: failed to allocate device region\n");
                goto no_region;
index 1367655eee39784ec53995a55270f6fb349f89a7..bea94510ad2d4c0d3faf7cc703c9b8883ec9cf97 100644 (file)
@@ -118,7 +118,7 @@ struct pch_ts_regs {
  * struct pch_dev - Driver private data
  */
 struct pch_dev {
-       struct pch_ts_regs *regs;
+       struct pch_ts_regs __iomem *regs;
        struct ptp_clock *ptp_clock;
        struct ptp_clock_info caps;
        int exts0_enabled;
@@ -154,7 +154,7 @@ static inline void pch_eth_enable_set(struct pch_dev *chip)
        iowrite32(val, (&chip->regs->ts_sel));
 }
 
-static u64 pch_systime_read(struct pch_ts_regs *regs)
+static u64 pch_systime_read(struct pch_ts_regs __iomem *regs)
 {
        u64 ns;
        u32 lo, hi;
@@ -169,7 +169,7 @@ static u64 pch_systime_read(struct pch_ts_regs *regs)
        return ns;
 }
 
-static void pch_systime_write(struct pch_ts_regs *regs, u64 ns)
+static void pch_systime_write(struct pch_ts_regs __iomem *regs, u64 ns)
 {
        u32 hi, lo;
 
@@ -315,7 +315,7 @@ int pch_set_station_address(u8 *addr, struct pci_dev *pdev)
        struct pch_dev *chip = pci_get_drvdata(pdev);
 
        /* Verify the parameter */
-       if ((chip->regs == 0) || addr == (u8 *)NULL) {
+       if ((chip->regs == NULL) || addr == (u8 *)NULL) {
                dev_err(&pdev->dev,
                        "invalid params returning PCH_INVALIDPARAM\n");
                return PCH_INVALIDPARAM;
@@ -361,7 +361,7 @@ EXPORT_SYMBOL(pch_set_station_address);
 static irqreturn_t isr(int irq, void *priv)
 {
        struct pch_dev *pch_dev = priv;
-       struct pch_ts_regs *regs = pch_dev->regs;
+       struct pch_ts_regs __iomem *regs = pch_dev->regs;
        struct ptp_clock_event event;
        u32 ack = 0, lo, hi, val;
 
@@ -415,7 +415,7 @@ static int ptp_pch_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
        u32 diff, addend;
        int neg_adj = 0;
        struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps);
-       struct pch_ts_regs *regs = pch_dev->regs;
+       struct pch_ts_regs __iomem *regs = pch_dev->regs;
 
        if (ppb < 0) {
                neg_adj = 1;
@@ -438,7 +438,7 @@ static int ptp_pch_adjtime(struct ptp_clock_info *ptp, s64 delta)
        s64 now;
        unsigned long flags;
        struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps);
-       struct pch_ts_regs *regs = pch_dev->regs;
+       struct pch_ts_regs __iomem *regs = pch_dev->regs;
 
        spin_lock_irqsave(&pch_dev->register_lock, flags);
        now = pch_systime_read(regs);
@@ -455,7 +455,7 @@ static int ptp_pch_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
        u32 remainder;
        unsigned long flags;
        struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps);
-       struct pch_ts_regs *regs = pch_dev->regs;
+       struct pch_ts_regs __iomem *regs = pch_dev->regs;
 
        spin_lock_irqsave(&pch_dev->register_lock, flags);
        ns = pch_systime_read(regs);
@@ -472,7 +472,7 @@ static int ptp_pch_settime(struct ptp_clock_info *ptp,
        u64 ns;
        unsigned long flags;
        struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps);
-       struct pch_ts_regs *regs = pch_dev->regs;
+       struct pch_ts_regs __iomem *regs = pch_dev->regs;
 
        ns = ts->tv_sec * 1000000000ULL;
        ns += ts->tv_nsec;
@@ -567,9 +567,9 @@ static void pch_remove(struct pci_dev *pdev)
                free_irq(pdev->irq, chip);
 
        /* unmap the virtual IO memory space */
-       if (chip->regs != 0) {
+       if (chip->regs != NULL) {
                iounmap(chip->regs);
-               chip->regs = 0;
+               chip->regs = NULL;
        }
        /* release the reserved IO memory space */
        if (chip->mem_base != 0) {
@@ -670,7 +670,7 @@ pch_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 err_req_irq:
        ptp_clock_unregister(chip->ptp_clock);
        iounmap(chip->regs);
-       chip->regs = 0;
+       chip->regs = NULL;
 
 err_ioremap:
        release_mem_region(chip->mem_base, chip->mem_size);
@@ -723,9 +723,10 @@ static s32 __init ptp_pch_init(void)
 module_init(ptp_pch_init);
 module_exit(ptp_pch_exit);
 
-module_param_string(station, pch_param.station, sizeof pch_param.station, 0444);
+module_param_string(station,
+                   pch_param.station, sizeof(pch_param.station), 0444);
 MODULE_PARM_DESC(station,
-        "IEEE 1588 station address to use - column separated hex values");
+        "IEEE 1588 station address to use - colon separated hex values");
 
 MODULE_AUTHOR("LAPIS SEMICONDUCTOR, <tshimizu818@gmail.com>");
 MODULE_DESCRIPTION("PTP clock using the EG20T timer");
index 2029b6caa5956c2b4a01044b83325a0de16e3104..fb877b59ec57a472e74e314a7c8afb98c245b64a 100644 (file)
@@ -166,7 +166,7 @@ static void virtio_ccw_kvm_notify(struct virtqueue *vq)
 
        vcdev = to_vc_device(info->vq->vdev);
        ccw_device_get_schid(vcdev->cdev, &schid);
-       do_kvm_notify(schid, virtqueue_get_queue_index(vq));
+       do_kvm_notify(schid, vq->index);
 }
 
 static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev,
@@ -188,7 +188,7 @@ static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw)
        unsigned long flags;
        unsigned long size;
        int ret;
-       unsigned int index = virtqueue_get_queue_index(vq);
+       unsigned int index = vq->index;
 
        /* Remove from our list. */
        spin_lock_irqsave(&vcdev->lock, flags);
@@ -610,7 +610,7 @@ static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev,
        vq = NULL;
        spin_lock_irqsave(&vcdev->lock, flags);
        list_for_each_entry(info, &vcdev->virtqueues, node) {
-               if (virtqueue_get_queue_index(info->vq) == index) {
+               if (info->vq->index == index) {
                        vq = info->vq;
                        break;
                }
index 6ccb7457746b29e4e6ac0f53731bba725709f6a8..c4f392d5db4cd595dfea3211fbcc8f99a81f48c2 100644 (file)
@@ -918,7 +918,7 @@ int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
        int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),
        void *reply_param);
 int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);
-int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int);
+int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int);
 int qeth_get_elements_for_frags(struct sk_buff *);
 int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,
                        struct sk_buff *, struct qeth_hdr *, int, int, int);
@@ -932,7 +932,7 @@ void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
 void qeth_dbf_longtext(debug_info_t *id, int level, char *text, ...);
 int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *);
 int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback);
-int qeth_hdr_chk_and_bounce(struct sk_buff *, int);
+int qeth_hdr_chk_and_bounce(struct sk_buff *, struct qeth_hdr **, int);
 int qeth_configure_cq(struct qeth_card *, enum qeth_cq);
 int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
 int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot);
index 451f92020599e68add4a6b8a52888f672ad69084..6cd0fc1b203a2c6e8147dc37d980933ff9b7e89f 100644 (file)
@@ -335,7 +335,7 @@ static inline int qeth_alloc_cq(struct qeth_card *card)
 
                card->qdio.no_in_queues = 2;
 
-               card->qdio.out_bufstates = (struct qdio_outbuf_state *)
+               card->qdio.out_bufstates =
                        kzalloc(card->qdio.no_out_queues *
                                QDIO_MAX_BUFFERS_PER_Q *
                                sizeof(struct qdio_outbuf_state), GFP_KERNEL);
@@ -3717,7 +3717,7 @@ int qeth_get_elements_for_frags(struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags);
 
-int qeth_get_elements_no(struct qeth_card *card, void *hdr,
+int qeth_get_elements_no(struct qeth_card *card,
                     struct sk_buff *skb, int elems)
 {
        int dlen = skb->len - skb->data_len;
@@ -3736,7 +3736,7 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,
 }
 EXPORT_SYMBOL_GPL(qeth_get_elements_no);
 
-int qeth_hdr_chk_and_bounce(struct sk_buff *skb, int len)
+int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len)
 {
        int hroom, inpage, rest;
 
@@ -3749,6 +3749,8 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, int len)
                        return 1;
                memmove(skb->data - rest, skb->data, skb->len - skb->data_len);
                skb->data -= rest;
+               skb->tail -= rest;
+               *hdr = (struct qeth_hdr *)skb->data;
                QETH_DBF_MESSAGE(2, "skb bounce len: %d rest: %d\n", len, rest);
        }
        return 0;
index 155b101bd7309432d32ce9ed915c1a3a1b889a60..ec8ccdae7aba3d5b2685f4c8e7954912259b7870 100644 (file)
@@ -302,7 +302,8 @@ static void qeth_l2_process_vlans(struct qeth_card *card)
        spin_unlock_bh(&card->vlanlock);
 }
 
-static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+static int qeth_l2_vlan_rx_add_vid(struct net_device *dev,
+                                  __be16 proto, u16 vid)
 {
        struct qeth_card *card = dev->ml_priv;
        struct qeth_vlan_vid *id;
@@ -331,7 +332,8 @@ static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
        return 0;
 }
 
-static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
+                                   __be16 proto, u16 vid)
 {
        struct qeth_vlan_vid *id, *tmpid = NULL;
        struct qeth_card *card = dev->ml_priv;
@@ -771,8 +773,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                }
        }
 
-       elements = qeth_get_elements_no(card, (void *)hdr, new_skb,
-                                               elements_needed);
+       elements = qeth_get_elements_no(card, new_skb, elements_needed);
        if (!elements) {
                if (data_offset >= 0)
                        kmem_cache_free(qeth_core_header_cache, hdr);
@@ -780,7 +781,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        if (card->info.type != QETH_CARD_TYPE_IQD) {
-               if (qeth_hdr_chk_and_bounce(new_skb,
+               if (qeth_hdr_chk_and_bounce(new_skb, &hdr,
                    sizeof(struct qeth_hdr_layer2)))
                        goto tx_drop;
                rc = qeth_do_send_packet(card, queue, new_skb, hdr,
@@ -959,7 +960,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
                SET_ETHTOOL_OPS(card->dev, &qeth_l2_ethtool_ops);
        else
                SET_ETHTOOL_OPS(card->dev, &qeth_l2_osn_ops);
-       card->dev->features |= NETIF_F_HW_VLAN_FILTER;
+       card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
        card->info.broadcast_capable = 1;
        qeth_l2_request_initial_mac(card);
        SET_NETDEV_DEV(card->dev, &card->gdev->dev);
index 1f7edf1b26c33bff2b6a9baa6b163311a79fad6f..c1b0b2761f8dce6e0d3ad709902eeb19a925beba 100644 (file)
@@ -1659,7 +1659,8 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card)
        for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
                struct net_device *netdev;
 
-               netdev = __vlan_find_dev_deep(card->dev, vid);
+               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+                                             vid);
                if (netdev == NULL ||
                    !(netdev->flags & IFF_UP))
                        continue;
@@ -1720,7 +1721,8 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card)
        for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
                struct net_device *netdev;
 
-               netdev = __vlan_find_dev_deep(card->dev, vid);
+               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+                                             vid);
                if (netdev == NULL ||
                    !(netdev->flags & IFF_UP))
                        continue;
@@ -1764,7 +1766,7 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "frvaddr4");
 
-       netdev = __vlan_find_dev_deep(card->dev, vid);
+       netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid);
        if (!netdev)
                return;
        in_dev = in_dev_get(netdev);
@@ -1794,7 +1796,7 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "frvaddr6");
 
-       netdev = __vlan_find_dev_deep(card->dev, vid);
+       netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid);
        if (!netdev)
                return;
        in6_dev = in6_dev_get(netdev);
@@ -1824,7 +1826,8 @@ static void qeth_l3_free_vlan_addresses(struct qeth_card *card,
        rcu_read_unlock();
 }
 
-static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+static int qeth_l3_vlan_rx_add_vid(struct net_device *dev,
+                                  __be16 proto, u16 vid)
 {
        struct qeth_card *card = dev->ml_priv;
 
@@ -1832,7 +1835,8 @@ static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
        return 0;
 }
 
-static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev,
+                                   __be16 proto, u16 vid)
 {
        struct qeth_card *card = dev->ml_priv;
        unsigned long flags;
@@ -1975,7 +1979,8 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
                                                      &vlan_tag);
                                len = skb->len;
                                if (is_vlan && !card->options.sniffer)
-                                       __vlan_hwaccel_put_tag(skb, vlan_tag);
+                                       __vlan_hwaccel_put_tag(skb,
+                                               htons(ETH_P_8021Q), vlan_tag);
                                napi_gro_receive(&card->napi, skb);
                        }
                        break;
@@ -2084,7 +2089,8 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,
                struct net_device *netdev;
 
                rcu_read_lock();
-               netdev = __vlan_find_dev_deep(card->dev, vid);
+               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+                                             vid);
                rcu_read_unlock();
                if (netdev == dev) {
                        rc = QETH_VLAN_CARD;
@@ -3031,8 +3037,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        qeth_l3_hdr_csum(card, hdr, new_skb);
        }
 
-       elems = qeth_get_elements_no(card, (void *)hdr, new_skb,
-                                                elements_needed);
+       elems = qeth_get_elements_no(card, new_skb, elements_needed);
        if (!elems) {
                if (data_offset >= 0)
                        kmem_cache_free(qeth_core_header_cache, hdr);
@@ -3050,7 +3055,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                else
                        len = sizeof(struct qeth_hdr_layer3);
 
-               if (qeth_hdr_chk_and_bounce(new_skb, len))
+               if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len))
                        goto tx_drop;
                rc = qeth_do_send_packet(card, queue, new_skb, hdr,
                                         elements_needed);
@@ -3294,9 +3299,9 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->mtu = card->info.initial_mtu;
        SET_ETHTOOL_OPS(card->dev, &qeth_l3_ethtool_ops);
-       card->dev->features |=  NETIF_F_HW_VLAN_TX |
-                               NETIF_F_HW_VLAN_RX |
-                               NETIF_F_HW_VLAN_FILTER;
+       card->dev->features |=  NETIF_F_HW_VLAN_CTAG_TX |
+                               NETIF_F_HW_VLAN_CTAG_RX |
+                               NETIF_F_HW_VLAN_CTAG_FILTER;
        card->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
        card->dev->gso_max_size = 15 * PAGE_SIZE;
 
index b581966c88f950825babad388956fbc497bc6115..913b9a92fb0641a57f3cdd109ff699f4f230a37c 100644 (file)
@@ -8,4 +8,5 @@ ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb4
 obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor.o
 
 csiostor-objs := csio_attr.o csio_init.o csio_lnode.o csio_scsi.o \
-               csio_hw.o csio_isr.o csio_mb.o csio_rnode.o csio_wr.o
+               csio_hw.o csio_hw_t4.o csio_hw_t5.o csio_isr.o \
+               csio_mb.o csio_rnode.o csio_wr.o
index bdd78fb4fc70c5a7c91131e35a94a00e6bf6bb94..a0b4c8991deb9c2ef7dc4c95a3e4ff6862cf8068 100644 (file)
@@ -61,7 +61,7 @@ int csio_msi = 2;
 static int dev_num;
 
 /* FCoE Adapter types & its description */
-static const struct csio_adap_desc csio_fcoe_adapters[] = {
+static const struct csio_adap_desc csio_t4_fcoe_adapters[] = {
        {"T440-Dbg 10G", "Chelsio T440-Dbg 10G [FCoE]"},
        {"T420-CR 10G", "Chelsio T420-CR 10G [FCoE]"},
        {"T422-CR 10G/1G", "Chelsio T422-CR 10G/1G [FCoE]"},
@@ -77,7 +77,38 @@ static const struct csio_adap_desc csio_fcoe_adapters[] = {
        {"B404-BT 1G", "Chelsio B404-BT 1G [FCoE]"},
        {"T480-CR 10G", "Chelsio T480-CR 10G [FCoE]"},
        {"T440-LP-CR 10G", "Chelsio T440-LP-CR 10G [FCoE]"},
-       {"T4 FPGA", "Chelsio T4 FPGA [FCoE]"}
+       {"AMSTERDAM 10G", "Chelsio AMSTERDAM 10G [FCoE]"},
+       {"HUAWEI T480 10G", "Chelsio HUAWEI T480 10G [FCoE]"},
+       {"HUAWEI T440 10G", "Chelsio HUAWEI T440 10G [FCoE]"},
+       {"HUAWEI STG 10G", "Chelsio HUAWEI STG 10G [FCoE]"},
+       {"ACROMAG XAUI 10G", "Chelsio ACROMAG XAUI 10G [FCoE]"},
+       {"ACROMAG SFP+ 10G", "Chelsio ACROMAG SFP+ 10G [FCoE]"},
+       {"QUANTA SFP+ 10G", "Chelsio QUANTA SFP+ 10G [FCoE]"},
+       {"HUAWEI 10Gbase-T", "Chelsio HUAWEI 10Gbase-T [FCoE]"},
+       {"HUAWEI T4TOE 10G", "Chelsio HUAWEI T4TOE 10G [FCoE]"}
+};
+
+static const struct csio_adap_desc csio_t5_fcoe_adapters[] = {
+       {"T580-Dbg 10G", "Chelsio T580-Dbg 10G [FCoE]"},
+       {"T520-CR 10G", "Chelsio T520-CR 10G [FCoE]"},
+       {"T522-CR 10G/1G", "Chelsio T452-CR 10G/1G [FCoE]"},
+       {"T540-CR 10G", "Chelsio T540-CR 10G [FCoE]"},
+       {"T520-BCH 10G", "Chelsio T520-BCH 10G [FCoE]"},
+       {"T540-BCH 10G", "Chelsio T540-BCH 10G [FCoE]"},
+       {"T540-CH 10G", "Chelsio T540-CH 10G [FCoE]"},
+       {"T520-SO 10G", "Chelsio T520-SO 10G [FCoE]"},
+       {"T520-CX4 10G", "Chelsio T520-CX4 10G [FCoE]"},
+       {"T520-BT 10G", "Chelsio T520-BT 10G [FCoE]"},
+       {"T504-BT 1G", "Chelsio T504-BT 1G [FCoE]"},
+       {"B520-SR 10G", "Chelsio B520-SR 10G [FCoE]"},
+       {"B504-BT 1G", "Chelsio B504-BT 1G [FCoE]"},
+       {"T580-CR 10G", "Chelsio T580-CR 10G [FCoE]"},
+       {"T540-LP-CR 10G", "Chelsio T540-LP-CR 10G [FCoE]"},
+       {"AMSTERDAM 10G", "Chelsio AMSTERDAM 10G [FCoE]"},
+       {"T580-LP-CR 40G", "Chelsio T580-LP-CR 40G [FCoE]"},
+       {"T520-LL-CR 10G", "Chelsio T520-LL-CR 10G [FCoE]"},
+       {"T560-CR 40G", "Chelsio T560-CR 40G [FCoE]"},
+       {"T580-CR 40G", "Chelsio T580-CR 40G [FCoE]"}
 };
 
 static void csio_mgmtm_cleanup(struct csio_mgmtm *);
@@ -124,7 +155,7 @@ int csio_is_hw_removing(struct csio_hw *hw)
  *     at the time it indicated completion is stored there.  Returns 0 if the
  *     operation completes and -EAGAIN otherwise.
  */
-static int
+int
 csio_hw_wait_op_done_val(struct csio_hw *hw, int reg, uint32_t mask,
                         int polarity, int attempts, int delay, uint32_t *valp)
 {
@@ -145,6 +176,24 @@ csio_hw_wait_op_done_val(struct csio_hw *hw, int reg, uint32_t mask,
        }
 }
 
+/*
+ *     csio_hw_tp_wr_bits_indirect - set/clear bits in an indirect TP register
+ *     @hw: the adapter
+ *     @addr: the indirect TP register address
+ *     @mask: specifies the field within the register to modify
+ *     @val: new value for the field
+ *
+ *     Sets a field of an indirect TP register to the given value.
+ */
+void
+csio_hw_tp_wr_bits_indirect(struct csio_hw *hw, unsigned int addr,
+                       unsigned int mask, unsigned int val)
+{
+       csio_wr_reg32(hw, addr, TP_PIO_ADDR);
+       val |= csio_rd_reg32(hw, TP_PIO_DATA) & ~mask;
+       csio_wr_reg32(hw, val, TP_PIO_DATA);
+}
+
 void
 csio_set_reg_field(struct csio_hw *hw, uint32_t reg, uint32_t mask,
                   uint32_t value)
@@ -157,242 +206,22 @@ csio_set_reg_field(struct csio_hw *hw, uint32_t reg, uint32_t mask,
 
 }
 
-/*
- *     csio_hw_mc_read - read from MC through backdoor accesses
- *     @hw: the hw module
- *     @addr: address of first byte requested
- *     @data: 64 bytes of data containing the requested address
- *     @ecc: where to store the corresponding 64-bit ECC word
- *
- *     Read 64 bytes of data from MC starting at a 64-byte-aligned address
- *     that covers the requested address @addr.  If @parity is not %NULL it
- *     is assigned the 64-bit ECC word for the read data.
- */
-int
-csio_hw_mc_read(struct csio_hw *hw, uint32_t addr, __be32 *data,
-               uint64_t *ecc)
-{
-       int i;
-
-       if (csio_rd_reg32(hw, MC_BIST_CMD) & START_BIST)
-               return -EBUSY;
-       csio_wr_reg32(hw, addr & ~0x3fU, MC_BIST_CMD_ADDR);
-       csio_wr_reg32(hw, 64, MC_BIST_CMD_LEN);
-       csio_wr_reg32(hw, 0xc, MC_BIST_DATA_PATTERN);
-       csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST |  BIST_CMD_GAP(1),
-                     MC_BIST_CMD);
-       i = csio_hw_wait_op_done_val(hw, MC_BIST_CMD, START_BIST,
-                0, 10, 1, NULL);
-       if (i)
-               return i;
-
-#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i)
-
-       for (i = 15; i >= 0; i--)
-               *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i)));
-       if (ecc)
-               *ecc = csio_rd_reg64(hw, MC_DATA(16));
-#undef MC_DATA
-       return 0;
-}
-
-/*
- *     csio_hw_edc_read - read from EDC through backdoor accesses
- *     @hw: the hw module
- *     @idx: which EDC to access
- *     @addr: address of first byte requested
- *     @data: 64 bytes of data containing the requested address
- *     @ecc: where to store the corresponding 64-bit ECC word
- *
- *     Read 64 bytes of data from EDC starting at a 64-byte-aligned address
- *     that covers the requested address @addr.  If @parity is not %NULL it
- *     is assigned the 64-bit ECC word for the read data.
- */
-int
-csio_hw_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data,
-               uint64_t *ecc)
-{
-       int i;
-
-       idx *= EDC_STRIDE;
-       if (csio_rd_reg32(hw, EDC_BIST_CMD + idx) & START_BIST)
-               return -EBUSY;
-       csio_wr_reg32(hw, addr & ~0x3fU, EDC_BIST_CMD_ADDR + idx);
-       csio_wr_reg32(hw, 64, EDC_BIST_CMD_LEN + idx);
-       csio_wr_reg32(hw, 0xc, EDC_BIST_DATA_PATTERN + idx);
-       csio_wr_reg32(hw, BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST,
-                    EDC_BIST_CMD + idx);
-       i = csio_hw_wait_op_done_val(hw, EDC_BIST_CMD + idx, START_BIST,
-                0, 10, 1, NULL);
-       if (i)
-               return i;
-
-#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx)
-
-       for (i = 15; i >= 0; i--)
-               *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i)));
-       if (ecc)
-               *ecc = csio_rd_reg64(hw, EDC_DATA(16));
-#undef EDC_DATA
-       return 0;
-}
-
-/*
- *      csio_mem_win_rw - read/write memory through PCIE memory window
- *      @hw: the adapter
- *      @addr: address of first byte requested
- *      @data: MEMWIN0_APERTURE bytes of data containing the requested address
- *      @dir: direction of transfer 1 => read, 0 => write
- *
- *      Read/write MEMWIN0_APERTURE bytes of data from MC starting at a
- *      MEMWIN0_APERTURE-byte-aligned address that covers the requested
- *      address @addr.
- */
-static int
-csio_mem_win_rw(struct csio_hw *hw, u32 addr, u32 *data, int dir)
-{
-       int i;
-
-       /*
-        * Setup offset into PCIE memory window.  Address must be a
-        * MEMWIN0_APERTURE-byte-aligned address.  (Read back MA register to
-        * ensure that changes propagate before we attempt to use the new
-        * values.)
-        */
-       csio_wr_reg32(hw, addr & ~(MEMWIN0_APERTURE - 1),
-                       PCIE_MEM_ACCESS_OFFSET);
-       csio_rd_reg32(hw, PCIE_MEM_ACCESS_OFFSET);
-
-       /* Collecting data 4 bytes at a time upto MEMWIN0_APERTURE */
-       for (i = 0; i < MEMWIN0_APERTURE; i = i + sizeof(__be32)) {
-               if (dir)
-                       *data++ = csio_rd_reg32(hw, (MEMWIN0_BASE + i));
-               else
-                       csio_wr_reg32(hw, *data++, (MEMWIN0_BASE + i));
-       }
-
-       return 0;
-}
-
-/*
- *      csio_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window
- *      @hw: the csio_hw
- *      @mtype: memory type: MEM_EDC0, MEM_EDC1 or MEM_MC
- *      @addr: address within indicated memory type
- *      @len: amount of memory to transfer
- *      @buf: host memory buffer
- *      @dir: direction of transfer 1 => read, 0 => write
- *
- *      Reads/writes an [almost] arbitrary memory region in the firmware: the
- *      firmware memory address, length and host buffer must be aligned on
- *      32-bit boudaries.  The memory is transferred as a raw byte sequence
- *      from/to the firmware's memory.  If this memory contains data
- *      structures which contain multi-byte integers, it's the callers
- *      responsibility to perform appropriate byte order conversions.
- */
-static int
-csio_memory_rw(struct csio_hw *hw, int mtype, u32 addr, u32 len,
-               uint32_t *buf, int dir)
-{
-       uint32_t pos, start, end, offset, memoffset;
-       int ret;
-       uint32_t *data;
-
-       /*
-        * Argument sanity checks ...
-        */
-       if ((addr & 0x3) || (len & 0x3))
-               return -EINVAL;
-
-       data = kzalloc(MEMWIN0_APERTURE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       /* Offset into the region of memory which is being accessed
-        * MEM_EDC0 = 0
-        * MEM_EDC1 = 1
-        * MEM_MC   = 2
-        */
-       memoffset = (mtype * (5 * 1024 * 1024));
-
-       /* Determine the PCIE_MEM_ACCESS_OFFSET */
-       addr = addr + memoffset;
-
-       /*
-        * The underlaying EDC/MC read routines read MEMWIN0_APERTURE bytes
-        * at a time so we need to round down the start and round up the end.
-        * We'll start copying out of the first line at (addr - start) a word
-        * at a time.
-        */
-       start = addr & ~(MEMWIN0_APERTURE-1);
-       end = (addr + len + MEMWIN0_APERTURE-1) & ~(MEMWIN0_APERTURE-1);
-       offset = (addr - start)/sizeof(__be32);
-
-       for (pos = start; pos < end; pos += MEMWIN0_APERTURE, offset = 0) {
-               /*
-                * If we're writing, copy the data from the caller's memory
-                * buffer
-                */
-               if (!dir) {
-                       /*
-                        * If we're doing a partial write, then we need to do
-                        * a read-modify-write ...
-                        */
-                       if (offset || len < MEMWIN0_APERTURE) {
-                               ret = csio_mem_win_rw(hw, pos, data, 1);
-                               if (ret) {
-                                       kfree(data);
-                                       return ret;
-                               }
-                       }
-                       while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) &&
-                                                               len > 0) {
-                               data[offset++] = *buf++;
-                               len -= sizeof(__be32);
-                       }
-               }
-
-               /*
-                * Transfer a block of memory and bail if there's an error.
-                */
-               ret = csio_mem_win_rw(hw, pos, data, dir);
-               if (ret) {
-                       kfree(data);
-                       return ret;
-               }
-
-               /*
-                * If we're reading, copy the data into the caller's memory
-                * buffer.
-                */
-               if (dir)
-                       while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) &&
-                                                               len > 0) {
-                               *buf++ = data[offset++];
-                               len -= sizeof(__be32);
-                       }
-       }
-
-       kfree(data);
-
-       return 0;
-}
-
 static int
 csio_memory_write(struct csio_hw *hw, int mtype, u32 addr, u32 len, u32 *buf)
 {
-       return csio_memory_rw(hw, mtype, addr, len, buf, 0);
+       return hw->chip_ops->chip_memory_rw(hw, MEMWIN_CSIOSTOR, mtype,
+                                           addr, len, buf, 0);
 }
 
 /*
  * EEPROM reads take a few tens of us while writes can take a bit over 5 ms.
  */
-#define EEPROM_MAX_RD_POLL 40
-#define EEPROM_MAX_WR_POLL 6
-#define EEPROM_STAT_ADDR   0x7bfc
-#define VPD_BASE           0x400
-#define VPD_BASE_OLD      0
-#define VPD_LEN            512
+#define EEPROM_MAX_RD_POLL     40
+#define EEPROM_MAX_WR_POLL     6
+#define EEPROM_STAT_ADDR       0x7bfc
+#define VPD_BASE               0x400
+#define VPD_BASE_OLD           0
+#define VPD_LEN                        1024
 #define VPD_INFO_FLD_HDR_SIZE  3
 
 /*
@@ -817,23 +646,6 @@ out:
        return 0;
 }
 
-/*
- *     csio_hw_flash_cfg_addr - return the address of the flash
- *                             configuration file
- *     @hw: the HW module
- *
- *     Return the address within the flash where the Firmware Configuration
- *     File is stored.
- */
-static unsigned int
-csio_hw_flash_cfg_addr(struct csio_hw *hw)
-{
-       if (hw->params.sf_size == 0x100000)
-               return FPGA_FLASH_CFG_OFFSET;
-       else
-               return FLASH_CFG_OFFSET;
-}
-
 static void
 csio_hw_print_fw_version(struct csio_hw *hw, char *str)
 {
@@ -898,13 +710,13 @@ csio_hw_check_fw_version(struct csio_hw *hw)
        minor = FW_HDR_FW_VER_MINOR_GET(hw->fwrev);
        micro = FW_HDR_FW_VER_MICRO_GET(hw->fwrev);
 
-       if (major != FW_VERSION_MAJOR) {            /* major mismatch - fail */
+       if (major != FW_VERSION_MAJOR(hw)) {    /* major mismatch - fail */
                csio_err(hw, "card FW has major version %u, driver wants %u\n",
-                        major, FW_VERSION_MAJOR);
+                        major, FW_VERSION_MAJOR(hw));
                return -EINVAL;
        }
 
-       if (minor == FW_VERSION_MINOR && micro == FW_VERSION_MICRO)
+       if (minor == FW_VERSION_MINOR(hw) && micro == FW_VERSION_MICRO(hw))
                return 0;        /* perfect match */
 
        /* Minor/micro version mismatch */
@@ -1044,7 +856,7 @@ static void
 csio_set_pcie_completion_timeout(struct csio_hw *hw, u8 range)
 {
        uint16_t val;
-       uint32_t pcie_cap;
+       int pcie_cap;
 
        if (!csio_pci_capability(hw->pdev, PCI_CAP_ID_EXP, &pcie_cap)) {
                pci_read_config_word(hw->pdev,
@@ -1056,84 +868,6 @@ csio_set_pcie_completion_timeout(struct csio_hw *hw, u8 range)
        }
 }
 
-
-/*
- * Return the specified PCI-E Configuration Space register from our Physical
- * Function.  We try first via a Firmware LDST Command since we prefer to let
- * the firmware own all of these registers, but if that fails we go for it
- * directly ourselves.
- */
-static uint32_t
-csio_read_pcie_cfg4(struct csio_hw *hw, int reg)
-{
-       u32 val = 0;
-       struct csio_mb *mbp;
-       int rv;
-       struct fw_ldst_cmd *ldst_cmd;
-
-       mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
-       if (!mbp) {
-               CSIO_INC_STATS(hw, n_err_nomem);
-               pci_read_config_dword(hw->pdev, reg, &val);
-               return val;
-       }
-
-       csio_mb_ldst(hw, mbp, CSIO_MB_DEFAULT_TMO, reg);
-
-       rv = csio_mb_issue(hw, mbp);
-
-       /*
-        * If the LDST Command suucceeded, exctract the returned register
-        * value.  Otherwise read it directly ourself.
-        */
-       if (rv == 0) {
-               ldst_cmd = (struct fw_ldst_cmd *)(mbp->mb);
-               val = ntohl(ldst_cmd->u.pcie.data[0]);
-       } else
-               pci_read_config_dword(hw->pdev, reg, &val);
-
-       mempool_free(mbp, hw->mb_mempool);
-
-       return val;
-} /* csio_read_pcie_cfg4 */
-
-static int
-csio_hw_set_mem_win(struct csio_hw *hw)
-{
-       u32 bar0;
-
-       /*
-        * Truncation intentional: we only read the bottom 32-bits of the
-        * 64-bit BAR0/BAR1 ...  We use the hardware backdoor mechanism to
-        * read BAR0 instead of using pci_resource_start() because we could be
-        * operating from within a Virtual Machine which is trapping our
-        * accesses to our Configuration Space and we need to set up the PCI-E
-        * Memory Window decoders with the actual addresses which will be
-        * coming across the PCI-E link.
-        */
-       bar0 = csio_read_pcie_cfg4(hw, PCI_BASE_ADDRESS_0);
-       bar0 &= PCI_BASE_ADDRESS_MEM_MASK;
-
-       /*
-        * Set up memory window for accessing adapter memory ranges.  (Read
-        * back MA register to ensure that changes propagate before we attempt
-        * to use the new values.)
-        */
-       csio_wr_reg32(hw, (bar0 + MEMWIN0_BASE) | BIR(0) |
-               WINDOW(ilog2(MEMWIN0_APERTURE) - 10),
-               PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 0));
-       csio_wr_reg32(hw, (bar0 + MEMWIN1_BASE) | BIR(0) |
-               WINDOW(ilog2(MEMWIN1_APERTURE) - 10),
-               PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 1));
-       csio_wr_reg32(hw, (bar0 + MEMWIN2_BASE) | BIR(0) |
-               WINDOW(ilog2(MEMWIN2_APERTURE) - 10),
-               PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2));
-       csio_rd_reg32(hw, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2));
-       return 0;
-} /* csio_hw_set_mem_win */
-
-
-
 /*****************************************************************************/
 /* HW State machine assists                                                  */
 /*****************************************************************************/
@@ -1234,7 +968,9 @@ retry:
                for (;;) {
                        uint32_t pcie_fw;
 
+                       spin_unlock_irq(&hw->lock);
                        msleep(50);
+                       spin_lock_irq(&hw->lock);
                        waiting -= 50;
 
                        /*
@@ -2121,9 +1857,9 @@ csio_hw_flash_config(struct csio_hw *hw, u32 *fw_cfg_param, char *path)
        uint32_t *cfg_data;
        int value_to_add = 0;
 
-       if (request_firmware(&cf, CSIO_CF_FNAME, dev) < 0) {
-               csio_err(hw, "could not find config file " CSIO_CF_FNAME
-                        ",err: %d\n", ret);
+       if (request_firmware(&cf, CSIO_CF_FNAME(hw), dev) < 0) {
+               csio_err(hw, "could not find config file %s, err: %d\n",
+                        CSIO_CF_FNAME(hw), ret);
                return -ENOENT;
        }
 
@@ -2147,9 +1883,24 @@ csio_hw_flash_config(struct csio_hw *hw, u32 *fw_cfg_param, char *path)
 
        ret = csio_memory_write(hw, mtype, maddr,
                                cf->size + value_to_add, cfg_data);
+
+       if ((ret == 0) && (value_to_add != 0)) {
+               union {
+                       u32 word;
+                       char buf[4];
+               } last;
+               size_t size = cf->size & ~0x3;
+               int i;
+
+               last.word = cfg_data[size >> 2];
+               for (i = value_to_add; i < 4; i++)
+                       last.buf[i] = 0;
+               ret = csio_memory_write(hw, mtype, maddr + size, 4, &last.word);
+       }
        if (ret == 0) {
-               csio_info(hw, "config file upgraded to " CSIO_CF_FNAME "\n");
-               strncpy(path, "/lib/firmware/" CSIO_CF_FNAME, 64);
+               csio_info(hw, "config file upgraded to %s\n",
+                         CSIO_CF_FNAME(hw));
+               snprintf(path, 64, "%s%s", "/lib/firmware/", CSIO_CF_FNAME(hw));
        }
 
 leave:
@@ -2179,7 +1930,7 @@ csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param)
 {
        unsigned int mtype, maddr;
        int rv;
-       uint32_t finiver, finicsum, cfcsum;
+       uint32_t finiver = 0, finicsum = 0, cfcsum = 0;
        int using_flash;
        char path[64];
 
@@ -2207,7 +1958,7 @@ csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param)
                         * config file from flash.
                         */
                        mtype = FW_MEMTYPE_CF_FLASH;
-                       maddr = csio_hw_flash_cfg_addr(hw);
+                       maddr = hw->chip_ops->chip_flash_cfg_addr(hw);
                        using_flash = 1;
                } else {
                        /*
@@ -2346,30 +2097,32 @@ csio_hw_flash_fw(struct csio_hw *hw)
        struct pci_dev *pci_dev = hw->pdev;
        struct device *dev = &pci_dev->dev ;
 
-       if (request_firmware(&fw, CSIO_FW_FNAME, dev) < 0) {
-               csio_err(hw, "could not find firmware image " CSIO_FW_FNAME
-               ",err: %d\n", ret);
+       if (request_firmware(&fw, CSIO_FW_FNAME(hw), dev) < 0) {
+               csio_err(hw, "could not find firmware image %s, err: %d\n",
+                        CSIO_FW_FNAME(hw), ret);
                return -EINVAL;
        }
 
        hdr = (const struct fw_hdr *)fw->data;
        fw_ver = ntohl(hdr->fw_ver);
-       if (FW_HDR_FW_VER_MAJOR_GET(fw_ver) != FW_VERSION_MAJOR)
+       if (FW_HDR_FW_VER_MAJOR_GET(fw_ver) != FW_VERSION_MAJOR(hw))
                return -EINVAL;      /* wrong major version, won't do */
 
        /*
         * If the flash FW is unusable or we found something newer, load it.
         */
-       if (FW_HDR_FW_VER_MAJOR_GET(hw->fwrev) != FW_VERSION_MAJOR ||
+       if (FW_HDR_FW_VER_MAJOR_GET(hw->fwrev) != FW_VERSION_MAJOR(hw) ||
            fw_ver > hw->fwrev) {
                ret = csio_hw_fw_upgrade(hw, hw->pfn, fw->data, fw->size,
                                    /*force=*/false);
                if (!ret)
-                       csio_info(hw, "firmware upgraded to version %pI4 from "
-                                 CSIO_FW_FNAME "\n", &hdr->fw_ver);
+                       csio_info(hw,
+                                 "firmware upgraded to version %pI4 from %s\n",
+                                 &hdr->fw_ver, CSIO_FW_FNAME(hw));
                else
                        csio_err(hw, "firmware upgrade failed! err=%d\n", ret);
-       }
+       } else
+               ret = -EINVAL;
 
        release_firmware(fw);
 
@@ -2410,7 +2163,7 @@ csio_hw_configure(struct csio_hw *hw)
        /* Set pci completion timeout value to 4 seconds. */
        csio_set_pcie_completion_timeout(hw, 0xd);
 
-       csio_hw_set_mem_win(hw);
+       hw->chip_ops->chip_set_mem_win(hw, MEMWIN_CSIOSTOR);
 
        rv = csio_hw_get_fw_version(hw, &hw->fwrev);
        if (rv != 0)
@@ -2478,6 +2231,8 @@ csio_hw_configure(struct csio_hw *hw)
        } else {
                if (hw->fw_state == CSIO_DEV_STATE_INIT) {
 
+                       hw->flags |= CSIO_HWF_USING_SOFT_PARAMS;
+
                        /* device parameters */
                        rv = csio_get_device_params(hw);
                        if (rv != 0)
@@ -2651,7 +2406,7 @@ csio_hw_intr_disable(struct csio_hw *hw)
 
 }
 
-static void
+void
 csio_hw_fatal_err(struct csio_hw *hw)
 {
        csio_set_reg_field(hw, SGE_CONTROL, GLOBALENABLE, 0);
@@ -2990,14 +2745,6 @@ csio_hws_pcierr(struct csio_hw *hw, enum csio_hw_ev evt)
 /* END: HW SM                                                                */
 /*****************************************************************************/
 
-/* Slow path handlers */
-struct intr_info {
-       unsigned int mask;       /* bits to check in interrupt status */
-       const char *msg;         /* message to print or NULL */
-       short stat_idx;          /* stat counter to increment or -1 */
-       unsigned short fatal;    /* whether the condition reported is fatal */
-};
-
 /*
  *     csio_handle_intr_status - table driven interrupt handler
  *     @hw: HW instance
@@ -3011,7 +2758,7 @@ struct intr_info {
  *     by an entry specifying mask 0.  Returns the number of fatal interrupt
  *     conditions.
  */
-static int
+int
 csio_handle_intr_status(struct csio_hw *hw, unsigned int reg,
                                 const struct intr_info *acts)
 {
@@ -3037,80 +2784,6 @@ csio_handle_intr_status(struct csio_hw *hw, unsigned int reg,
        return fatal;
 }
 
-/*
- * Interrupt handler for the PCIE module.
- */
-static void
-csio_pcie_intr_handler(struct csio_hw *hw)
-{
-       static struct intr_info sysbus_intr_info[] = {
-               { RNPP, "RXNP array parity error", -1, 1 },
-               { RPCP, "RXPC array parity error", -1, 1 },
-               { RCIP, "RXCIF array parity error", -1, 1 },
-               { RCCP, "Rx completions control array parity error", -1, 1 },
-               { RFTP, "RXFT array parity error", -1, 1 },
-               { 0, NULL, 0, 0 }
-       };
-       static struct intr_info pcie_port_intr_info[] = {
-               { TPCP, "TXPC array parity error", -1, 1 },
-               { TNPP, "TXNP array parity error", -1, 1 },
-               { TFTP, "TXFT array parity error", -1, 1 },
-               { TCAP, "TXCA array parity error", -1, 1 },
-               { TCIP, "TXCIF array parity error", -1, 1 },
-               { RCAP, "RXCA array parity error", -1, 1 },
-               { OTDD, "outbound request TLP discarded", -1, 1 },
-               { RDPE, "Rx data parity error", -1, 1 },
-               { TDUE, "Tx uncorrectable data error", -1, 1 },
-               { 0, NULL, 0, 0 }
-       };
-       static struct intr_info pcie_intr_info[] = {
-               { MSIADDRLPERR, "MSI AddrL parity error", -1, 1 },
-               { MSIADDRHPERR, "MSI AddrH parity error", -1, 1 },
-               { MSIDATAPERR, "MSI data parity error", -1, 1 },
-               { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 },
-               { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 },
-               { MSIXDATAPERR, "MSI-X data parity error", -1, 1 },
-               { MSIXDIPERR, "MSI-X DI parity error", -1, 1 },
-               { PIOCPLPERR, "PCI PIO completion FIFO parity error", -1, 1 },
-               { PIOREQPERR, "PCI PIO request FIFO parity error", -1, 1 },
-               { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 },
-               { CCNTPERR, "PCI CMD channel count parity error", -1, 1 },
-               { CREQPERR, "PCI CMD channel request parity error", -1, 1 },
-               { CRSPPERR, "PCI CMD channel response parity error", -1, 1 },
-               { DCNTPERR, "PCI DMA channel count parity error", -1, 1 },
-               { DREQPERR, "PCI DMA channel request parity error", -1, 1 },
-               { DRSPPERR, "PCI DMA channel response parity error", -1, 1 },
-               { HCNTPERR, "PCI HMA channel count parity error", -1, 1 },
-               { HREQPERR, "PCI HMA channel request parity error", -1, 1 },
-               { HRSPPERR, "PCI HMA channel response parity error", -1, 1 },
-               { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 },
-               { FIDPERR, "PCI FID parity error", -1, 1 },
-               { INTXCLRPERR, "PCI INTx clear parity error", -1, 1 },
-               { MATAGPERR, "PCI MA tag parity error", -1, 1 },
-               { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 },
-               { RXCPLPERR, "PCI Rx completion parity error", -1, 1 },
-               { RXWRPERR, "PCI Rx write parity error", -1, 1 },
-               { RPLPERR, "PCI replay buffer parity error", -1, 1 },
-               { PCIESINT, "PCI core secondary fault", -1, 1 },
-               { PCIEPINT, "PCI core primary fault", -1, 1 },
-               { UNXSPLCPLERR, "PCI unexpected split completion error", -1,
-                 0 },
-               { 0, NULL, 0, 0 }
-       };
-
-       int fat;
-
-       fat = csio_handle_intr_status(hw,
-                                   PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS,
-                                   sysbus_intr_info) +
-             csio_handle_intr_status(hw,
-                                   PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS,
-                                   pcie_port_intr_info) +
-             csio_handle_intr_status(hw, PCIE_INT_CAUSE, pcie_intr_info);
-       if (fat)
-               csio_hw_fatal_err(hw);
-}
-
 /*
  * TP interrupt handler.
  */
@@ -3517,7 +3190,7 @@ static void csio_ncsi_intr_handler(struct csio_hw *hw)
  */
 static void csio_xgmac_intr_handler(struct csio_hw *hw, int port)
 {
-       uint32_t v = csio_rd_reg32(hw, PORT_REG(port, XGMAC_PORT_INT_CAUSE));
+       uint32_t v = csio_rd_reg32(hw, CSIO_MAC_INT_CAUSE_REG(hw, port));
 
        v &= TXFIFO_PRTY_ERR | RXFIFO_PRTY_ERR;
        if (!v)
@@ -3527,7 +3200,7 @@ static void csio_xgmac_intr_handler(struct csio_hw *hw, int port)
                csio_fatal(hw, "XGMAC %d Tx FIFO parity error\n", port);
        if (v & RXFIFO_PRTY_ERR)
                csio_fatal(hw, "XGMAC %d Rx FIFO parity error\n", port);
-       csio_wr_reg32(hw, v, PORT_REG(port, XGMAC_PORT_INT_CAUSE));
+       csio_wr_reg32(hw, v, CSIO_MAC_INT_CAUSE_REG(hw, port));
        csio_hw_fatal_err(hw);
 }
 
@@ -3596,7 +3269,7 @@ csio_hw_slow_intr_handler(struct csio_hw *hw)
                csio_xgmac_intr_handler(hw, 3);
 
        if (cause & PCIE)
-               csio_pcie_intr_handler(hw);
+               hw->chip_ops->chip_pcie_intr_handler(hw);
 
        if (cause & MC)
                csio_mem_intr_handler(hw, MEM_MC);
@@ -4262,6 +3935,7 @@ csio_hw_get_device_id(struct csio_hw *hw)
                             &hw->params.pci.device_id);
 
        csio_dev_id_cached(hw);
+       hw->chip_id = (hw->params.pci.device_id & CSIO_HW_CHIP_MASK);
 
 } /* csio_hw_get_device_id */
 
@@ -4280,19 +3954,21 @@ csio_hw_set_description(struct csio_hw *hw, uint16_t ven_id, uint16_t dev_id)
                prot_type = (dev_id & CSIO_ASIC_DEVID_PROTO_MASK);
                adap_type = (dev_id & CSIO_ASIC_DEVID_TYPE_MASK);
 
-               if (prot_type == CSIO_FPGA) {
+               if (prot_type == CSIO_T4_FCOE_ASIC) {
+                       memcpy(hw->hw_ver,
+                              csio_t4_fcoe_adapters[adap_type].model_no, 16);
                        memcpy(hw->model_desc,
-                               csio_fcoe_adapters[13].description, 32);
-               } else if (prot_type == CSIO_T4_FCOE_ASIC) {
+                              csio_t4_fcoe_adapters[adap_type].description,
+                              32);
+               } else if (prot_type == CSIO_T5_FCOE_ASIC) {
                        memcpy(hw->hw_ver,
-                              csio_fcoe_adapters[adap_type].model_no, 16);
+                              csio_t5_fcoe_adapters[adap_type].model_no, 16);
                        memcpy(hw->model_desc,
-                               csio_fcoe_adapters[adap_type].description, 32);
+                              csio_t5_fcoe_adapters[adap_type].description,
+                              32);
                } else {
                        char tempName[32] = "Chelsio FCoE Controller";
                        memcpy(hw->model_desc, tempName, 32);
-
-                       CSIO_DB_ASSERT(0);
                }
        }
 } /* csio_hw_set_description */
@@ -4321,6 +3997,9 @@ csio_hw_init(struct csio_hw *hw)
 
        strcpy(hw->name, CSIO_HW_NAME);
 
+       /* Initialize the HW chip ops with T4/T5 specific ops */
+       hw->chip_ops = csio_is_t4(hw->chip_id) ? &t4_ops : &t5_ops;
+
        /* Set the model & its description */
 
        ven_id = hw->params.pci.vendor_id;
index 9edcca4c71af32a1c58f523399afd87a1afde088..489fc095cb034232eacf4a7a4747fa9999d00816 100644 (file)
@@ -48,6 +48,7 @@
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "csio_hw_chip.h"
 #include "csio_wr.h"
 #include "csio_mb.h"
 #include "csio_scsi.h"
  */
 #define        FW_HOSTERROR                    255
 
-#define CSIO_FW_FNAME          "cxgb4/t4fw.bin"
-#define CSIO_CF_FNAME          "cxgb4/t4-config.txt"
-
-#define FW_VERSION_MAJOR       1
-#define FW_VERSION_MINOR       2
-#define FW_VERSION_MICRO       8
-
 #define CSIO_HW_NAME           "Chelsio FCoE Adapter"
 #define CSIO_MAX_PFN           8
 #define CSIO_MAX_PPORTS                4
@@ -123,8 +117,6 @@ extern int csio_msi;
 #define CSIO_VENDOR_ID                         0x1425
 #define CSIO_ASIC_DEVID_PROTO_MASK             0xFF00
 #define CSIO_ASIC_DEVID_TYPE_MASK              0x00FF
-#define CSIO_FPGA                              0xA000
-#define CSIO_T4_FCOE_ASIC                      0x4600
 
 #define CSIO_GLBL_INTR_MASK            (CIM | MPS | PL | PCIE | MC | EDC0 | \
                                         EDC1 | LE | TP | MA | PM_TX | PM_RX | \
@@ -207,17 +199,6 @@ enum {
        SF_SIZE = SF_SEC_SIZE * 16,   /* serial flash size */
 };
 
-enum { MEM_EDC0, MEM_EDC1, MEM_MC };
-
-enum {
-       MEMWIN0_APERTURE = 2048,
-       MEMWIN0_BASE     = 0x1b800,
-       MEMWIN1_APERTURE = 32768,
-       MEMWIN1_BASE     = 0x28000,
-       MEMWIN2_APERTURE = 65536,
-       MEMWIN2_BASE     = 0x30000,
-};
-
 /* serial flash and firmware constants */
 enum {
        SF_ATTEMPTS = 10,             /* max retries for SF operations */
@@ -239,9 +220,6 @@ enum {
        FLASH_CFG_MAX_SIZE    = 0x10000 , /* max size of the flash config file*/
        FLASH_CFG_OFFSET      = 0x1f0000,
        FLASH_CFG_START_SEC   = FLASH_CFG_OFFSET / SF_SEC_SIZE,
-       FPGA_FLASH_CFG_OFFSET = 0xf0000 , /* if FPGA mode, then cfg file is
-                                          * at 1MB - 64KB */
-       FPGA_FLASH_CFG_START_SEC  = FPGA_FLASH_CFG_OFFSET / SF_SEC_SIZE,
 };
 
 /*
@@ -259,6 +237,8 @@ enum {
        FLASH_FW_START = FLASH_START(FLASH_FW_START_SEC),
        FLASH_FW_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FW_NSECS),
 
+       /* Location of Firmware Configuration File in FLASH. */
+       FLASH_CFG_START = FLASH_START(FLASH_CFG_START_SEC),
 };
 
 #undef FLASH_START
@@ -310,7 +290,7 @@ struct csio_adap_desc {
 struct pci_params {
        uint16_t   vendor_id;
        uint16_t   device_id;
-       uint32_t   vpd_cap_addr;
+       int        vpd_cap_addr;
        uint16_t   speed;
        uint8_t    width;
 };
@@ -513,6 +493,7 @@ struct csio_hw {
        uint32_t                fwrev;
        uint32_t                tp_vers;
        char                    chip_ver;
+       uint16_t                chip_id;                /* Tells T4/T5 chip */
        uint32_t                cfg_finiver;
        uint32_t                cfg_finicsum;
        uint32_t                cfg_cfcsum;
@@ -556,6 +537,9 @@ struct csio_hw {
                                                         */
 
        struct csio_fcoe_res_info  fres_info;           /* Fcoe resource info */
+       struct csio_hw_chip_ops *chip_ops;              /* T4/T5 Chip specific
+                                                        * Operations
+                                                        */
 
        /* MSIX vectors */
        struct csio_msix_entries msix_entries[CSIO_MAX_MSIX_VECS];
@@ -636,9 +620,16 @@ csio_us_to_core_ticks(struct csio_hw *hw, uint32_t us)
 #define csio_dbg(__hw, __fmt, ...)
 #endif
 
+int csio_hw_wait_op_done_val(struct csio_hw *, int, uint32_t, int,
+                            int, int, uint32_t *);
+void csio_hw_tp_wr_bits_indirect(struct csio_hw *, unsigned int,
+                                unsigned int, unsigned int);
 int csio_mgmt_req_lookup(struct csio_mgmtm *, struct csio_ioreq *);
 void csio_hw_intr_disable(struct csio_hw *);
-int csio_hw_slow_intr_handler(struct csio_hw *hw);
+int csio_hw_slow_intr_handler(struct csio_hw *);
+int csio_handle_intr_status(struct csio_hw *, unsigned int,
+                           const struct intr_info *);
+
 int csio_hw_start(struct csio_hw *);
 int csio_hw_stop(struct csio_hw *);
 int csio_hw_reset(struct csio_hw *);
@@ -647,19 +638,17 @@ int csio_is_hw_removing(struct csio_hw *);
 
 int csio_fwevtq_handler(struct csio_hw *);
 void csio_evtq_worker(struct work_struct *);
-int csio_enqueue_evt(struct csio_hw *hw, enum csio_evt type,
-                               void *evt_msg, uint16_t len);
+int csio_enqueue_evt(struct csio_hw *, enum csio_evt, void *, uint16_t);
 void csio_evtq_flush(struct csio_hw *hw);
 
 int csio_request_irqs(struct csio_hw *);
 void csio_intr_enable(struct csio_hw *);
 void csio_intr_disable(struct csio_hw *, bool);
+void csio_hw_fatal_err(struct csio_hw *);
 
 struct csio_lnode *csio_lnode_alloc(struct csio_hw *);
 int csio_config_queues(struct csio_hw *);
 
-int csio_hw_mc_read(struct csio_hw *, uint32_t, __be32 *, uint64_t *);
-int csio_hw_edc_read(struct csio_hw *, int, uint32_t, __be32 *, uint64_t *);
 int csio_hw_init(struct csio_hw *);
 void csio_hw_exit(struct csio_hw *);
 #endif /* ifndef __CSIO_HW_H__ */
diff --git a/drivers/scsi/csiostor/csio_hw_chip.h b/drivers/scsi/csiostor/csio_hw_chip.h
new file mode 100644 (file)
index 0000000..bca0de6
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2013 Chelsio Communications, 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
+ * 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 __CSIO_HW_CHIP_H__
+#define __CSIO_HW_CHIP_H__
+
+#include "csio_defs.h"
+
+/* FCoE device IDs for T4 */
+#define CSIO_DEVID_T440DBG_FCOE                        0x4600
+#define CSIO_DEVID_T420CR_FCOE                 0x4601
+#define CSIO_DEVID_T422CR_FCOE                 0x4602
+#define CSIO_DEVID_T440CR_FCOE                 0x4603
+#define CSIO_DEVID_T420BCH_FCOE                        0x4604
+#define CSIO_DEVID_T440BCH_FCOE                        0x4605
+#define CSIO_DEVID_T440CH_FCOE                 0x4606
+#define CSIO_DEVID_T420SO_FCOE                 0x4607
+#define CSIO_DEVID_T420CX_FCOE                 0x4608
+#define CSIO_DEVID_T420BT_FCOE                 0x4609
+#define CSIO_DEVID_T404BT_FCOE                 0x460A
+#define CSIO_DEVID_B420_FCOE                   0x460B
+#define CSIO_DEVID_B404_FCOE                   0x460C
+#define CSIO_DEVID_T480CR_FCOE                 0x460D
+#define CSIO_DEVID_T440LPCR_FCOE               0x460E
+#define CSIO_DEVID_AMSTERDAM_T4_FCOE           0x460F
+#define CSIO_DEVID_HUAWEI_T480_FCOE            0x4680
+#define CSIO_DEVID_HUAWEI_T440_FCOE            0x4681
+#define CSIO_DEVID_HUAWEI_STG310_FCOE          0x4682
+#define CSIO_DEVID_ACROMAG_XMC_XAUI            0x4683
+#define CSIO_DEVID_ACROMAG_XMC_SFP_FCOE                0x4684
+#define CSIO_DEVID_QUANTA_MEZZ_SFP_FCOE                0x4685
+#define CSIO_DEVID_HUAWEI_10GT_FCOE            0x4686
+#define CSIO_DEVID_HUAWEI_T440_TOE_FCOE                0x4687
+
+/* FCoE device IDs for T5 */
+#define CSIO_DEVID_T580DBG_FCOE                        0x5600
+#define CSIO_DEVID_T520CR_FCOE                 0x5601
+#define CSIO_DEVID_T522CR_FCOE                 0x5602
+#define CSIO_DEVID_T540CR_FCOE                 0x5603
+#define CSIO_DEVID_T520BCH_FCOE                        0x5604
+#define CSIO_DEVID_T540BCH_FCOE                        0x5605
+#define CSIO_DEVID_T540CH_FCOE                 0x5606
+#define CSIO_DEVID_T520SO_FCOE                 0x5607
+#define CSIO_DEVID_T520CX_FCOE                 0x5608
+#define CSIO_DEVID_T520BT_FCOE                 0x5609
+#define CSIO_DEVID_T504BT_FCOE                 0x560A
+#define CSIO_DEVID_B520_FCOE                   0x560B
+#define CSIO_DEVID_B504_FCOE                   0x560C
+#define CSIO_DEVID_T580CR2_FCOE                        0x560D
+#define CSIO_DEVID_T540LPCR_FCOE               0x560E
+#define CSIO_DEVID_AMSTERDAM_T5_FCOE           0x560F
+#define CSIO_DEVID_T580LPCR_FCOE               0x5610
+#define CSIO_DEVID_T520LLCR_FCOE               0x5611
+#define CSIO_DEVID_T560CR_FCOE                 0x5612
+#define CSIO_DEVID_T580CR_FCOE                 0x5613
+
+/* Define MACRO values */
+#define CSIO_HW_T4                             0x4000
+#define CSIO_T4_FCOE_ASIC                      0x4600
+#define CSIO_HW_T5                             0x5000
+#define CSIO_T5_FCOE_ASIC                      0x5600
+#define CSIO_HW_CHIP_MASK                      0xF000
+#define T4_REGMAP_SIZE                         (160 * 1024)
+#define T5_REGMAP_SIZE                         (332 * 1024)
+#define FW_FNAME_T4                            "cxgb4/t4fw.bin"
+#define FW_FNAME_T5                            "cxgb4/t5fw.bin"
+#define FW_CFG_NAME_T4                         "cxgb4/t4-config.txt"
+#define FW_CFG_NAME_T5                         "cxgb4/t5-config.txt"
+
+/* Define static functions */
+static inline int csio_is_t4(uint16_t chip)
+{
+       return (chip == CSIO_HW_T4);
+}
+
+static inline int csio_is_t5(uint16_t chip)
+{
+       return (chip == CSIO_HW_T5);
+}
+
+/* Define MACRO DEFINITIONS */
+#define CSIO_DEVICE(devid, idx)                                                \
+       { PCI_VENDOR_ID_CHELSIO, (devid), PCI_ANY_ID, PCI_ANY_ID, 0, 0, (idx) }
+
+#define CSIO_HW_PIDX(hw, index)                                                \
+       (csio_is_t4(hw->chip_id) ? (PIDX(index)) :                      \
+                                       (PIDX_T5(index) | DBTYPE(1U)))
+
+#define CSIO_HW_LP_INT_THRESH(hw, val)                                 \
+       (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH(val)) :               \
+                                       (V_LP_INT_THRESH_T5(val)))
+
+#define CSIO_HW_M_LP_INT_THRESH(hw)                                    \
+       (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_MASK) : (M_LP_INT_THRESH_T5))
+
+#define CSIO_MAC_INT_CAUSE_REG(hw, port)                               \
+       (csio_is_t4(hw->chip_id) ? (PORT_REG(port, XGMAC_PORT_INT_CAUSE)) : \
+                               (T5_PORT_REG(port, MAC_PORT_INT_CAUSE)))
+
+#define FW_VERSION_MAJOR(hw) (csio_is_t4(hw->chip_id) ? 1 : 0)
+#define FW_VERSION_MINOR(hw) (csio_is_t4(hw->chip_id) ? 2 : 0)
+#define FW_VERSION_MICRO(hw) (csio_is_t4(hw->chip_id) ? 8 : 0)
+
+#define CSIO_FW_FNAME(hw)                                              \
+       (csio_is_t4(hw->chip_id) ? FW_FNAME_T4 : FW_FNAME_T5)
+
+#define CSIO_CF_FNAME(hw)                                              \
+       (csio_is_t4(hw->chip_id) ? FW_CFG_NAME_T4 : FW_CFG_NAME_T5)
+
+/* Declare ENUMS */
+enum { MEM_EDC0, MEM_EDC1, MEM_MC, MEM_MC0 = MEM_MC, MEM_MC1 };
+
+enum {
+       MEMWIN_APERTURE = 2048,
+       MEMWIN_BASE     = 0x1b800,
+       MEMWIN_CSIOSTOR = 6,            /* PCI-e Memory Window access */
+};
+
+/* Slow path handlers */
+struct intr_info {
+       unsigned int mask;       /* bits to check in interrupt status */
+       const char *msg;         /* message to print or NULL */
+       short stat_idx;          /* stat counter to increment or -1 */
+       unsigned short fatal;    /* whether the condition reported is fatal */
+};
+
+/* T4/T5 Chip specific ops */
+struct csio_hw;
+struct csio_hw_chip_ops {
+       int (*chip_set_mem_win)(struct csio_hw *, uint32_t);
+       void (*chip_pcie_intr_handler)(struct csio_hw *);
+       uint32_t (*chip_flash_cfg_addr)(struct csio_hw *);
+       int (*chip_mc_read)(struct csio_hw *, int, uint32_t,
+                                       __be32 *, uint64_t *);
+       int (*chip_edc_read)(struct csio_hw *, int, uint32_t,
+                                       __be32 *, uint64_t *);
+       int (*chip_memory_rw)(struct csio_hw *, u32, int, u32,
+                                       u32, uint32_t *, int);
+       void (*chip_dfs_create_ext_mem)(struct csio_hw *);
+};
+
+extern struct csio_hw_chip_ops t4_ops;
+extern struct csio_hw_chip_ops t5_ops;
+
+#endif /* #ifndef __CSIO_HW_CHIP_H__ */
diff --git a/drivers/scsi/csiostor/csio_hw_t4.c b/drivers/scsi/csiostor/csio_hw_t4.c
new file mode 100644 (file)
index 0000000..89ecbac
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2013 Chelsio Communications, 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
+ * 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
+ *      - 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 "csio_hw.h"
+#include "csio_init.h"
+
+/*
+ * Return the specified PCI-E Configuration Space register from our Physical
+ * Function.  We try first via a Firmware LDST Command since we prefer to let
+ * the firmware own all of these registers, but if that fails we go for it
+ * directly ourselves.
+ */
+static uint32_t
+csio_t4_read_pcie_cfg4(struct csio_hw *hw, int reg)
+{
+       u32 val = 0;
+       struct csio_mb *mbp;
+       int rv;
+       struct fw_ldst_cmd *ldst_cmd;
+
+       mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+       if (!mbp) {
+               CSIO_INC_STATS(hw, n_err_nomem);
+               pci_read_config_dword(hw->pdev, reg, &val);
+               return val;
+       }
+
+       csio_mb_ldst(hw, mbp, CSIO_MB_DEFAULT_TMO, reg);
+       rv = csio_mb_issue(hw, mbp);
+
+       /*
+        * If the LDST Command suucceeded, exctract the returned register
+        * value.  Otherwise read it directly ourself.
+        */
+       if (rv == 0) {
+               ldst_cmd = (struct fw_ldst_cmd *)(mbp->mb);
+               val = ntohl(ldst_cmd->u.pcie.data[0]);
+       } else
+               pci_read_config_dword(hw->pdev, reg, &val);
+
+       mempool_free(mbp, hw->mb_mempool);
+
+       return val;
+}
+
+static int
+csio_t4_set_mem_win(struct csio_hw *hw, uint32_t win)
+{
+       u32 bar0;
+       u32 mem_win_base;
+
+       /*
+        * Truncation intentional: we only read the bottom 32-bits of the
+        * 64-bit BAR0/BAR1 ...  We use the hardware backdoor mechanism to
+        * read BAR0 instead of using pci_resource_start() because we could be
+        * operating from within a Virtual Machine which is trapping our
+        * accesses to our Configuration Space and we need to set up the PCI-E
+        * Memory Window decoders with the actual addresses which will be
+        * coming across the PCI-E link.
+        */
+       bar0 = csio_t4_read_pcie_cfg4(hw, PCI_BASE_ADDRESS_0);
+       bar0 &= PCI_BASE_ADDRESS_MEM_MASK;
+
+       mem_win_base = bar0 + MEMWIN_BASE;
+
+       /*
+        * Set up memory window for accessing adapter memory ranges.  (Read
+        * back MA register to ensure that changes propagate before we attempt
+        * to use the new values.)
+        */
+       csio_wr_reg32(hw, mem_win_base | BIR(0) |
+                         WINDOW(ilog2(MEMWIN_APERTURE) - 10),
+                         PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win));
+       csio_rd_reg32(hw,
+                     PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win));
+       return 0;
+}
+
+/*
+ * Interrupt handler for the PCIE module.
+ */
+static void
+csio_t4_pcie_intr_handler(struct csio_hw *hw)
+{
+       static struct intr_info sysbus_intr_info[] = {
+               { RNPP, "RXNP array parity error", -1, 1 },
+               { RPCP, "RXPC array parity error", -1, 1 },
+               { RCIP, "RXCIF array parity error", -1, 1 },
+               { RCCP, "Rx completions control array parity error", -1, 1 },
+               { RFTP, "RXFT array parity error", -1, 1 },
+               { 0, NULL, 0, 0 }
+       };
+       static struct intr_info pcie_port_intr_info[] = {
+               { TPCP, "TXPC array parity error", -1, 1 },
+               { TNPP, "TXNP array parity error", -1, 1 },
+               { TFTP, "TXFT array parity error", -1, 1 },
+               { TCAP, "TXCA array parity error", -1, 1 },
+               { TCIP, "TXCIF array parity error", -1, 1 },
+               { RCAP, "RXCA array parity error", -1, 1 },
+               { OTDD, "outbound request TLP discarded", -1, 1 },
+               { RDPE, "Rx data parity error", -1, 1 },
+               { TDUE, "Tx uncorrectable data error", -1, 1 },
+               { 0, NULL, 0, 0 }
+       };
+
+       static struct intr_info pcie_intr_info[] = {
+               { MSIADDRLPERR, "MSI AddrL parity error", -1, 1 },
+               { MSIADDRHPERR, "MSI AddrH parity error", -1, 1 },
+               { MSIDATAPERR, "MSI data parity error", -1, 1 },
+               { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 },
+               { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 },
+               { MSIXDATAPERR, "MSI-X data parity error", -1, 1 },
+               { MSIXDIPERR, "MSI-X DI parity error", -1, 1 },
+               { PIOCPLPERR, "PCI PIO completion FIFO parity error", -1, 1 },
+               { PIOREQPERR, "PCI PIO request FIFO parity error", -1, 1 },
+               { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 },
+               { CCNTPERR, "PCI CMD channel count parity error", -1, 1 },
+               { CREQPERR, "PCI CMD channel request parity error", -1, 1 },
+               { CRSPPERR, "PCI CMD channel response parity error", -1, 1 },
+               { DCNTPERR, "PCI DMA channel count parity error", -1, 1 },
+               { DREQPERR, "PCI DMA channel request parity error", -1, 1 },
+               { DRSPPERR, "PCI DMA channel response parity error", -1, 1 },
+               { HCNTPERR, "PCI HMA channel count parity error", -1, 1 },
+               { HREQPERR, "PCI HMA channel request parity error", -1, 1 },
+               { HRSPPERR, "PCI HMA channel response parity error", -1, 1 },
+               { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 },
+               { FIDPERR, "PCI FID parity error", -1, 1 },
+               { INTXCLRPERR, "PCI INTx clear parity error", -1, 1 },
+               { MATAGPERR, "PCI MA tag parity error", -1, 1 },
+               { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 },
+               { RXCPLPERR, "PCI Rx completion parity error", -1, 1 },
+               { RXWRPERR, "PCI Rx write parity error", -1, 1 },
+               { RPLPERR, "PCI replay buffer parity error", -1, 1 },
+               { PCIESINT, "PCI core secondary fault", -1, 1 },
+               { PCIEPINT, "PCI core primary fault", -1, 1 },
+               { UNXSPLCPLERR, "PCI unexpected split completion error", -1,
+                 0 },
+               { 0, NULL, 0, 0 }
+       };
+
+       int fat;
+       fat = csio_handle_intr_status(hw,
+                                     PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS,
+                                     sysbus_intr_info) +
+             csio_handle_intr_status(hw,
+                                     PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS,
+                                     pcie_port_intr_info) +
+             csio_handle_intr_status(hw, PCIE_INT_CAUSE, pcie_intr_info);
+       if (fat)
+               csio_hw_fatal_err(hw);
+}
+
+/*
+ * csio_t4_flash_cfg_addr - return the address of the flash configuration file
+ * @hw: the HW module
+ *
+ * Return the address within the flash where the Firmware Configuration
+ * File is stored.
+ */
+static unsigned int
+csio_t4_flash_cfg_addr(struct csio_hw *hw)
+{
+       return FLASH_CFG_OFFSET;
+}
+
+/*
+ *      csio_t4_mc_read - read from MC through backdoor accesses
+ *      @hw: the hw module
+ *      @idx: not used for T4 adapter
+ *      @addr: address of first byte requested
+ *      @data: 64 bytes of data containing the requested address
+ *      @ecc: where to store the corresponding 64-bit ECC word
+ *
+ *      Read 64 bytes of data from MC starting at a 64-byte-aligned address
+ *      that covers the requested address @addr.  If @parity is not %NULL it
+ *      is assigned the 64-bit ECC word for the read data.
+ */
+static int
+csio_t4_mc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data,
+               uint64_t *ecc)
+{
+       int i;
+
+       if (csio_rd_reg32(hw, MC_BIST_CMD) & START_BIST)
+               return -EBUSY;
+       csio_wr_reg32(hw, addr & ~0x3fU, MC_BIST_CMD_ADDR);
+       csio_wr_reg32(hw, 64, MC_BIST_CMD_LEN);
+       csio_wr_reg32(hw, 0xc, MC_BIST_DATA_PATTERN);
+       csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST | BIST_CMD_GAP(1),
+                     MC_BIST_CMD);
+       i = csio_hw_wait_op_done_val(hw, MC_BIST_CMD, START_BIST,
+                                    0, 10, 1, NULL);
+       if (i)
+               return i;
+
+#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i)
+
+       for (i = 15; i >= 0; i--)
+               *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i)));
+       if (ecc)
+               *ecc = csio_rd_reg64(hw, MC_DATA(16));
+#undef MC_DATA
+       return 0;
+}
+
+/*
+ *      csio_t4_edc_read - read from EDC through backdoor accesses
+ *      @hw: the hw module
+ *      @idx: which EDC to access
+ *      @addr: address of first byte requested
+ *      @data: 64 bytes of data containing the requested address
+ *      @ecc: where to store the corresponding 64-bit ECC word
+ *
+ *      Read 64 bytes of data from EDC starting at a 64-byte-aligned address
+ *      that covers the requested address @addr.  If @parity is not %NULL it
+ *      is assigned the 64-bit ECC word for the read data.
+ */
+static int
+csio_t4_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data,
+               uint64_t *ecc)
+{
+       int i;
+
+       idx *= EDC_STRIDE;
+       if (csio_rd_reg32(hw, EDC_BIST_CMD + idx) & START_BIST)
+               return -EBUSY;
+       csio_wr_reg32(hw, addr & ~0x3fU, EDC_BIST_CMD_ADDR + idx);
+       csio_wr_reg32(hw, 64, EDC_BIST_CMD_LEN + idx);
+       csio_wr_reg32(hw, 0xc, EDC_BIST_DATA_PATTERN + idx);
+       csio_wr_reg32(hw, BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST,
+                     EDC_BIST_CMD + idx);
+       i = csio_hw_wait_op_done_val(hw, EDC_BIST_CMD + idx, START_BIST,
+                                    0, 10, 1, NULL);
+       if (i)
+               return i;
+
+#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx)
+
+       for (i = 15; i >= 0; i--)
+               *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i)));
+       if (ecc)
+               *ecc = csio_rd_reg64(hw, EDC_DATA(16));
+#undef EDC_DATA
+       return 0;
+}
+
+/*
+ * csio_t4_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window
+ * @hw: the csio_hw
+ * @win: PCI-E memory Window to use
+ * @mtype: memory type: MEM_EDC0, MEM_EDC1, MEM_MC0 (or MEM_MC) or MEM_MC1
+ * @addr: address within indicated memory type
+ * @len: amount of memory to transfer
+ * @buf: host memory buffer
+ * @dir: direction of transfer 1 => read, 0 => write
+ *
+ * Reads/writes an [almost] arbitrary memory region in the firmware: the
+ * firmware memory address, length and host buffer must be aligned on
+ * 32-bit boudaries.  The memory is transferred as a raw byte sequence
+ * from/to the firmware's memory.  If this memory contains data
+ * structures which contain multi-byte integers, it's the callers
+ * responsibility to perform appropriate byte order conversions.
+ */
+static int
+csio_t4_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr,
+               u32 len, uint32_t *buf, int dir)
+{
+       u32 pos, start, offset, memoffset, bar0;
+       u32 edc_size, mc_size, mem_reg, mem_aperture, mem_base;
+
+       /*
+        * Argument sanity checks ...
+        */
+       if ((addr & 0x3) || (len & 0x3))
+               return -EINVAL;
+
+       /* Offset into the region of memory which is being accessed
+        * MEM_EDC0 = 0
+        * MEM_EDC1 = 1
+        * MEM_MC   = 2 -- T4
+        */
+       edc_size  = EDRAM_SIZE_GET(csio_rd_reg32(hw, MA_EDRAM0_BAR));
+       if (mtype != MEM_MC1)
+               memoffset = (mtype * (edc_size * 1024 * 1024));
+       else {
+               mc_size = EXT_MEM_SIZE_GET(csio_rd_reg32(hw,
+                                                        MA_EXT_MEMORY_BAR));
+               memoffset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024;
+       }
+
+       /* Determine the PCIE_MEM_ACCESS_OFFSET */
+       addr = addr + memoffset;
+
+       /*
+        * Each PCI-E Memory Window is programmed with a window size -- or
+        * "aperture" -- which controls the granularity of its mapping onto
+        * adapter memory.  We need to grab that aperture in order to know
+        * how to use the specified window.  The window is also programmed
+        * with the base address of the Memory Window in BAR0's address
+        * space.  For T4 this is an absolute PCI-E Bus Address.  For T5
+        * the address is relative to BAR0.
+        */
+       mem_reg = csio_rd_reg32(hw,
+                       PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win));
+       mem_aperture = 1 << (WINDOW(mem_reg) + 10);
+       mem_base = GET_PCIEOFST(mem_reg) << 10;
+
+       bar0 = csio_t4_read_pcie_cfg4(hw, PCI_BASE_ADDRESS_0);
+       bar0 &= PCI_BASE_ADDRESS_MEM_MASK;
+       mem_base -= bar0;
+
+       start = addr & ~(mem_aperture-1);
+       offset = addr - start;
+
+       csio_dbg(hw, "csio_t4_memory_rw: mem_reg: 0x%x, mem_aperture: 0x%x\n",
+                mem_reg, mem_aperture);
+       csio_dbg(hw, "csio_t4_memory_rw: mem_base: 0x%x, mem_offset: 0x%x\n",
+                mem_base, memoffset);
+       csio_dbg(hw, "csio_t4_memory_rw: bar0: 0x%x, start:0x%x, offset:0x%x\n",
+                bar0, start, offset);
+       csio_dbg(hw, "csio_t4_memory_rw: mtype: %d, addr: 0x%x, len: %d\n",
+                mtype, addr, len);
+
+       for (pos = start; len > 0; pos += mem_aperture, offset = 0) {
+               /*
+                * Move PCI-E Memory Window to our current transfer
+                * position.  Read it back to ensure that changes propagate
+                * before we attempt to use the new value.
+                */
+               csio_wr_reg32(hw, pos,
+                       PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win));
+               csio_rd_reg32(hw,
+                       PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win));
+
+               while (offset < mem_aperture && len > 0) {
+                       if (dir)
+                               *buf++ = csio_rd_reg32(hw, mem_base + offset);
+                       else
+                               csio_wr_reg32(hw, *buf++, mem_base + offset);
+
+                       offset += sizeof(__be32);
+                       len -= sizeof(__be32);
+               }
+       }
+       return 0;
+}
+
+/*
+ * csio_t4_dfs_create_ext_mem - setup debugfs for MC to read the values
+ * @hw: the csio_hw
+ *
+ * This function creates files in the debugfs with external memory region MC.
+ */
+static void
+csio_t4_dfs_create_ext_mem(struct csio_hw *hw)
+{
+       u32 size;
+       int i = csio_rd_reg32(hw, MA_TARGET_MEM_ENABLE);
+       if (i & EXT_MEM_ENABLE) {
+               size = csio_rd_reg32(hw, MA_EXT_MEMORY_BAR);
+               csio_add_debugfs_mem(hw, "mc", MEM_MC,
+                                    EXT_MEM_SIZE_GET(size));
+       }
+}
+
+/* T4 adapter specific function */
+struct csio_hw_chip_ops t4_ops = {
+       .chip_set_mem_win               = csio_t4_set_mem_win,
+       .chip_pcie_intr_handler         = csio_t4_pcie_intr_handler,
+       .chip_flash_cfg_addr            = csio_t4_flash_cfg_addr,
+       .chip_mc_read                   = csio_t4_mc_read,
+       .chip_edc_read                  = csio_t4_edc_read,
+       .chip_memory_rw                 = csio_t4_memory_rw,
+       .chip_dfs_create_ext_mem        = csio_t4_dfs_create_ext_mem,
+};
diff --git a/drivers/scsi/csiostor/csio_hw_t5.c b/drivers/scsi/csiostor/csio_hw_t5.c
new file mode 100644 (file)
index 0000000..27745c1
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2013 Chelsio Communications, 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
+ * 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 "csio_hw.h"
+#include "csio_init.h"
+
+static int
+csio_t5_set_mem_win(struct csio_hw *hw, uint32_t win)
+{
+       u32 mem_win_base;
+       /*
+        * Truncation intentional: we only read the bottom 32-bits of the
+        * 64-bit BAR0/BAR1 ...  We use the hardware backdoor mechanism to
+        * read BAR0 instead of using pci_resource_start() because we could be
+        * operating from within a Virtual Machine which is trapping our
+        * accesses to our Configuration Space and we need to set up the PCI-E
+        * Memory Window decoders with the actual addresses which will be
+        * coming across the PCI-E link.
+        */
+
+       /* For T5, only relative offset inside the PCIe BAR is passed */
+       mem_win_base = MEMWIN_BASE;
+
+       /*
+        * Set up memory window for accessing adapter memory ranges.  (Read
+        * back MA register to ensure that changes propagate before we attempt
+        * to use the new values.)
+        */
+       csio_wr_reg32(hw, mem_win_base | BIR(0) |
+                         WINDOW(ilog2(MEMWIN_APERTURE) - 10),
+                         PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win));
+       csio_rd_reg32(hw,
+                     PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win));
+
+       return 0;
+}
+
+/*
+ * Interrupt handler for the PCIE module.
+ */
+static void
+csio_t5_pcie_intr_handler(struct csio_hw *hw)
+{
+       static struct intr_info sysbus_intr_info[] = {
+               { RNPP, "RXNP array parity error", -1, 1 },
+               { RPCP, "RXPC array parity error", -1, 1 },
+               { RCIP, "RXCIF array parity error", -1, 1 },
+               { RCCP, "Rx completions control array parity error", -1, 1 },
+               { RFTP, "RXFT array parity error", -1, 1 },
+               { 0, NULL, 0, 0 }
+       };
+       static struct intr_info pcie_port_intr_info[] = {
+               { TPCP, "TXPC array parity error", -1, 1 },
+               { TNPP, "TXNP array parity error", -1, 1 },
+               { TFTP, "TXFT array parity error", -1, 1 },
+               { TCAP, "TXCA array parity error", -1, 1 },
+               { TCIP, "TXCIF array parity error", -1, 1 },
+               { RCAP, "RXCA array parity error", -1, 1 },
+               { OTDD, "outbound request TLP discarded", -1, 1 },
+               { RDPE, "Rx data parity error", -1, 1 },
+               { TDUE, "Tx uncorrectable data error", -1, 1 },
+               { 0, NULL, 0, 0 }
+       };
+
+       static struct intr_info pcie_intr_info[] = {
+               { MSTGRPPERR, "Master Response Read Queue parity error",
+               -1, 1 },
+               { MSTTIMEOUTPERR, "Master Timeout FIFO parity error", -1, 1 },
+               { MSIXSTIPERR, "MSI-X STI SRAM parity error", -1, 1 },
+               { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 },
+               { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 },
+               { MSIXDATAPERR, "MSI-X data parity error", -1, 1 },
+               { MSIXDIPERR, "MSI-X DI parity error", -1, 1 },
+               { PIOCPLGRPPERR, "PCI PIO completion Group FIFO parity error",
+               -1, 1 },
+               { PIOREQGRPPERR, "PCI PIO request Group FIFO parity error",
+               -1, 1 },
+               { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 },
+               { MSTTAGQPERR, "PCI master tag queue parity error", -1, 1 },
+               { CREQPERR, "PCI CMD channel request parity error", -1, 1 },
+               { CRSPPERR, "PCI CMD channel response parity error", -1, 1 },
+               { DREQWRPERR, "PCI DMA channel write request parity error",
+               -1, 1 },
+               { DREQPERR, "PCI DMA channel request parity error", -1, 1 },
+               { DRSPPERR, "PCI DMA channel response parity error", -1, 1 },
+               { HREQWRPERR, "PCI HMA channel count parity error", -1, 1 },
+               { HREQPERR, "PCI HMA channel request parity error", -1, 1 },
+               { HRSPPERR, "PCI HMA channel response parity error", -1, 1 },
+               { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 },
+               { FIDPERR, "PCI FID parity error", -1, 1 },
+               { VFIDPERR, "PCI INTx clear parity error", -1, 1 },
+               { MAGRPPERR, "PCI MA group FIFO parity error", -1, 1 },
+               { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 },
+               { IPRXHDRGRPPERR, "PCI IP Rx header group parity error",
+               -1, 1 },
+               { IPRXDATAGRPPERR, "PCI IP Rx data group parity error",
+               -1, 1 },
+               { RPLPERR, "PCI IP replay buffer parity error", -1, 1 },
+               { IPSOTPERR, "PCI IP SOT buffer parity error", -1, 1 },
+               { TRGT1GRPPERR, "PCI TRGT1 group FIFOs parity error", -1, 1 },
+               { READRSPERR, "Outbound read error", -1, 0 },
+               { 0, NULL, 0, 0 }
+       };
+
+       int fat;
+       fat = csio_handle_intr_status(hw,
+                                     PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS,
+                                     sysbus_intr_info) +
+             csio_handle_intr_status(hw,
+                                     PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS,
+                                     pcie_port_intr_info) +
+             csio_handle_intr_status(hw, PCIE_INT_CAUSE, pcie_intr_info);
+       if (fat)
+               csio_hw_fatal_err(hw);
+}
+
+/*
+ * csio_t5_flash_cfg_addr - return the address of the flash configuration file
+ * @hw: the HW module
+ *
+ * Return the address within the flash where the Firmware Configuration
+ * File is stored.
+ */
+static unsigned int
+csio_t5_flash_cfg_addr(struct csio_hw *hw)
+{
+       return FLASH_CFG_START;
+}
+
+/*
+ *      csio_t5_mc_read - read from MC through backdoor accesses
+ *      @hw: the hw module
+ *      @idx: index to the register
+ *      @addr: address of first byte requested
+ *      @data: 64 bytes of data containing the requested address
+ *      @ecc: where to store the corresponding 64-bit ECC word
+ *
+ *      Read 64 bytes of data from MC starting at a 64-byte-aligned address
+ *      that covers the requested address @addr.  If @parity is not %NULL it
+ *      is assigned the 64-bit ECC word for the read data.
+ */
+static int
+csio_t5_mc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data,
+               uint64_t *ecc)
+{
+       int i;
+       uint32_t mc_bist_cmd_reg, mc_bist_cmd_addr_reg, mc_bist_cmd_len_reg;
+       uint32_t mc_bist_status_rdata_reg, mc_bist_data_pattern_reg;
+
+       mc_bist_cmd_reg = MC_REG(MC_P_BIST_CMD, idx);
+       mc_bist_cmd_addr_reg = MC_REG(MC_P_BIST_CMD_ADDR, idx);
+       mc_bist_cmd_len_reg = MC_REG(MC_P_BIST_CMD_LEN, idx);
+       mc_bist_status_rdata_reg = MC_REG(MC_P_BIST_STATUS_RDATA, idx);
+       mc_bist_data_pattern_reg = MC_REG(MC_P_BIST_DATA_PATTERN, idx);
+
+       if (csio_rd_reg32(hw, mc_bist_cmd_reg) & START_BIST)
+               return -EBUSY;
+       csio_wr_reg32(hw, addr & ~0x3fU, mc_bist_cmd_addr_reg);
+       csio_wr_reg32(hw, 64, mc_bist_cmd_len_reg);
+       csio_wr_reg32(hw, 0xc, mc_bist_data_pattern_reg);
+       csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST |  BIST_CMD_GAP(1),
+                     mc_bist_cmd_reg);
+       i = csio_hw_wait_op_done_val(hw, mc_bist_cmd_reg, START_BIST,
+                                    0, 10, 1, NULL);
+       if (i)
+               return i;
+
+#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i)
+
+       for (i = 15; i >= 0; i--)
+               *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i)));
+       if (ecc)
+               *ecc = csio_rd_reg64(hw, MC_DATA(16));
+#undef MC_DATA
+       return 0;
+}
+
+/*
+ *      csio_t5_edc_read - read from EDC through backdoor accesses
+ *      @hw: the hw module
+ *      @idx: which EDC to access
+ *      @addr: address of first byte requested
+ *      @data: 64 bytes of data containing the requested address
+ *      @ecc: where to store the corresponding 64-bit ECC word
+ *
+ *      Read 64 bytes of data from EDC starting at a 64-byte-aligned address
+ *      that covers the requested address @addr.  If @parity is not %NULL it
+ *      is assigned the 64-bit ECC word for the read data.
+ */
+static int
+csio_t5_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data,
+               uint64_t *ecc)
+{
+       int i;
+       uint32_t edc_bist_cmd_reg, edc_bist_cmd_addr_reg, edc_bist_cmd_len_reg;
+       uint32_t edc_bist_cmd_data_pattern, edc_bist_status_rdata_reg;
+
+/*
+ * These macro are missing in t4_regs.h file.
+ */
+#define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR)
+#define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx)
+
+       edc_bist_cmd_reg = EDC_REG_T5(EDC_H_BIST_CMD, idx);
+       edc_bist_cmd_addr_reg = EDC_REG_T5(EDC_H_BIST_CMD_ADDR, idx);
+       edc_bist_cmd_len_reg = EDC_REG_T5(EDC_H_BIST_CMD_LEN, idx);
+       edc_bist_cmd_data_pattern = EDC_REG_T5(EDC_H_BIST_DATA_PATTERN, idx);
+       edc_bist_status_rdata_reg = EDC_REG_T5(EDC_H_BIST_STATUS_RDATA, idx);
+#undef EDC_REG_T5
+#undef EDC_STRIDE_T5
+
+       if (csio_rd_reg32(hw, edc_bist_cmd_reg) & START_BIST)
+               return -EBUSY;
+       csio_wr_reg32(hw, addr & ~0x3fU, edc_bist_cmd_addr_reg);
+       csio_wr_reg32(hw, 64, edc_bist_cmd_len_reg);
+       csio_wr_reg32(hw, 0xc, edc_bist_cmd_data_pattern);
+       csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST |  BIST_CMD_GAP(1),
+                     edc_bist_cmd_reg);
+       i = csio_hw_wait_op_done_val(hw, edc_bist_cmd_reg, START_BIST,
+                                    0, 10, 1, NULL);
+       if (i)
+               return i;
+
+#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx)
+
+       for (i = 15; i >= 0; i--)
+               *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i)));
+       if (ecc)
+               *ecc = csio_rd_reg64(hw, EDC_DATA(16));
+#undef EDC_DATA
+       return 0;
+}
+
+/*
+ * csio_t5_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window
+ * @hw: the csio_hw
+ * @win: PCI-E memory Window to use
+ * @mtype: memory type: MEM_EDC0, MEM_EDC1, MEM_MC0 (or MEM_MC) or MEM_MC1
+ * @addr: address within indicated memory type
+ * @len: amount of memory to transfer
+ * @buf: host memory buffer
+ * @dir: direction of transfer 1 => read, 0 => write
+ *
+ * Reads/writes an [almost] arbitrary memory region in the firmware: the
+ * firmware memory address, length and host buffer must be aligned on
+ * 32-bit boudaries.  The memory is transferred as a raw byte sequence
+ * from/to the firmware's memory.  If this memory contains data
+ * structures which contain multi-byte integers, it's the callers
+ * responsibility to perform appropriate byte order conversions.
+ */
+static int
+csio_t5_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr,
+               u32 len, uint32_t *buf, int dir)
+{
+       u32 pos, start, offset, memoffset;
+       u32 edc_size, mc_size, win_pf, mem_reg, mem_aperture, mem_base;
+
+       /*
+        * Argument sanity checks ...
+        */
+       if ((addr & 0x3) || (len & 0x3))
+               return -EINVAL;
+
+       /* Offset into the region of memory which is being accessed
+        * MEM_EDC0 = 0
+        * MEM_EDC1 = 1
+        * MEM_MC   = 2 -- T4
+        * MEM_MC0  = 2 -- For T5
+        * MEM_MC1  = 3 -- For T5
+        */
+       edc_size  = EDRAM_SIZE_GET(csio_rd_reg32(hw, MA_EDRAM0_BAR));
+       if (mtype != MEM_MC1)
+               memoffset = (mtype * (edc_size * 1024 * 1024));
+       else {
+               mc_size = EXT_MEM_SIZE_GET(csio_rd_reg32(hw,
+                                                        MA_EXT_MEMORY_BAR));
+               memoffset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024;
+       }
+
+       /* Determine the PCIE_MEM_ACCESS_OFFSET */
+       addr = addr + memoffset;
+
+       /*
+        * Each PCI-E Memory Window is programmed with a window size -- or
+        * "aperture" -- which controls the granularity of its mapping onto
+        * adapter memory.  We need to grab that aperture in order to know
+        * how to use the specified window.  The window is also programmed
+        * with the base address of the Memory Window in BAR0's address
+        * space.  For T4 this is an absolute PCI-E Bus Address.  For T5
+        * the address is relative to BAR0.
+        */
+       mem_reg = csio_rd_reg32(hw,
+                       PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win));
+       mem_aperture = 1 << (WINDOW(mem_reg) + 10);
+       mem_base = GET_PCIEOFST(mem_reg) << 10;
+
+       start = addr & ~(mem_aperture-1);
+       offset = addr - start;
+       win_pf = V_PFNUM(hw->pfn);
+
+       csio_dbg(hw, "csio_t5_memory_rw: mem_reg: 0x%x, mem_aperture: 0x%x\n",
+                mem_reg, mem_aperture);
+       csio_dbg(hw, "csio_t5_memory_rw: mem_base: 0x%x, mem_offset: 0x%x\n",
+                mem_base, memoffset);
+       csio_dbg(hw, "csio_t5_memory_rw: start:0x%x, offset:0x%x, win_pf:%d\n",
+                start, offset, win_pf);
+       csio_dbg(hw, "csio_t5_memory_rw: mtype: %d, addr: 0x%x, len: %d\n",
+                mtype, addr, len);
+
+       for (pos = start; len > 0; pos += mem_aperture, offset = 0) {
+               /*
+                * Move PCI-E Memory Window to our current transfer
+                * position.  Read it back to ensure that changes propagate
+                * before we attempt to use the new value.
+                */
+               csio_wr_reg32(hw, pos | win_pf,
+                       PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win));
+               csio_rd_reg32(hw,
+                       PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win));
+
+               while (offset < mem_aperture && len > 0) {
+                       if (dir)
+                               *buf++ = csio_rd_reg32(hw, mem_base + offset);
+                       else
+                               csio_wr_reg32(hw, *buf++, mem_base + offset);
+
+                       offset += sizeof(__be32);
+                       len -= sizeof(__be32);
+               }
+       }
+       return 0;
+}
+
+/*
+ * csio_t5_dfs_create_ext_mem - setup debugfs for MC0 or MC1 to read the values
+ * @hw: the csio_hw
+ *
+ * This function creates files in the debugfs with external memory region
+ * MC0 & MC1.
+ */
+static void
+csio_t5_dfs_create_ext_mem(struct csio_hw *hw)
+{
+       u32 size;
+       int i = csio_rd_reg32(hw, MA_TARGET_MEM_ENABLE);
+       if (i & EXT_MEM_ENABLE) {
+               size = csio_rd_reg32(hw, MA_EXT_MEMORY_BAR);
+               csio_add_debugfs_mem(hw, "mc0", MEM_MC0,
+                                    EXT_MEM_SIZE_GET(size));
+       }
+       if (i & EXT_MEM1_ENABLE) {
+               size = csio_rd_reg32(hw, MA_EXT_MEMORY1_BAR);
+               csio_add_debugfs_mem(hw, "mc1", MEM_MC1,
+                                    EXT_MEM_SIZE_GET(size));
+       }
+}
+
+/* T5 adapter specific function */
+struct csio_hw_chip_ops t5_ops = {
+       .chip_set_mem_win               = csio_t5_set_mem_win,
+       .chip_pcie_intr_handler         = csio_t5_pcie_intr_handler,
+       .chip_flash_cfg_addr            = csio_t5_flash_cfg_addr,
+       .chip_mc_read                   = csio_t5_mc_read,
+       .chip_edc_read                  = csio_t5_edc_read,
+       .chip_memory_rw                 = csio_t5_memory_rw,
+       .chip_dfs_create_ext_mem        = csio_t5_dfs_create_ext_mem,
+};
index 0604b5ff36381a432cb1c45beae670a1a8de7d16..00346fe939d554b5d089980718cd2fac1492ba41 100644 (file)
@@ -81,9 +81,11 @@ csio_mem_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
                __be32 data[16];
 
                if (mem == MEM_MC)
-                       ret = csio_hw_mc_read(hw, pos, data, NULL);
+                       ret = hw->chip_ops->chip_mc_read(hw, 0, pos,
+                                                        data, NULL);
                else
-                       ret = csio_hw_edc_read(hw, mem, pos, data, NULL);
+                       ret = hw->chip_ops->chip_edc_read(hw, mem, pos,
+                                                         data, NULL);
                if (ret)
                        return ret;
 
@@ -108,7 +110,7 @@ static const struct file_operations csio_mem_debugfs_fops = {
        .llseek  = default_llseek,
 };
 
-static void csio_add_debugfs_mem(struct csio_hw *hw, const char *name,
+void csio_add_debugfs_mem(struct csio_hw *hw, const char *name,
                                 unsigned int idx, unsigned int size_mb)
 {
        struct dentry *de;
@@ -131,9 +133,8 @@ static int csio_setup_debugfs(struct csio_hw *hw)
                csio_add_debugfs_mem(hw, "edc0", MEM_EDC0, 5);
        if (i & EDRAM1_ENABLE)
                csio_add_debugfs_mem(hw, "edc1", MEM_EDC1, 5);
-       if (i & EXT_MEM_ENABLE)
-               csio_add_debugfs_mem(hw, "mc", MEM_MC,
-                     EXT_MEM_SIZE_GET(csio_rd_reg32(hw, MA_EXT_MEMORY_BAR)));
+
+       hw->chip_ops->chip_dfs_create_ext_mem(hw);
        return 0;
 }
 
@@ -1169,7 +1170,7 @@ static struct pci_error_handlers csio_err_handler = {
 };
 
 static DEFINE_PCI_DEVICE_TABLE(csio_pci_tbl) = {
-       CSIO_DEVICE(CSIO_DEVID_T440DBG_FCOE, 0),        /* T440DBG FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T440DBG_FCOE, 0),        /* T4 DEBUG FCOE */
        CSIO_DEVICE(CSIO_DEVID_T420CR_FCOE, 0),         /* T420CR FCOE */
        CSIO_DEVICE(CSIO_DEVID_T422CR_FCOE, 0),         /* T422CR FCOE */
        CSIO_DEVICE(CSIO_DEVID_T440CR_FCOE, 0),         /* T440CR FCOE */
@@ -1184,8 +1185,34 @@ static DEFINE_PCI_DEVICE_TABLE(csio_pci_tbl) = {
        CSIO_DEVICE(CSIO_DEVID_B404_FCOE, 0),           /* B404 FCOE */
        CSIO_DEVICE(CSIO_DEVID_T480CR_FCOE, 0),         /* T480 CR FCOE */
        CSIO_DEVICE(CSIO_DEVID_T440LPCR_FCOE, 0),       /* T440 LP-CR FCOE */
-       CSIO_DEVICE(CSIO_DEVID_PE10K, 0),               /* PE10K FCOE */
-       CSIO_DEVICE(CSIO_DEVID_PE10K_PF1, 0),   /* PE10K FCOE on PF1 */
+       CSIO_DEVICE(CSIO_DEVID_AMSTERDAM_T4_FCOE, 0),   /* AMSTERDAM T4 FCOE */
+       CSIO_DEVICE(CSIO_DEVID_HUAWEI_T480_FCOE, 0),    /* HUAWEI T480 FCOE */
+       CSIO_DEVICE(CSIO_DEVID_HUAWEI_T440_FCOE, 0),    /* HUAWEI T440 FCOE */
+       CSIO_DEVICE(CSIO_DEVID_HUAWEI_STG310_FCOE, 0),  /* HUAWEI STG FCOE */
+       CSIO_DEVICE(CSIO_DEVID_ACROMAG_XMC_XAUI, 0),    /* ACROMAG XAUI FCOE */
+       CSIO_DEVICE(CSIO_DEVID_QUANTA_MEZZ_SFP_FCOE, 0),/* QUANTA MEZZ FCOE */
+       CSIO_DEVICE(CSIO_DEVID_HUAWEI_10GT_FCOE, 0),    /* HUAWEI 10GT FCOE */
+       CSIO_DEVICE(CSIO_DEVID_HUAWEI_T440_TOE_FCOE, 0),/* HUAWEI T4 TOE FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T580DBG_FCOE, 0),        /* T5 DEBUG FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T520CR_FCOE, 0),         /* T520CR FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T522CR_FCOE, 0),         /* T522CR FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T540CR_FCOE, 0),         /* T540CR FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T520BCH_FCOE, 0),        /* T520BCH FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T540BCH_FCOE, 0),        /* T540BCH FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T540CH_FCOE, 0),         /* T540CH FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T520SO_FCOE, 0),         /* T520SO FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T520CX_FCOE, 0),         /* T520CX FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T520BT_FCOE, 0),         /* T520BT FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T504BT_FCOE, 0),         /* T504BT FCOE */
+       CSIO_DEVICE(CSIO_DEVID_B520_FCOE, 0),           /* B520 FCOE */
+       CSIO_DEVICE(CSIO_DEVID_B504_FCOE, 0),           /* B504 FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T580CR2_FCOE, 0),        /* T580 CR FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T540LPCR_FCOE, 0),       /* T540 LP-CR FCOE */
+       CSIO_DEVICE(CSIO_DEVID_AMSTERDAM_T5_FCOE, 0),   /* AMSTERDAM T5 FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T580LPCR_FCOE, 0),       /* T580 LP-CR FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T520LLCR_FCOE, 0),       /* T520 LL-CR FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T560CR_FCOE, 0),         /* T560 CR FCOE */
+       CSIO_DEVICE(CSIO_DEVID_T580CR_FCOE, 0),         /* T580 CR FCOE */
        { 0, 0, 0, 0, 0, 0, 0 }
 };
 
@@ -1259,4 +1286,5 @@ MODULE_DESCRIPTION(CSIO_DRV_DESC);
 MODULE_LICENSE(CSIO_DRV_LICENSE);
 MODULE_DEVICE_TABLE(pci, csio_pci_tbl);
 MODULE_VERSION(CSIO_DRV_VERSION);
-MODULE_FIRMWARE(CSIO_FW_FNAME);
+MODULE_FIRMWARE(FW_FNAME_T4);
+MODULE_FIRMWARE(FW_FNAME_T5);
index 0838fd7ec9c7a475912dcf43c0edf470a6800624..5cc5d317a442ec2feb4b4fd3bfc59b7d3ddc4c6f 100644 (file)
 #define CSIO_DRV_DESC                  "Chelsio FCoE driver"
 #define CSIO_DRV_VERSION               "1.0.0"
 
-#define CSIO_DEVICE(devid, idx)                                        \
-{ PCI_VENDOR_ID_CHELSIO, (devid), PCI_ANY_ID, PCI_ANY_ID, 0, 0, (idx) }
-
-#define CSIO_IS_T4_FPGA(_dev)          (((_dev) == CSIO_DEVID_PE10K) ||\
-                                        ((_dev) == CSIO_DEVID_PE10K_PF1))
-
-/* FCoE device IDs */
-#define CSIO_DEVID_PE10K               0xA000
-#define CSIO_DEVID_PE10K_PF1           0xA001
-#define CSIO_DEVID_T440DBG_FCOE                0x4600
-#define CSIO_DEVID_T420CR_FCOE         0x4601
-#define CSIO_DEVID_T422CR_FCOE         0x4602
-#define CSIO_DEVID_T440CR_FCOE         0x4603
-#define CSIO_DEVID_T420BCH_FCOE                0x4604
-#define CSIO_DEVID_T440BCH_FCOE                0x4605
-#define CSIO_DEVID_T440CH_FCOE         0x4606
-#define CSIO_DEVID_T420SO_FCOE         0x4607
-#define CSIO_DEVID_T420CX_FCOE         0x4608
-#define CSIO_DEVID_T420BT_FCOE         0x4609
-#define CSIO_DEVID_T404BT_FCOE         0x460A
-#define CSIO_DEVID_B420_FCOE           0x460B
-#define CSIO_DEVID_B404_FCOE           0x460C
-#define CSIO_DEVID_T480CR_FCOE         0x460D
-#define CSIO_DEVID_T440LPCR_FCOE       0x460E
-
 extern struct fc_function_template csio_fc_transport_funcs;
 extern struct fc_function_template csio_fc_transport_vport_funcs;
 
@@ -100,6 +75,10 @@ struct csio_lnode *csio_shost_init(struct csio_hw *, struct device *, bool,
 void csio_shost_exit(struct csio_lnode *);
 void csio_lnodes_exit(struct csio_hw *, bool);
 
+/* DebugFS helper routines */
+void csio_add_debugfs_mem(struct csio_hw *, const char *,
+               unsigned int, unsigned int);
+
 static inline struct Scsi_Host *
 csio_ln_to_shost(struct csio_lnode *ln)
 {
index 8d84988ab06d9645543902c9da68da91efe28573..0f9c04175b11d4894f34157bf8177d6956c2f9c7 100644 (file)
@@ -114,7 +114,7 @@ struct csio_lnode_stats {
        uint32_t        n_rnode_match;  /* matched rnode */
        uint32_t        n_dev_loss_tmo; /* Device loss timeout */
        uint32_t        n_fdmi_err;     /* fdmi err */
-       uint32_t        n_evt_fw[RSCN_DEV_LOST];        /* fw events */
+       uint32_t        n_evt_fw[PROTO_ERR_IMPL_LOGO];  /* fw events */
        enum csio_ln_ev n_evt_sm[CSIO_LNE_MAX_EVENT];   /* State m/c events */
        uint32_t        n_rnode_alloc;  /* rnode allocated */
        uint32_t        n_rnode_free;   /* rnode freed */
index 51c6a388de2b732db19144aea701a8a0af5e21ca..e9c3b045f587509ffc92d01fd91d7c455a7e47a7 100644 (file)
@@ -302,7 +302,7 @@ csio_confirm_rnode(struct csio_lnode *ln, uint32_t rdev_flowid,
 {
        uint8_t rport_type;
        struct csio_rnode *rn, *match_rn;
-       uint32_t vnp_flowid;
+       uint32_t vnp_flowid = 0;
        __be32 *port_id;
 
        port_id = (__be32 *)&rdevp->r_id[0];
@@ -350,6 +350,14 @@ csio_confirm_rnode(struct csio_lnode *ln, uint32_t rdev_flowid,
                         * Else, go ahead and alloc a new rnode.
                         */
                        if (!memcmp(csio_rn_wwpn(match_rn), rdevp->wwpn, 8)) {
+                               if (rn == match_rn)
+                                       goto found_rnode;
+                               csio_ln_dbg(ln,
+                                           "nport_id:x%x and wwpn:%llx"
+                                           " match for ssni:x%x\n",
+                                           rn->nport_id,
+                                           wwn_to_u64(rdevp->wwpn),
+                                           rdev_flowid);
                                if (csio_is_rnode_ready(rn)) {
                                        csio_ln_warn(ln,
                                                     "rnode is already"
index a3b434c801da7b0800d35b6a82f06e4c930a880e..65940096a80d8426eb438392bf544ecee1d5a1e1 100644 (file)
@@ -63,7 +63,7 @@ struct csio_rnode_stats {
        uint32_t        n_err_nomem;    /* error nomem */
        uint32_t        n_evt_unexp;    /* unexpected event */
        uint32_t        n_evt_drop;     /* unexpected event */
-       uint32_t        n_evt_fw[RSCN_DEV_LOST];        /* fw events */
+       uint32_t        n_evt_fw[PROTO_ERR_IMPL_LOGO];  /* fw events */
        enum csio_rn_ev n_evt_sm[CSIO_RNFE_MAX_EVENT];  /* State m/c events */
        uint32_t        n_lun_rst;      /* Number of resets of
                                         * of LUNs under this
index c32df1bdaa97a20cdf67a41cb485e7502dc7ba44..4255ce264abf9085040de66d5075b75e9125eb9c 100644 (file)
@@ -85,8 +85,8 @@ csio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq)
         */
        if (flq->inc_idx >= 8) {
                csio_wr_reg32(hw, DBPRIO(1) | QID(flq->un.fl.flid) |
-                             PIDX(flq->inc_idx / 8),
-                             MYPF_REG(SGE_PF_KDOORBELL));
+                                 CSIO_HW_PIDX(hw, flq->inc_idx / 8),
+                                 MYPF_REG(SGE_PF_KDOORBELL));
                flq->inc_idx &= 7;
        }
 }
@@ -989,7 +989,8 @@ csio_wr_issue(struct csio_hw *hw, int qidx, bool prio)
        wmb();
        /* Ring SGE Doorbell writing q->pidx into it */
        csio_wr_reg32(hw, DBPRIO(prio) | QID(q->un.eq.physeqid) |
-                     PIDX(q->inc_idx), MYPF_REG(SGE_PF_KDOORBELL));
+                         CSIO_HW_PIDX(hw, q->inc_idx),
+                         MYPF_REG(SGE_PF_KDOORBELL));
        q->inc_idx = 0;
 
        return 0;
@@ -1331,20 +1332,30 @@ csio_wr_fixup_host_params(struct csio_hw *hw)
 
        /* FL BUFFER SIZE#0 is Page size i,e already aligned to cache line */
        csio_wr_reg32(hw, PAGE_SIZE, SGE_FL_BUFFER_SIZE0);
-       csio_wr_reg32(hw,
-                     (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2) +
-                     sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1),
-                     SGE_FL_BUFFER_SIZE2);
-       csio_wr_reg32(hw,
-                     (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3) +
-                     sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1),
-                     SGE_FL_BUFFER_SIZE3);
+
+       /*
+        * If using hard params, the following will get set correctly
+        * in csio_wr_set_sge().
+        */
+       if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS) {
+               csio_wr_reg32(hw,
+                       (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2) +
+                       sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1),
+                       SGE_FL_BUFFER_SIZE2);
+               csio_wr_reg32(hw,
+                       (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3) +
+                       sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1),
+                       SGE_FL_BUFFER_SIZE3);
+       }
 
        csio_wr_reg32(hw, HPZ0(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ);
 
        /* default value of rx_dma_offset of the NIC driver */
        csio_set_reg_field(hw, SGE_CONTROL, PKTSHIFT_MASK,
                           PKTSHIFT(CSIO_SGE_RX_DMA_OFFSET));
+
+       csio_hw_tp_wr_bits_indirect(hw, TP_INGRESS_CONFIG,
+                                   CSUM_HAS_PSEUDO_HDR, 0);
 }
 
 static void
@@ -1460,18 +1471,21 @@ csio_wr_set_sge(struct csio_hw *hw)
         * and generate an interrupt when this occurs so we can recover.
         */
        csio_set_reg_field(hw, SGE_DBFIFO_STATUS,
-                          HP_INT_THRESH(HP_INT_THRESH_MASK) |
-                          LP_INT_THRESH(LP_INT_THRESH_MASK),
-                          HP_INT_THRESH(CSIO_SGE_DBFIFO_INT_THRESH) |
-                          LP_INT_THRESH(CSIO_SGE_DBFIFO_INT_THRESH));
+                  HP_INT_THRESH(HP_INT_THRESH_MASK) |
+                  CSIO_HW_LP_INT_THRESH(hw, CSIO_HW_M_LP_INT_THRESH(hw)),
+                  HP_INT_THRESH(CSIO_SGE_DBFIFO_INT_THRESH) |
+                  CSIO_HW_LP_INT_THRESH(hw, CSIO_SGE_DBFIFO_INT_THRESH));
+
        csio_set_reg_field(hw, SGE_DOORBELL_CONTROL, ENABLE_DROP,
                           ENABLE_DROP);
 
        /* SGE_FL_BUFFER_SIZE0 is set up by csio_wr_fixup_host_params(). */
 
        CSIO_SET_FLBUF_SIZE(hw, 1, CSIO_SGE_FLBUF_SIZE1);
-       CSIO_SET_FLBUF_SIZE(hw, 2, CSIO_SGE_FLBUF_SIZE2);
-       CSIO_SET_FLBUF_SIZE(hw, 3, CSIO_SGE_FLBUF_SIZE3);
+       csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE2 + sge->csio_fl_align - 1)
+                     & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE2);
+       csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE3 + sge->csio_fl_align - 1)
+                     & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE3);
        CSIO_SET_FLBUF_SIZE(hw, 4, CSIO_SGE_FLBUF_SIZE4);
        CSIO_SET_FLBUF_SIZE(hw, 5, CSIO_SGE_FLBUF_SIZE5);
        CSIO_SET_FLBUF_SIZE(hw, 6, CSIO_SGE_FLBUF_SIZE6);
@@ -1522,22 +1536,24 @@ void
 csio_wr_sge_init(struct csio_hw *hw)
 {
        /*
-        * If we are master:
+        * If we are master and chip is not initialized:
         *    - If we plan to use the config file, we need to fixup some
         *      host specific registers, and read the rest of the SGE
         *      configuration.
         *    - If we dont plan to use the config file, we need to initialize
         *      SGE entirely, including fixing the host specific registers.
+        * If we are master and chip is initialized, just read and work off of
+        *      the already initialized SGE values.
         * If we arent the master, we are only allowed to read and work off of
         *      the already initialized SGE values.
         *
         * Therefore, before calling this function, we assume that the master-
-        * ship of the card, and whether to use config file or not, have
-        * already been decided. In other words, CSIO_HWF_USING_SOFT_PARAMS and
-        * CSIO_HWF_MASTER should be set/unset.
+        * ship of the card, state and whether to use config file or not, have
+        * already been decided.
         */
        if (csio_is_hw_master(hw)) {
-               csio_wr_fixup_host_params(hw);
+               if (hw->fw_state != CSIO_DEV_STATE_INIT)
+                       csio_wr_fixup_host_params(hw);
 
                if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS)
                        csio_wr_get_sge(hw);
index 9bfdc9a3f897a2e16dc9e0a34d5d6e47969c0d1c..292b24f9bf935f119ab12dbbca96d5a00d16783d 100644 (file)
@@ -1655,7 +1655,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        skb->priority = fcoe->priority;
 
        if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN &&
-           fcoe->realdev->features & NETIF_F_HW_VLAN_TX) {
+           fcoe->realdev->features & NETIF_F_HW_VLAN_CTAG_TX) {
                skb->vlan_tci = VLAN_TAG_PRESENT |
                                vlan_dev_vlan_id(fcoe->netdev);
                skb->dev = fcoe->realdev;
index 65123a21b97ec17ffb695a3e09dd06556bc5b0a8..fe30ea94ffe67ef4e5d355fdc9cdcb71eee9e0d7 100644 (file)
@@ -50,7 +50,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
        u32 rlen;
        int err, tport;
 
-       while (skb->len >= NLMSG_SPACE(0)) {
+       while (skb->len >= NLMSG_HDRLEN) {
                err = 0;
 
                nlh = nlmsg_hdr(skb);
@@ -70,7 +70,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
                        goto next_msg;
                }
 
-               hdr = NLMSG_DATA(nlh);
+               hdr = nlmsg_data(nlh);
                if ((hdr->version != SCSI_NL_VERSION) ||
                    (hdr->magic != SCSI_NL_MAGIC)) {
                        err = -EPROTOTYPE;
index e894ca7b54c0c895cd92cf15134f0b6196d71c43..e106c276aa00275ce1564ed5cccba739e62d6804 100644 (file)
@@ -35,7 +35,6 @@
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
 #include <scsi/scsi_cmnd.h>
-#include <linux/netlink.h>
 #include <net/netlink.h>
 #include <scsi/scsi_netlink_fc.h>
 #include <scsi/scsi_bsg_fc.h>
@@ -534,7 +533,7 @@ fc_host_post_event(struct Scsi_Host *shost, u32 event_number,
        struct nlmsghdr *nlh;
        struct fc_nl_event *event;
        const char *name;
-       u32 len, skblen;
+       u32 len;
        int err;
 
        if (!scsi_nl_sock) {
@@ -543,21 +542,19 @@ fc_host_post_event(struct Scsi_Host *shost, u32 event_number,
        }
 
        len = FC_NL_MSGALIGN(sizeof(*event));
-       skblen = NLMSG_SPACE(len);
 
-       skb = alloc_skb(skblen, GFP_KERNEL);
+       skb = nlmsg_new(len, GFP_KERNEL);
        if (!skb) {
                err = -ENOBUFS;
                goto send_fail;
        }
 
-       nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
-                               skblen - sizeof(*nlh), 0);
+       nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, len, 0);
        if (!nlh) {
                err = -ENOBUFS;
                goto send_fail_skb;
        }
-       event = NLMSG_DATA(nlh);
+       event = nlmsg_data(nlh);
 
        INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC,
                                FC_NL_ASYNC_EVENT, len);
@@ -604,7 +601,7 @@ fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
        struct sk_buff *skb;
        struct nlmsghdr *nlh;
        struct fc_nl_event *event;
-       u32 len, skblen;
+       u32 len;
        int err;
 
        if (!scsi_nl_sock) {
@@ -613,21 +610,19 @@ fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
        }
 
        len = FC_NL_MSGALIGN(sizeof(*event) + data_len);
-       skblen = NLMSG_SPACE(len);
 
-       skb = alloc_skb(skblen, GFP_KERNEL);
+       skb = nlmsg_new(len, GFP_KERNEL);
        if (!skb) {
                err = -ENOBUFS;
                goto send_vendor_fail;
        }
 
-       nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
-                               skblen - sizeof(*nlh), 0);
+       nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, len, 0);
        if (!nlh) {
                err = -ENOBUFS;
                goto send_vendor_fail_skb;
        }
-       event = NLMSG_DATA(nlh);
+       event = nlmsg_data(nlh);
 
        INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC,
                                FC_NL_ASYNC_EVENT, len);
index 0a74b975efdf798a8149a72c767112e35e0c9531..2e3816530bba72be76b1b155cd5d64991c64d036 100644 (file)
@@ -1344,8 +1344,8 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
        struct iscsi_uevent *ev;
        char *pdu;
        struct iscsi_internal *priv;
-       int len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct iscsi_hdr) +
-                             data_size);
+       int len = nlmsg_total_size(sizeof(*ev) + sizeof(struct iscsi_hdr) +
+                                  data_size);
 
        priv = iscsi_if_transport_lookup(conn->transport);
        if (!priv)
@@ -1360,7 +1360,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
        }
 
        nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
-       ev = NLMSG_DATA(nlh);
+       ev = nlmsg_data(nlh);
        memset(ev, 0, sizeof(*ev));
        ev->transport_handle = iscsi_handle(conn->transport);
        ev->type = ISCSI_KEVENT_RECV_PDU;
@@ -1381,7 +1381,7 @@ int iscsi_offload_mesg(struct Scsi_Host *shost,
        struct nlmsghdr *nlh;
        struct sk_buff *skb;
        struct iscsi_uevent *ev;
-       int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+       int len = nlmsg_total_size(sizeof(*ev) + data_size);
 
        skb = alloc_skb(len, GFP_ATOMIC);
        if (!skb) {
@@ -1390,7 +1390,7 @@ int iscsi_offload_mesg(struct Scsi_Host *shost,
        }
 
        nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
-       ev = NLMSG_DATA(nlh);
+       ev = nlmsg_data(nlh);
        memset(ev, 0, sizeof(*ev));
        ev->type = type;
        ev->transport_handle = iscsi_handle(transport);
@@ -1415,7 +1415,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
        struct sk_buff  *skb;
        struct iscsi_uevent *ev;
        struct iscsi_internal *priv;
-       int len = NLMSG_SPACE(sizeof(*ev));
+       int len = nlmsg_total_size(sizeof(*ev));
 
        priv = iscsi_if_transport_lookup(conn->transport);
        if (!priv)
@@ -1429,7 +1429,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
        }
 
        nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
-       ev = NLMSG_DATA(nlh);
+       ev = nlmsg_data(nlh);
        ev->transport_handle = iscsi_handle(conn->transport);
        ev->type = ISCSI_KEVENT_CONN_ERROR;
        ev->r.connerror.error = error;
@@ -1450,7 +1450,7 @@ void iscsi_conn_login_event(struct iscsi_cls_conn *conn,
        struct sk_buff  *skb;
        struct iscsi_uevent *ev;
        struct iscsi_internal *priv;
-       int len = NLMSG_SPACE(sizeof(*ev));
+       int len = nlmsg_total_size(sizeof(*ev));
 
        priv = iscsi_if_transport_lookup(conn->transport);
        if (!priv)
@@ -1464,7 +1464,7 @@ void iscsi_conn_login_event(struct iscsi_cls_conn *conn,
        }
 
        nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
-       ev = NLMSG_DATA(nlh);
+       ev = nlmsg_data(nlh);
        ev->transport_handle = iscsi_handle(conn->transport);
        ev->type = ISCSI_KEVENT_CONN_LOGIN_STATE;
        ev->r.conn_login.state = state;
@@ -1484,7 +1484,7 @@ void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
        struct nlmsghdr *nlh;
        struct sk_buff *skb;
        struct iscsi_uevent *ev;
-       int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+       int len = nlmsg_total_size(sizeof(*ev) + data_size);
 
        skb = alloc_skb(len, GFP_NOIO);
        if (!skb) {
@@ -1494,7 +1494,7 @@ void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
        }
 
        nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
-       ev = NLMSG_DATA(nlh);
+       ev = nlmsg_data(nlh);
        ev->transport_handle = iscsi_handle(transport);
        ev->type = ISCSI_KEVENT_HOST_EVENT;
        ev->r.host_event.host_no = host_no;
@@ -1515,7 +1515,7 @@ void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
        struct nlmsghdr *nlh;
        struct sk_buff *skb;
        struct iscsi_uevent *ev;
-       int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+       int len = nlmsg_total_size(sizeof(*ev) + data_size);
 
        skb = alloc_skb(len, GFP_NOIO);
        if (!skb) {
@@ -1524,7 +1524,7 @@ void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
        }
 
        nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
-       ev = NLMSG_DATA(nlh);
+       ev = nlmsg_data(nlh);
        ev->transport_handle = iscsi_handle(transport);
        ev->type = ISCSI_KEVENT_PING_COMP;
        ev->r.ping_comp.host_no = host_no;
@@ -1543,7 +1543,7 @@ iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
 {
        struct sk_buff  *skb;
        struct nlmsghdr *nlh;
-       int len = NLMSG_SPACE(size);
+       int len = nlmsg_total_size(size);
        int flags = multi ? NLM_F_MULTI : 0;
        int t = done ? NLMSG_DONE : type;
 
@@ -1555,24 +1555,24 @@ iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
 
        nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
        nlh->nlmsg_flags = flags;
-       memcpy(NLMSG_DATA(nlh), payload, size);
+       memcpy(nlmsg_data(nlh), payload, size);
        return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
 }
 
 static int
 iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
 {
-       struct iscsi_uevent *ev = NLMSG_DATA(nlh);
+       struct iscsi_uevent *ev = nlmsg_data(nlh);
        struct iscsi_stats *stats;
        struct sk_buff *skbstat;
        struct iscsi_cls_conn *conn;
        struct nlmsghdr *nlhstat;
        struct iscsi_uevent *evstat;
        struct iscsi_internal *priv;
-       int len = NLMSG_SPACE(sizeof(*ev) +
-                             sizeof(struct iscsi_stats) +
-                             sizeof(struct iscsi_stats_custom) *
-                             ISCSI_STATS_CUSTOM_MAX);
+       int len = nlmsg_total_size(sizeof(*ev) +
+                                  sizeof(struct iscsi_stats) +
+                                  sizeof(struct iscsi_stats_custom) *
+                                  ISCSI_STATS_CUSTOM_MAX);
        int err = 0;
 
        priv = iscsi_if_transport_lookup(transport);
@@ -1595,7 +1595,7 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
 
                nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
                                      (len - sizeof(*nlhstat)), 0);
-               evstat = NLMSG_DATA(nlhstat);
+               evstat = nlmsg_data(nlhstat);
                memset(evstat, 0, sizeof(*evstat));
                evstat->transport_handle = iscsi_handle(conn->transport);
                evstat->type = nlh->nlmsg_type;
@@ -1608,12 +1608,12 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
                memset(stats, 0, sizeof(*stats));
 
                transport->get_stats(conn, stats);
-               actual_size = NLMSG_SPACE(sizeof(struct iscsi_uevent) +
-                                         sizeof(struct iscsi_stats) +
-                                         sizeof(struct iscsi_stats_custom) *
-                                         stats->custom_length);
+               actual_size = nlmsg_total_size(sizeof(struct iscsi_uevent) +
+                                              sizeof(struct iscsi_stats) +
+                                              sizeof(struct iscsi_stats_custom) *
+                                              stats->custom_length);
                actual_size -= sizeof(*nlhstat);
-               actual_size = NLMSG_LENGTH(actual_size);
+               actual_size = nlmsg_msg_size(actual_size);
                skb_trim(skbstat, NLMSG_ALIGN(actual_size));
                nlhstat->nlmsg_len = actual_size;
 
@@ -1637,7 +1637,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
        struct iscsi_uevent *ev;
        struct sk_buff  *skb;
        struct nlmsghdr *nlh;
-       int rc, len = NLMSG_SPACE(sizeof(*ev));
+       int rc, len = nlmsg_total_size(sizeof(*ev));
 
        priv = iscsi_if_transport_lookup(session->transport);
        if (!priv)
@@ -1653,7 +1653,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
        }
 
        nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
-       ev = NLMSG_DATA(nlh);
+       ev = nlmsg_data(nlh);
        ev->transport_handle = iscsi_handle(session->transport);
 
        ev->type = event;
@@ -2005,7 +2005,7 @@ iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev)
 static int
 iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh)
 {
-       struct iscsi_uevent *ev = NLMSG_DATA(nlh);
+       struct iscsi_uevent *ev = nlmsg_data(nlh);
        struct Scsi_Host *shost = NULL;
        struct iscsi_chap_rec *chap_rec;
        struct iscsi_internal *priv;
@@ -2024,7 +2024,7 @@ iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh)
                return -EINVAL;
 
        chap_buf_size = (ev->u.get_chap.num_entries * sizeof(*chap_rec));
-       len = NLMSG_SPACE(sizeof(*ev) + chap_buf_size);
+       len = nlmsg_total_size(sizeof(*ev) + chap_buf_size);
 
        shost = scsi_host_lookup(ev->u.get_chap.host_no);
        if (!shost) {
@@ -2045,7 +2045,7 @@ iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh)
 
                nlhchap = __nlmsg_put(skbchap, 0, 0, 0,
                                      (len - sizeof(*nlhchap)), 0);
-               evchap = NLMSG_DATA(nlhchap);
+               evchap = nlmsg_data(nlhchap);
                memset(evchap, 0, sizeof(*evchap));
                evchap->transport_handle = iscsi_handle(transport);
                evchap->type = nlh->nlmsg_type;
@@ -2058,7 +2058,7 @@ iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh)
                err = transport->get_chap(shost, ev->u.get_chap.chap_tbl_idx,
                                    &evchap->u.get_chap.num_entries, buf);
 
-               actual_size = NLMSG_SPACE(sizeof(*ev) + chap_buf_size);
+               actual_size = nlmsg_total_size(sizeof(*ev) + chap_buf_size);
                skb_trim(skbchap, NLMSG_ALIGN(actual_size));
                nlhchap->nlmsg_len = actual_size;
 
@@ -2096,7 +2096,7 @@ static int
 iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
 {
        int err = 0;
-       struct iscsi_uevent *ev = NLMSG_DATA(nlh);
+       struct iscsi_uevent *ev = nlmsg_data(nlh);
        struct iscsi_transport *transport = NULL;
        struct iscsi_internal *priv;
        struct iscsi_cls_session *session;
@@ -2263,7 +2263,7 @@ static void
 iscsi_if_rx(struct sk_buff *skb)
 {
        mutex_lock(&rx_queue_mutex);
-       while (skb->len >= NLMSG_SPACE(0)) {
+       while (skb->len >= NLMSG_HDRLEN) {
                int err;
                uint32_t rlen;
                struct nlmsghdr *nlh;
@@ -2276,7 +2276,7 @@ iscsi_if_rx(struct sk_buff *skb)
                        break;
                }
 
-               ev = NLMSG_DATA(nlh);
+               ev = nlmsg_data(nlh);
                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
                if (rlen > skb->len)
                        rlen = skb->len;
index 71098a7b5fed2123319e5351a5a1157c5e2fc9a1..7cb7d2c8fd8664d567a52374842f52d65a790309 100644 (file)
@@ -354,7 +354,7 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
 
        if (cc->dev->id.revision >= 11)
                cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
-       ssb_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status);
+       ssb_dbg("chipcommon status is 0x%x\n", cc->status);
 
        if (cc->dev->id.revision >= 20) {
                chipco_write32(cc, SSB_CHIPCO_GPIOPULLUP, 0);
index 7b0bce9367626ad987eb900528a7b329a2f32261..23c5dbfea115acdf9a2272c365fcc0ecbf89d14c 100644 (file)
@@ -110,8 +110,8 @@ static void ssb_pmu0_pllinit_r0(struct ssb_chipcommon *cc,
                return;
        }
 
-       ssb_printk(KERN_INFO PFX "Programming PLL to %u.%03u MHz\n",
-                  (crystalfreq / 1000), (crystalfreq % 1000));
+       ssb_info("Programming PLL to %u.%03u MHz\n",
+                crystalfreq / 1000, crystalfreq % 1000);
 
        /* First turn the PLL off. */
        switch (bus->chip_id) {
@@ -138,7 +138,7 @@ static void ssb_pmu0_pllinit_r0(struct ssb_chipcommon *cc,
        }
        tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
        if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)
-               ssb_printk(KERN_EMERG PFX "Failed to turn the PLL off!\n");
+               ssb_emerg("Failed to turn the PLL off!\n");
 
        /* Set PDIV in PLL control 0. */
        pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL0);
@@ -249,8 +249,8 @@ static void ssb_pmu1_pllinit_r0(struct ssb_chipcommon *cc,
                return;
        }
 
-       ssb_printk(KERN_INFO PFX "Programming PLL to %u.%03u MHz\n",
-                  (crystalfreq / 1000), (crystalfreq % 1000));
+       ssb_info("Programming PLL to %u.%03u MHz\n",
+                crystalfreq / 1000, crystalfreq % 1000);
 
        /* First turn the PLL off. */
        switch (bus->chip_id) {
@@ -275,7 +275,7 @@ static void ssb_pmu1_pllinit_r0(struct ssb_chipcommon *cc,
        }
        tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
        if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)
-               ssb_printk(KERN_EMERG PFX "Failed to turn the PLL off!\n");
+               ssb_emerg("Failed to turn the PLL off!\n");
 
        /* Set p1div and p2div. */
        pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL0);
@@ -349,9 +349,8 @@ static void ssb_pmu_pll_init(struct ssb_chipcommon *cc)
        case 43222:
                break;
        default:
-               ssb_printk(KERN_ERR PFX
-                          "ERROR: PLL init unknown for device %04X\n",
-                          bus->chip_id);
+               ssb_err("ERROR: PLL init unknown for device %04X\n",
+                       bus->chip_id);
        }
 }
 
@@ -472,9 +471,8 @@ static void ssb_pmu_resources_init(struct ssb_chipcommon *cc)
                max_msk = 0xFFFFF;
                break;
        default:
-               ssb_printk(KERN_ERR PFX
-                          "ERROR: PMU resource config unknown for device %04X\n",
-                          bus->chip_id);
+               ssb_err("ERROR: PMU resource config unknown for device %04X\n",
+                       bus->chip_id);
        }
 
        if (updown_tab) {
@@ -526,8 +524,8 @@ void ssb_pmu_init(struct ssb_chipcommon *cc)
        pmucap = chipco_read32(cc, SSB_CHIPCO_PMU_CAP);
        cc->pmu.rev = (pmucap & SSB_CHIPCO_PMU_CAP_REVISION);
 
-       ssb_dprintk(KERN_DEBUG PFX "Found rev %u PMU (capabilities 0x%08X)\n",
-                   cc->pmu.rev, pmucap);
+       ssb_dbg("Found rev %u PMU (capabilities 0x%08X)\n",
+               cc->pmu.rev, pmucap);
 
        if (cc->pmu.rev == 1)
                chipco_mask32(cc, SSB_CHIPCO_PMU_CTL,
@@ -638,9 +636,8 @@ u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc)
        case 0x5354:
                ssb_pmu_get_alp_clock_clk0(cc);
        default:
-               ssb_printk(KERN_ERR PFX
-                          "ERROR: PMU alp clock unknown for device %04X\n",
-                          bus->chip_id);
+               ssb_err("ERROR: PMU alp clock unknown for device %04X\n",
+                       bus->chip_id);
                return 0;
        }
 }
@@ -654,9 +651,8 @@ u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc)
                /* 5354 chip uses a non programmable PLL of frequency 240MHz */
                return 240000000;
        default:
-               ssb_printk(KERN_ERR PFX
-                          "ERROR: PMU cpu clock unknown for device %04X\n",
-                          bus->chip_id);
+               ssb_err("ERROR: PMU cpu clock unknown for device %04X\n",
+                       bus->chip_id);
                return 0;
        }
 }
@@ -669,9 +665,8 @@ u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc)
        case 0x5354:
                return 120000000;
        default:
-               ssb_printk(KERN_ERR PFX
-                          "ERROR: PMU controlclock unknown for device %04X\n",
-                          bus->chip_id);
+               ssb_err("ERROR: PMU controlclock unknown for device %04X\n",
+                       bus->chip_id);
                return 0;
        }
 }
index 33b37dac40bddc4bc21078a63b3b2d0861d62ec1..fa385a368a561219b3c510524bffd0325ea1446d 100644 (file)
@@ -167,21 +167,22 @@ static void set_irq(struct ssb_device *dev, unsigned int irq)
                irqflag |= (ipsflag & ~ipsflag_irq_mask[irq]);
                ssb_write32(mdev, SSB_IPSFLAG, irqflag);
        }
-       ssb_dprintk(KERN_INFO PFX
-                   "set_irq: core 0x%04x, irq %d => %d\n",
-                   dev->id.coreid, oldirq+2, irq+2);
+       ssb_dbg("set_irq: core 0x%04x, irq %d => %d\n",
+               dev->id.coreid, oldirq+2, irq+2);
 }
 
 static void print_irq(struct ssb_device *dev, unsigned int irq)
 {
-       int i;
        static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
-       ssb_dprintk(KERN_INFO PFX
-               "core 0x%04x, irq :", dev->id.coreid);
-       for (i = 0; i <= 6; i++) {
-               ssb_dprintk(" %s%s", irq_name[i], i==irq?"*":" ");
-       }
-       ssb_dprintk("\n");
+       ssb_dbg("core 0x%04x, irq : %s%s %s%s %s%s %s%s %s%s %s%s %s%s\n",
+               dev->id.coreid,
+               irq_name[0], irq == 0 ? "*" : " ",
+               irq_name[1], irq == 1 ? "*" : " ",
+               irq_name[2], irq == 2 ? "*" : " ",
+               irq_name[3], irq == 3 ? "*" : " ",
+               irq_name[4], irq == 4 ? "*" : " ",
+               irq_name[5], irq == 5 ? "*" : " ",
+               irq_name[6], irq == 6 ? "*" : " ");
 }
 
 static void dump_irq(struct ssb_bus *bus)
@@ -286,7 +287,7 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
        if (!mcore->dev)
                return; /* We don't have a MIPS core */
 
-       ssb_dprintk(KERN_INFO PFX "Initializing MIPS core...\n");
+       ssb_dbg("Initializing MIPS core...\n");
 
        bus = mcore->dev->bus;
        hz = ssb_clockspeed(bus);
@@ -334,7 +335,7 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
                        break;
                }
        }
-       ssb_dprintk(KERN_INFO PFX "after irq reconfiguration\n");
+       ssb_dbg("after irq reconfiguration\n");
        dump_irq(bus);
 
        ssb_mips_serial_init(mcore);
index 59801d23d7ec13624010be83c11ce5fd0f80a6c6..d75b72ba2672b74ccfc50f8f8a5c67dc17db0ea9 100644 (file)
@@ -263,8 +263,7 @@ int ssb_pcicore_plat_dev_init(struct pci_dev *d)
                return -ENODEV;
        }
 
-       ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
-                  pci_name(d));
+       ssb_info("PCI: Fixing up device %s\n", pci_name(d));
 
        /* Fix up interrupt lines */
        d->irq = ssb_mips_irq(extpci_core->dev) + 2;
@@ -285,12 +284,12 @@ static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)
        if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
                return;
 
-       ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev));
+       ssb_info("PCI: Fixing up bridge %s\n", pci_name(dev));
 
        /* Enable PCI bridge bus mastering and memory space */
        pci_set_master(dev);
        if (pcibios_enable_device(dev, ~0) < 0) {
-               ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
+               ssb_err("PCI: SSB bridge enable failed\n");
                return;
        }
 
@@ -299,8 +298,8 @@ static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)
 
        /* Make sure our latency is high enough to handle the devices behind us */
        lat = 168;
-       ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n",
-                  pci_name(dev), lat);
+       ssb_info("PCI: Fixing latency timer of device %s to %u\n",
+                pci_name(dev), lat);
        pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
 }
 DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge);
@@ -323,7 +322,7 @@ static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
                return;
        extpci_core = pc;
 
-       ssb_dprintk(KERN_INFO PFX "PCIcore in host mode found\n");
+       ssb_dbg("PCIcore in host mode found\n");
        /* Reset devices on the external PCI bus */
        val = SSB_PCICORE_CTL_RST_OE;
        val |= SSB_PCICORE_CTL_CLK_OE;
@@ -338,7 +337,7 @@ static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
        udelay(1); /* Assertion time demanded by the PCI standard */
 
        if (pc->dev->bus->has_cardbus_slot) {
-               ssb_dprintk(KERN_INFO PFX "CardBus slot detected\n");
+               ssb_dbg("CardBus slot detected\n");
                pc->cardbusmode = 1;
                /* GPIO 1 resets the bridge */
                ssb_gpio_out(pc->dev->bus, 1, 1);
index bb18d76f9f2c39668dc0fa80ed854aa16fbece7d..55e101115038ab22c211e00ef5a2f3cad6807509 100644 (file)
@@ -57,9 +57,8 @@ int ssb_watchdog_register(struct ssb_bus *bus)
                                             bus->busnumber, &wdt,
                                             sizeof(wdt));
        if (IS_ERR(pdev)) {
-               ssb_dprintk(KERN_INFO PFX
-                           "can not register watchdog device, err: %li\n",
-                           PTR_ERR(pdev));
+               ssb_dbg("can not register watchdog device, err: %li\n",
+                       PTR_ERR(pdev));
                return PTR_ERR(pdev);
        }
 
index 3b645b8a261fa3476bdc05446ec11bc6a4d8f3de..812775a4bfb6c6a2accfba6549b6d930c5b59fb2 100644 (file)
@@ -275,8 +275,8 @@ int ssb_devices_thaw(struct ssb_freeze_context *ctx)
 
                err = sdrv->probe(sdev, &sdev->id);
                if (err) {
-                       ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n",
-                                  dev_name(sdev->dev));
+                       ssb_err("Failed to thaw device %s\n",
+                               dev_name(sdev->dev));
                        result = err;
                }
                ssb_device_put(sdev);
@@ -447,10 +447,9 @@ void ssb_bus_unregister(struct ssb_bus *bus)
 
        err = ssb_gpio_unregister(bus);
        if (err == -EBUSY)
-               ssb_dprintk(KERN_ERR PFX "Some GPIOs are still in use.\n");
+               ssb_dbg("Some GPIOs are still in use\n");
        else if (err)
-               ssb_dprintk(KERN_ERR PFX
-                           "Can not unregister GPIO driver: %i\n", err);
+               ssb_dbg("Can not unregister GPIO driver: %i\n", err);
 
        ssb_buses_lock();
        ssb_devices_unregister(bus);
@@ -497,8 +496,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
 
                devwrap = kzalloc(sizeof(*devwrap), GFP_KERNEL);
                if (!devwrap) {
-                       ssb_printk(KERN_ERR PFX
-                                  "Could not allocate device\n");
+                       ssb_err("Could not allocate device\n");
                        err = -ENOMEM;
                        goto error;
                }
@@ -537,9 +535,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
                sdev->dev = dev;
                err = device_register(dev);
                if (err) {
-                       ssb_printk(KERN_ERR PFX
-                                  "Could not register %s\n",
-                                  dev_name(dev));
+                       ssb_err("Could not register %s\n", dev_name(dev));
                        /* Set dev to NULL to not unregister
                         * dev on error unwinding. */
                        sdev->dev = NULL;
@@ -825,10 +821,9 @@ static int ssb_bus_register(struct ssb_bus *bus,
        ssb_mipscore_init(&bus->mipscore);
        err = ssb_gpio_init(bus);
        if (err == -ENOTSUPP)
-               ssb_dprintk(KERN_DEBUG PFX "GPIO driver not activated\n");
+               ssb_dbg("GPIO driver not activated\n");
        else if (err)
-               ssb_dprintk(KERN_ERR PFX
-                          "Error registering GPIO driver: %i\n", err);
+               ssb_dbg("Error registering GPIO driver: %i\n", err);
        err = ssb_fetch_invariants(bus, get_invariants);
        if (err) {
                ssb_bus_may_powerdown(bus);
@@ -878,11 +873,11 @@ int ssb_bus_pcibus_register(struct ssb_bus *bus, struct pci_dev *host_pci)
 
        err = ssb_bus_register(bus, ssb_pci_get_invariants, 0);
        if (!err) {
-               ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
-                          "PCI device %s\n", dev_name(&host_pci->dev));
+               ssb_info("Sonics Silicon Backplane found on PCI device %s\n",
+                        dev_name(&host_pci->dev));
        } else {
-               ssb_printk(KERN_ERR PFX "Failed to register PCI version"
-                          " of SSB with error %d\n", err);
+               ssb_err("Failed to register PCI version of SSB with error %d\n",
+                       err);
        }
 
        return err;
@@ -903,8 +898,8 @@ int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
 
        err = ssb_bus_register(bus, ssb_pcmcia_get_invariants, baseaddr);
        if (!err) {
-               ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
-                          "PCMCIA device %s\n", pcmcia_dev->devname);
+               ssb_info("Sonics Silicon Backplane found on PCMCIA device %s\n",
+                        pcmcia_dev->devname);
        }
 
        return err;
@@ -925,8 +920,8 @@ int ssb_bus_sdiobus_register(struct ssb_bus *bus, struct sdio_func *func,
 
        err = ssb_bus_register(bus, ssb_sdio_get_invariants, ~0);
        if (!err) {
-               ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
-                          "SDIO device %s\n", sdio_func_id(func));
+               ssb_info("Sonics Silicon Backplane found on SDIO device %s\n",
+                        sdio_func_id(func));
        }
 
        return err;
@@ -944,8 +939,8 @@ int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr,
 
        err = ssb_bus_register(bus, get_invariants, baseaddr);
        if (!err) {
-               ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found at "
-                          "address 0x%08lX\n", baseaddr);
+               ssb_info("Sonics Silicon Backplane found at address 0x%08lX\n",
+                        baseaddr);
        }
 
        return err;
@@ -1339,7 +1334,7 @@ out:
 #endif
        return err;
 error:
-       ssb_printk(KERN_ERR PFX "Bus powerdown failed\n");
+       ssb_err("Bus powerdown failed\n");
        goto out;
 }
 EXPORT_SYMBOL(ssb_bus_may_powerdown);
@@ -1362,7 +1357,7 @@ int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl)
 
        return 0;
 error:
-       ssb_printk(KERN_ERR PFX "Bus powerup failed\n");
+       ssb_err("Bus powerup failed\n");
        return err;
 }
 EXPORT_SYMBOL(ssb_bus_powerup);
@@ -1470,15 +1465,13 @@ static int __init ssb_modinit(void)
 
        err = b43_pci_ssb_bridge_init();
        if (err) {
-               ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge "
-                          "initialization failed\n");
+               ssb_err("Broadcom 43xx PCI-SSB-bridge initialization failed\n");
                /* don't fail SSB init because of this */
                err = 0;
        }
        err = ssb_gige_init();
        if (err) {
-               ssb_printk(KERN_ERR "SSB Broadcom Gigabit Ethernet "
-                          "driver initialization failed\n");
+               ssb_err("SSB Broadcom Gigabit Ethernet driver initialization failed\n");
                /* don't fail SSB init because of this */
                err = 0;
        }
index e9d94968f394579af2527c572168be945c12ac3d..a8dc95ebf2d605a8a774c6b05b9287dfaa3b7846 100644 (file)
@@ -56,7 +56,7 @@ int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
        }
        return 0;
 error:
-       ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
+       ssb_err("Failed to switch to core %u\n", coreidx);
        return -ENODEV;
 }
 
@@ -67,10 +67,9 @@ int ssb_pci_switch_core(struct ssb_bus *bus,
        unsigned long flags;
 
 #if SSB_VERBOSE_PCICORESWITCH_DEBUG
-       ssb_printk(KERN_INFO PFX
-                  "Switching to %s core, index %d\n",
-                  ssb_core_name(dev->id.coreid),
-                  dev->core_index);
+       ssb_info("Switching to %s core, index %d\n",
+                ssb_core_name(dev->id.coreid),
+                dev->core_index);
 #endif
 
        spin_lock_irqsave(&bus->bar_lock, flags);
@@ -231,6 +230,15 @@ static inline u8 ssb_crc8(u8 crc, u8 data)
        return t[crc ^ data];
 }
 
+static void sprom_get_mac(char *mac, const u16 *in)
+{
+       int i;
+       for (i = 0; i < 3; i++) {
+               *mac++ = in[i] >> 8;
+               *mac++ = in[i];
+       }
+}
+
 static u8 ssb_sprom_crc(const u16 *sprom, u16 size)
 {
        int word;
@@ -278,7 +286,7 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
        u32 spromctl;
        u16 size = bus->sprom_size;
 
-       ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n");
+       ssb_notice("Writing SPROM. Do NOT turn off the power! Please stand by...\n");
        err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
        if (err)
                goto err_ctlreg;
@@ -286,17 +294,17 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
        err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
        if (err)
                goto err_ctlreg;
-       ssb_printk(KERN_NOTICE PFX "[ 0%%");
+       ssb_notice("[ 0%%");
        msleep(500);
        for (i = 0; i < size; i++) {
                if (i == size / 4)
-                       ssb_printk("25%%");
+                       ssb_cont("25%%");
                else if (i == size / 2)
-                       ssb_printk("50%%");
+                       ssb_cont("50%%");
                else if (i == (size * 3) / 4)
-                       ssb_printk("75%%");
+                       ssb_cont("75%%");
                else if (i % 2)
-                       ssb_printk(".");
+                       ssb_cont(".");
                writew(sprom[i], bus->mmio + bus->sprom_offset + (i * 2));
                mmiowb();
                msleep(20);
@@ -309,12 +317,12 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
        if (err)
                goto err_ctlreg;
        msleep(500);
-       ssb_printk("100%% ]\n");
-       ssb_printk(KERN_NOTICE PFX "SPROM written.\n");
+       ssb_cont("100%% ]\n");
+       ssb_notice("SPROM written\n");
 
        return 0;
 err_ctlreg:
-       ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n");
+       ssb_err("Could not access SPROM control register.\n");
        return err;
 }
 
@@ -339,10 +347,23 @@ static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in,
        return (s8)gain;
 }
 
+static void sprom_extract_r23(struct ssb_sprom *out, const u16 *in)
+{
+       SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0);
+       SPEX(opo, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0);
+       SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0);
+       SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0);
+       SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0);
+       SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0);
+       SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0);
+       SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0);
+       SPEX(maxpwr_ah, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0);
+       SPEX(maxpwr_al, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO,
+            SSB_SPROM2_MAXP_A_LO_SHIFT);
+}
+
 static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
 {
-       int i;
-       u16 v;
        u16 loc[3];
 
        if (out->revision == 3)                 /* rev 3 moved MAC */
@@ -352,19 +373,10 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
                loc[1] = SSB_SPROM1_ET0MAC;
                loc[2] = SSB_SPROM1_ET1MAC;
        }
-       for (i = 0; i < 3; i++) {
-               v = in[SPOFF(loc[0]) + i];
-               *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
-       }
+       sprom_get_mac(out->il0mac, &in[SPOFF(loc[0])]);
        if (out->revision < 3) {        /* only rev 1-2 have et0, et1 */
-               for (i = 0; i < 3; i++) {
-                       v = in[SPOFF(loc[1]) + i];
-                       *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v);
-               }
-               for (i = 0; i < 3; i++) {
-                       v = in[SPOFF(loc[2]) + i];
-                       *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v);
-               }
+               sprom_get_mac(out->et0mac, &in[SPOFF(loc[1])]);
+               sprom_get_mac(out->et1mac, &in[SPOFF(loc[2])]);
        }
        SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
        SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
@@ -372,6 +384,7 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
        SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
        SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
        SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
+       SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
        if (out->revision == 1)
                SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
                     SSB_SPROM1_BINF_CCODE_SHIFT);
@@ -398,8 +411,7 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
             SSB_SPROM1_ITSSI_A_SHIFT);
        SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
        SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
-       if (out->revision >= 2)
-               SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0);
+
        SPEX(alpha2[0], SSB_SPROM1_CCODE, 0xff00, 8);
        SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0);
 
@@ -410,6 +422,8 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
        out->antenna_gain.a1 = r123_extract_antgain(out->revision, in,
                                                    SSB_SPROM1_AGAIN_A,
                                                    SSB_SPROM1_AGAIN_A_SHIFT);
+       if (out->revision >= 2)
+               sprom_extract_r23(out, in);
 }
 
 /* Revs 4 5 and 8 have partially shared layout */
@@ -454,23 +468,20 @@ static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in)
 
 static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
 {
-       int i;
-       u16 v;
        u16 il0mac_offset;
 
        if (out->revision == 4)
                il0mac_offset = SSB_SPROM4_IL0MAC;
        else
                il0mac_offset = SSB_SPROM5_IL0MAC;
-       /* extract the MAC address */
-       for (i = 0; i < 3; i++) {
-               v = in[SPOFF(il0mac_offset) + i];
-               *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
-       }
+
+       sprom_get_mac(out->il0mac, &in[SPOFF(il0mac_offset)]);
+
        SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
        SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
             SSB_SPROM4_ETHPHY_ET1A_SHIFT);
        SPEX(board_rev, SSB_SPROM4_BOARDREV, 0xFFFF, 0);
+       SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
        if (out->revision == 4) {
                SPEX(alpha2[0], SSB_SPROM4_CCODE, 0xff00, 8);
                SPEX(alpha2[1], SSB_SPROM4_CCODE, 0x00ff, 0);
@@ -530,7 +541,7 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
 static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
 {
        int i;
-       u16 v, o;
+       u16 o;
        u16 pwr_info_offset[] = {
                SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
                SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
@@ -539,11 +550,10 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
                        ARRAY_SIZE(out->core_pwr_info));
 
        /* extract the MAC address */
-       for (i = 0; i < 3; i++) {
-               v = in[SPOFF(SSB_SPROM8_IL0MAC) + i];
-               *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
-       }
+       sprom_get_mac(out->il0mac, &in[SPOFF(SSB_SPROM8_IL0MAC)]);
+
        SPEX(board_rev, SSB_SPROM8_BOARDREV, 0xFFFF, 0);
+       SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
        SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
        SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
        SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
@@ -743,7 +753,7 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
        memset(out, 0, sizeof(*out));
 
        out->revision = in[size - 1] & 0x00FF;
-       ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision);
+       ssb_dbg("SPROM revision %d detected\n", out->revision);
        memset(out->et0mac, 0xFF, 6);           /* preset et0 and et1 mac */
        memset(out->et1mac, 0xFF, 6);
 
@@ -752,7 +762,7 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
                 * number stored in the SPROM.
                 * Always extract r1. */
                out->revision = 1;
-               ssb_dprintk(KERN_DEBUG PFX "SPROM treated as revision %d\n", out->revision);
+               ssb_dbg("SPROM treated as revision %d\n", out->revision);
        }
 
        switch (out->revision) {
@@ -769,9 +779,8 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
                sprom_extract_r8(out, in);
                break;
        default:
-               ssb_printk(KERN_WARNING PFX "Unsupported SPROM"
-                          " revision %d detected. Will extract"
-                          " v1\n", out->revision);
+               ssb_warn("Unsupported SPROM revision %d detected. Will extract v1\n",
+                        out->revision);
                out->revision = 1;
                sprom_extract_r123(out, in);
        }
@@ -791,7 +800,7 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
        u16 *buf;
 
        if (!ssb_is_sprom_available(bus)) {
-               ssb_printk(KERN_ERR PFX "No SPROM available!\n");
+               ssb_err("No SPROM available!\n");
                return -ENODEV;
        }
        if (bus->chipco.dev) {  /* can be unavailable! */
@@ -810,7 +819,7 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
        } else {
                bus->sprom_offset = SSB_SPROM_BASE1;
        }
-       ssb_dprintk(KERN_INFO PFX "SPROM offset is 0x%x\n", bus->sprom_offset);
+       ssb_dbg("SPROM offset is 0x%x\n", bus->sprom_offset);
 
        buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
        if (!buf)
@@ -835,18 +844,15 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
                         * available for this device in some other storage */
                        err = ssb_fill_sprom_with_fallback(bus, sprom);
                        if (err) {
-                               ssb_printk(KERN_WARNING PFX "WARNING: Using"
-                                          " fallback SPROM failed (err %d)\n",
-                                          err);
+                               ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n",
+                                        err);
                        } else {
-                               ssb_dprintk(KERN_DEBUG PFX "Using SPROM"
-                                           " revision %d provided by"
-                                           " platform.\n", sprom->revision);
+                               ssb_dbg("Using SPROM revision %d provided by platform\n",
+                                       sprom->revision);
                                err = 0;
                                goto out_free;
                        }
-                       ssb_printk(KERN_WARNING PFX "WARNING: Invalid"
-                                  " SPROM CRC (corrupt SPROM)\n");
+                       ssb_warn("WARNING: Invalid SPROM CRC (corrupt SPROM)\n");
                }
        }
        err = sprom_extract(bus, sprom, buf, bus->sprom_size);
index fbafed5b729bdcf6ae57889c3c9d4f05ca0509c5..b413e01870878b51097456ec71112bf32b56f2b5 100644 (file)
@@ -143,7 +143,7 @@ int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
 
        return 0;
 error:
-       ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
+       ssb_err("Failed to switch to core %u\n", coreidx);
        return err;
 }
 
@@ -153,10 +153,9 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus,
        int err;
 
 #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
-       ssb_printk(KERN_INFO PFX
-                  "Switching to %s core, index %d\n",
-                  ssb_core_name(dev->id.coreid),
-                  dev->core_index);
+       ssb_info("Switching to %s core, index %d\n",
+                ssb_core_name(dev->id.coreid),
+                dev->core_index);
 #endif
 
        err = ssb_pcmcia_switch_coreidx(bus, dev->core_index);
@@ -192,7 +191,7 @@ int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
 
        return 0;
 error:
-       ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n");
+       ssb_err("Failed to switch pcmcia segment\n");
        return err;
 }
 
@@ -549,44 +548,39 @@ static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom)
        bool failed = 0;
        size_t size = SSB_PCMCIA_SPROM_SIZE;
 
-       ssb_printk(KERN_NOTICE PFX
-                  "Writing SPROM. Do NOT turn off the power! "
-                  "Please stand by...\n");
+       ssb_notice("Writing SPROM. Do NOT turn off the power! Please stand by...\n");
        err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEEN);
        if (err) {
-               ssb_printk(KERN_NOTICE PFX
-                          "Could not enable SPROM write access.\n");
+               ssb_notice("Could not enable SPROM write access\n");
                return -EBUSY;
        }
-       ssb_printk(KERN_NOTICE PFX "[ 0%%");
+       ssb_notice("[ 0%%");
        msleep(500);
        for (i = 0; i < size; i++) {
                if (i == size / 4)
-                       ssb_printk("25%%");
+                       ssb_cont("25%%");
                else if (i == size / 2)
-                       ssb_printk("50%%");
+                       ssb_cont("50%%");
                else if (i == (size * 3) / 4)
-                       ssb_printk("75%%");
+                       ssb_cont("75%%");
                else if (i % 2)
-                       ssb_printk(".");
+                       ssb_cont(".");
                err = ssb_pcmcia_sprom_write(bus, i, sprom[i]);
                if (err) {
-                       ssb_printk(KERN_NOTICE PFX
-                                  "Failed to write to SPROM.\n");
+                       ssb_notice("Failed to write to SPROM\n");
                        failed = 1;
                        break;
                }
        }
        err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS);
        if (err) {
-               ssb_printk(KERN_NOTICE PFX
-                          "Could not disable SPROM write access.\n");
+               ssb_notice("Could not disable SPROM write access\n");
                failed = 1;
        }
        msleep(500);
        if (!failed) {
-               ssb_printk("100%% ]\n");
-               ssb_printk(KERN_NOTICE PFX "SPROM written.\n");
+               ssb_cont("100%% ]\n");
+               ssb_notice("SPROM written\n");
        }
 
        return failed ? -EBUSY : 0;
@@ -700,7 +694,7 @@ static int ssb_pcmcia_do_get_invariants(struct pcmcia_device *p_dev,
        return -ENOSPC; /* continue with next entry */
 
 error:
-       ssb_printk(KERN_ERR PFX
+       ssb_err(
                   "PCMCIA: Failed to fetch device invariants: %s\n",
                   error_description);
        return -ENODEV;
@@ -722,7 +716,7 @@ int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
        res = pcmcia_loop_tuple(bus->host_pcmcia, CISTPL_FUNCE,
                                ssb_pcmcia_get_mac, sprom);
        if (res != 0) {
-               ssb_printk(KERN_ERR PFX
+               ssb_err(
                        "PCMCIA: Failed to fetch MAC address\n");
                return -ENODEV;
        }
@@ -733,7 +727,7 @@ int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
        if ((res == 0) || (res == -ENOSPC))
                return 0;
 
-       ssb_printk(KERN_ERR PFX
+       ssb_err(
                        "PCMCIA: Failed to fetch device invariants\n");
        return -ENODEV;
 }
@@ -843,6 +837,6 @@ int ssb_pcmcia_init(struct ssb_bus *bus)
 
        return 0;
 error:
-       ssb_printk(KERN_ERR PFX "Failed to initialize PCMCIA host device\n");
+       ssb_err("Failed to initialize PCMCIA host device\n");
        return err;
 }
index ab4627cf11148c96a969ebd966c4657265e49471..b9429df583eb7f744a1e3a9c68207294ccdb8764 100644 (file)
@@ -125,8 +125,7 @@ static u16 pcidev_to_chipid(struct pci_dev *pci_dev)
                chipid_fallback = 0x4401;
                break;
        default:
-               ssb_printk(KERN_ERR PFX
-                          "PCI-ID not in fallback list\n");
+               ssb_err("PCI-ID not in fallback list\n");
        }
 
        return chipid_fallback;
@@ -152,8 +151,7 @@ static u8 chipid_to_nrcores(u16 chipid)
        case 0x4704:
                return 9;
        default:
-               ssb_printk(KERN_ERR PFX
-                          "CHIPID not in nrcores fallback list\n");
+               ssb_err("CHIPID not in nrcores fallback list\n");
        }
 
        return 1;
@@ -320,15 +318,13 @@ int ssb_bus_scan(struct ssb_bus *bus,
                        bus->chip_package = 0;
                }
        }
-       ssb_printk(KERN_INFO PFX "Found chip with id 0x%04X, rev 0x%02X and "
-                  "package 0x%02X\n", bus->chip_id, bus->chip_rev,
-                  bus->chip_package);
+       ssb_info("Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n",
+                bus->chip_id, bus->chip_rev, bus->chip_package);
        if (!bus->nr_devices)
                bus->nr_devices = chipid_to_nrcores(bus->chip_id);
        if (bus->nr_devices > ARRAY_SIZE(bus->devices)) {
-               ssb_printk(KERN_ERR PFX
-                          "More than %d ssb cores found (%d)\n",
-                          SSB_MAX_NR_CORES, bus->nr_devices);
+               ssb_err("More than %d ssb cores found (%d)\n",
+                       SSB_MAX_NR_CORES, bus->nr_devices);
                goto err_unmap;
        }
        if (bus->bustype == SSB_BUSTYPE_SSB) {
@@ -370,8 +366,7 @@ int ssb_bus_scan(struct ssb_bus *bus,
                        nr_80211_cores++;
                        if (nr_80211_cores > 1) {
                                if (!we_support_multiple_80211_cores(bus)) {
-                                       ssb_dprintk(KERN_INFO PFX "Ignoring additional "
-                                                   "802.11 core\n");
+                                       ssb_dbg("Ignoring additional 802.11 core\n");
                                        continue;
                                }
                        }
@@ -379,8 +374,7 @@ int ssb_bus_scan(struct ssb_bus *bus,
                case SSB_DEV_EXTIF:
 #ifdef CONFIG_SSB_DRIVER_EXTIF
                        if (bus->extif.dev) {
-                               ssb_printk(KERN_WARNING PFX
-                                          "WARNING: Multiple EXTIFs found\n");
+                               ssb_warn("WARNING: Multiple EXTIFs found\n");
                                break;
                        }
                        bus->extif.dev = dev;
@@ -388,8 +382,7 @@ int ssb_bus_scan(struct ssb_bus *bus,
                        break;
                case SSB_DEV_CHIPCOMMON:
                        if (bus->chipco.dev) {
-                               ssb_printk(KERN_WARNING PFX
-                                          "WARNING: Multiple ChipCommon found\n");
+                               ssb_warn("WARNING: Multiple ChipCommon found\n");
                                break;
                        }
                        bus->chipco.dev = dev;
@@ -398,8 +391,7 @@ int ssb_bus_scan(struct ssb_bus *bus,
                case SSB_DEV_MIPS_3302:
 #ifdef CONFIG_SSB_DRIVER_MIPS
                        if (bus->mipscore.dev) {
-                               ssb_printk(KERN_WARNING PFX
-                                          "WARNING: Multiple MIPS cores found\n");
+                               ssb_warn("WARNING: Multiple MIPS cores found\n");
                                break;
                        }
                        bus->mipscore.dev = dev;
@@ -420,8 +412,7 @@ int ssb_bus_scan(struct ssb_bus *bus,
                                }
                        }
                        if (bus->pcicore.dev) {
-                               ssb_printk(KERN_WARNING PFX
-                                          "WARNING: Multiple PCI(E) cores found\n");
+                               ssb_warn("WARNING: Multiple PCI(E) cores found\n");
                                break;
                        }
                        bus->pcicore.dev = dev;
index 80d366fcf8d30acecd5cb43a3541d4182947770e..a3b23644b0fbb42218285c571656a8db2154517d 100644 (file)
@@ -127,13 +127,13 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
                goto out_kfree;
        err = ssb_devices_freeze(bus, &freeze);
        if (err) {
-               ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
+               ssb_err("SPROM write: Could not freeze all devices\n");
                goto out_unlock;
        }
        res = sprom_write(bus, sprom);
        err = ssb_devices_thaw(&freeze);
        if (err)
-               ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
+               ssb_err("SPROM write: Could not thaw all devices\n");
 out_unlock:
        mutex_unlock(&bus->sprom_mutex);
 out_kfree:
index 466171b77f68f74d0ebfd7c488a31af9006aebb7..4671f17f09aff44e809766e6317273d480ec6444 100644 (file)
@@ -9,16 +9,27 @@
 #define PFX    "ssb: "
 
 #ifdef CONFIG_SSB_SILENT
-# define ssb_printk(fmt, x...) do { /* nothing */ } while (0)
+# define ssb_printk(fmt, ...)                                  \
+       do { if (0) printk(fmt, ##__VA_ARGS__); } while (0)
 #else
-# define ssb_printk            printk
+# define ssb_printk(fmt, ...)                                  \
+       printk(fmt, ##__VA_ARGS__)
 #endif /* CONFIG_SSB_SILENT */
 
+#define ssb_emerg(fmt, ...)    ssb_printk(KERN_EMERG PFX fmt, ##__VA_ARGS__)
+#define ssb_err(fmt, ...)      ssb_printk(KERN_ERR PFX fmt, ##__VA_ARGS__)
+#define ssb_warn(fmt, ...)     ssb_printk(KERN_WARNING PFX fmt, ##__VA_ARGS__)
+#define ssb_notice(fmt, ...)   ssb_printk(KERN_NOTICE PFX fmt, ##__VA_ARGS__)
+#define ssb_info(fmt, ...)     ssb_printk(KERN_INFO PFX fmt, ##__VA_ARGS__)
+#define ssb_cont(fmt, ...)     ssb_printk(KERN_CONT fmt, ##__VA_ARGS__)
+
 /* dprintk: Debugging printk; vanishes for non-debug compilation */
 #ifdef CONFIG_SSB_DEBUG
-# define ssb_dprintk(fmt, x...)        ssb_printk(fmt , ##x)
+# define ssb_dbg(fmt, ...)                                     \
+       ssb_printk(KERN_DEBUG PFX fmt, ##__VA_ARGS__)
 #else
-# define ssb_dprintk(fmt, x...)        do { /* nothing */ } while (0)
+# define ssb_dbg(fmt, ...)                                     \
+       do { if (0) printk(KERN_DEBUG PFX fmt, ##__VA_ARGS__); } while (0)
 #endif
 
 #ifdef CONFIG_SSB_DEBUG
index 52c25ba5831d1bf2f595b6b37c1eefe6ee7b6fcf..c1239aaa6282e9fb0c009e651a44d82925c39044 100644 (file)
@@ -15,7 +15,7 @@
 
 #include <linux/module.h>
 #include <linux/etherdevice.h>
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <asm/byteorder.h>
 #include <net/sock.h>
 
 
 #define ND_MAX_GROUP                   30
 #define ND_IFINDEX_LEN                 sizeof(int)
-#define ND_NLMSG_SPACE(len)            (NLMSG_SPACE(len) + ND_IFINDEX_LEN)
+#define ND_NLMSG_SPACE(len)            (nlmsg_total_size(len) + ND_IFINDEX_LEN)
 #define ND_NLMSG_DATA(nlh) \
-       ((void *)((char *)NLMSG_DATA(nlh) + ND_IFINDEX_LEN))
+       ((void *)((char *)nlmsg_data(nlh) + ND_IFINDEX_LEN))
 #define ND_NLMSG_S_LEN(len)            (len+ND_IFINDEX_LEN)
 #define ND_NLMSG_R_LEN(nlh)            (nlh->nlmsg_len-ND_IFINDEX_LEN)
-#define ND_NLMSG_IFIDX(nlh)            NLMSG_DATA(nlh)
+#define ND_NLMSG_IFIDX(nlh)            nlmsg_data(nlh)
 #define ND_MAX_MSG_LEN                 8096
 
 #if defined(DEFINE_MUTEX)
@@ -51,7 +51,7 @@ static void netlink_rcv_cb(struct sk_buff *skb)
        void *msg;
        int ifindex;
 
-       if (skb->len >= NLMSG_SPACE(0)) {
+       if (skb->len >= NLMSG_HDRLEN) {
                nlh = (struct nlmsghdr *)skb->data;
 
                if (skb->len < nlh->nlmsg_len ||
@@ -124,7 +124,7 @@ int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len)
                return -EINVAL;
        }
 
-       skb = alloc_skb(NLMSG_SPACE(len), GFP_ATOMIC);
+       skb = nlmsg_new(len, GFP_ATOMIC);
        if (!skb) {
                pr_err("netlink_broadcast ret=%d\n", ret);
                return -ENOMEM;
index ec6fb3fa59bb5962281bb6277220941b91df053c..87c216c1e54ede98ac69c8bc36d9e9fa9c940170 100644 (file)
@@ -64,20 +64,10 @@ enum {
        VHOST_NET_VQ_MAX = 2,
 };
 
-enum vhost_net_poll_state {
-       VHOST_NET_POLL_DISABLED = 0,
-       VHOST_NET_POLL_STARTED = 1,
-       VHOST_NET_POLL_STOPPED = 2,
-};
-
 struct vhost_net {
        struct vhost_dev dev;
        struct vhost_virtqueue vqs[VHOST_NET_VQ_MAX];
        struct vhost_poll poll[VHOST_NET_VQ_MAX];
-       /* Tells us whether we are polling a socket for TX.
-        * We only do this when socket buffer fills up.
-        * Protected by tx vq lock. */
-       enum vhost_net_poll_state tx_poll_state;
        /* Number of TX recently submitted.
         * Protected by tx vq lock. */
        unsigned tx_packets;
@@ -155,28 +145,6 @@ static void copy_iovec_hdr(const struct iovec *from, struct iovec *to,
        }
 }
 
-/* Caller must have TX VQ lock */
-static void tx_poll_stop(struct vhost_net *net)
-{
-       if (likely(net->tx_poll_state != VHOST_NET_POLL_STARTED))
-               return;
-       vhost_poll_stop(net->poll + VHOST_NET_VQ_TX);
-       net->tx_poll_state = VHOST_NET_POLL_STOPPED;
-}
-
-/* Caller must have TX VQ lock */
-static int tx_poll_start(struct vhost_net *net, struct socket *sock)
-{
-       int ret;
-
-       if (unlikely(net->tx_poll_state != VHOST_NET_POLL_STOPPED))
-               return 0;
-       ret = vhost_poll_start(net->poll + VHOST_NET_VQ_TX, sock->file);
-       if (!ret)
-               net->tx_poll_state = VHOST_NET_POLL_STARTED;
-       return ret;
-}
-
 /* In case of DMA done not in order in lower device driver for some reason.
  * upend_idx is used to track end of used idx, done_idx is used to track head
  * of used idx. Once lower device DMA done contiguously, we will signal KVM
@@ -242,7 +210,7 @@ static void handle_tx(struct vhost_net *net)
                .msg_flags = MSG_DONTWAIT,
        };
        size_t len, total_len = 0;
-       int err, wmem;
+       int err;
        size_t hdr_size;
        struct socket *sock;
        struct vhost_ubuf_ref *uninitialized_var(ubufs);
@@ -253,19 +221,9 @@ static void handle_tx(struct vhost_net *net)
        if (!sock)
                return;
 
-       wmem = atomic_read(&sock->sk->sk_wmem_alloc);
-       if (wmem >= sock->sk->sk_sndbuf) {
-               mutex_lock(&vq->mutex);
-               tx_poll_start(net, sock);
-               mutex_unlock(&vq->mutex);
-               return;
-       }
-
        mutex_lock(&vq->mutex);
        vhost_disable_notify(&net->dev, vq);
 
-       if (wmem < sock->sk->sk_sndbuf / 2)
-               tx_poll_stop(net);
        hdr_size = vq->vhost_hlen;
        zcopy = vq->ubufs;
 
@@ -285,23 +243,14 @@ static void handle_tx(struct vhost_net *net)
                if (head == vq->num) {
                        int num_pends;
 
-                       wmem = atomic_read(&sock->sk->sk_wmem_alloc);
-                       if (wmem >= sock->sk->sk_sndbuf * 3 / 4) {
-                               tx_poll_start(net, sock);
-                               set_bit(SOCK_ASYNC_NOSPACE, &sock->flags);
-                               break;
-                       }
                        /* If more outstanding DMAs, queue the work.
                         * Handle upend_idx wrap around
                         */
                        num_pends = likely(vq->upend_idx >= vq->done_idx) ?
                                    (vq->upend_idx - vq->done_idx) :
                                    (vq->upend_idx + UIO_MAXIOV - vq->done_idx);
-                       if (unlikely(num_pends > VHOST_MAX_PEND)) {
-                               tx_poll_start(net, sock);
-                               set_bit(SOCK_ASYNC_NOSPACE, &sock->flags);
+                       if (unlikely(num_pends > VHOST_MAX_PEND))
                                break;
-                       }
                        if (unlikely(vhost_enable_notify(&net->dev, vq))) {
                                vhost_disable_notify(&net->dev, vq);
                                continue;
@@ -364,8 +313,6 @@ static void handle_tx(struct vhost_net *net)
                                        UIO_MAXIOV;
                        }
                        vhost_discard_vq_desc(vq, 1);
-                       if (err == -EAGAIN || err == -ENOBUFS)
-                               tx_poll_start(net, sock);
                        break;
                }
                if (err != len)
@@ -628,7 +575,6 @@ static int vhost_net_open(struct inode *inode, struct file *f)
 
        vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT, dev);
        vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN, dev);
-       n->tx_poll_state = VHOST_NET_POLL_DISABLED;
 
        f->private_data = n;
 
@@ -638,32 +584,24 @@ static int vhost_net_open(struct inode *inode, struct file *f)
 static void vhost_net_disable_vq(struct vhost_net *n,
                                 struct vhost_virtqueue *vq)
 {
+       struct vhost_poll *poll = n->poll + (vq - n->vqs);
        if (!vq->private_data)
                return;
-       if (vq == n->vqs + VHOST_NET_VQ_TX) {
-               tx_poll_stop(n);
-               n->tx_poll_state = VHOST_NET_POLL_DISABLED;
-       } else
-               vhost_poll_stop(n->poll + VHOST_NET_VQ_RX);
+       vhost_poll_stop(poll);
 }
 
 static int vhost_net_enable_vq(struct vhost_net *n,
                                struct vhost_virtqueue *vq)
 {
+       struct vhost_poll *poll = n->poll + (vq - n->vqs);
        struct socket *sock;
-       int ret;
 
        sock = rcu_dereference_protected(vq->private_data,
                                         lockdep_is_held(&vq->mutex));
        if (!sock)
                return 0;
-       if (vq == n->vqs + VHOST_NET_VQ_TX) {
-               n->tx_poll_state = VHOST_NET_POLL_STOPPED;
-               ret = tx_poll_start(n, sock);
-       } else
-               ret = vhost_poll_start(n->poll + VHOST_NET_VQ_RX, sock->file);
 
-       return ret;
+       return vhost_poll_start(poll, sock->file);
 }
 
 static struct socket *vhost_net_stop_vq(struct vhost_net *n,
index 9759249e6d908867cf72f53e5bb8e4ecc30db8ae..4eecdb867d53187179aec514072e756e17397719 100644 (file)
@@ -89,6 +89,9 @@ int vhost_poll_start(struct vhost_poll *poll, struct file *file)
        unsigned long mask;
        int ret = 0;
 
+       if (poll->wqh)
+               return 0;
+
        mask = file->f_op->poll(file, &poll->table);
        if (mask)
                vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask);
index 4f5ad246582f9c992f7e08f9700cd7ac8e01f655..d0ccd2fd79eb0430ac08db074edfb811859e4b90 100644 (file)
@@ -52,8 +52,8 @@
 #include <linux/mutex.h>
 #include <linux/sctp.h>
 #include <linux/slab.h>
+#include <linux/sctp.h>
 #include <net/sctp/sctp.h>
-#include <net/sctp/user.h>
 #include <net/ipv6.h>
 
 #include "dlm_internal.h"
index e0ce311011c007f9e7feb3a03b6390bc53e66cb5..0ab6712fd76b0c7e6adb46e5d9ff014b44c24cd7 100644 (file)
@@ -173,6 +173,60 @@ struct bcma_host_ops {
 #define BCMA_CHIP_ID_BCM53572  53572
 #define  BCMA_PKG_ID_BCM47188  9
 
+/* Board types (on PCI usually equals to the subsystem dev id) */
+/* BCM4313 */
+#define BCMA_BOARD_TYPE_BCM94313BU     0X050F
+#define BCMA_BOARD_TYPE_BCM94313HM     0X0510
+#define BCMA_BOARD_TYPE_BCM94313EPA    0X0511
+#define BCMA_BOARD_TYPE_BCM94313HMG    0X051C
+/* BCM4716 */
+#define BCMA_BOARD_TYPE_BCM94716NR2    0X04CD
+/* BCM43224 */
+#define BCMA_BOARD_TYPE_BCM943224X21   0X056E
+#define BCMA_BOARD_TYPE_BCM943224X21_FCC       0X00D1
+#define BCMA_BOARD_TYPE_BCM943224X21B  0X00E9
+#define BCMA_BOARD_TYPE_BCM943224M93   0X008B
+#define BCMA_BOARD_TYPE_BCM943224M93A  0X0090
+#define BCMA_BOARD_TYPE_BCM943224X16   0X0093
+#define BCMA_BOARD_TYPE_BCM94322X9     0X008D
+#define BCMA_BOARD_TYPE_BCM94322M35E   0X008E
+/* BCM43228 */
+#define BCMA_BOARD_TYPE_BCM943228BU8   0X0540
+#define BCMA_BOARD_TYPE_BCM943228BU9   0X0541
+#define BCMA_BOARD_TYPE_BCM943228BU    0X0542
+#define BCMA_BOARD_TYPE_BCM943227HM4L  0X0543
+#define BCMA_BOARD_TYPE_BCM943227HMB   0X0544
+#define BCMA_BOARD_TYPE_BCM943228HM4L  0X0545
+#define BCMA_BOARD_TYPE_BCM943228SD    0X0573
+/* BCM4331 */
+#define BCMA_BOARD_TYPE_BCM94331X19    0X00D6
+#define BCMA_BOARD_TYPE_BCM94331X28    0X00E4
+#define BCMA_BOARD_TYPE_BCM94331X28B   0X010E
+#define BCMA_BOARD_TYPE_BCM94331PCIEBT3AX      0X00E4
+#define BCMA_BOARD_TYPE_BCM94331X12_2G 0X00EC
+#define BCMA_BOARD_TYPE_BCM94331X12_5G 0X00ED
+#define BCMA_BOARD_TYPE_BCM94331X29B   0X00EF
+#define BCMA_BOARD_TYPE_BCM94331CSAX   0X00EF
+#define BCMA_BOARD_TYPE_BCM94331X19C   0X00F5
+#define BCMA_BOARD_TYPE_BCM94331X33    0X00F4
+#define BCMA_BOARD_TYPE_BCM94331BU     0X0523
+#define BCMA_BOARD_TYPE_BCM94331S9BU   0X0524
+#define BCMA_BOARD_TYPE_BCM94331MC     0X0525
+#define BCMA_BOARD_TYPE_BCM94331MCI    0X0526
+#define BCMA_BOARD_TYPE_BCM94331PCIEBT4        0X0527
+#define BCMA_BOARD_TYPE_BCM94331HM     0X0574
+#define BCMA_BOARD_TYPE_BCM94331PCIEDUAL       0X059B
+#define BCMA_BOARD_TYPE_BCM94331MCH5   0X05A9
+#define BCMA_BOARD_TYPE_BCM94331CS     0X05C6
+#define BCMA_BOARD_TYPE_BCM94331CD     0X05DA
+/* BCM53572 */
+#define BCMA_BOARD_TYPE_BCM953572BU    0X058D
+#define BCMA_BOARD_TYPE_BCM953572NR2   0X058E
+#define BCMA_BOARD_TYPE_BCM947188NR2   0X058F
+#define BCMA_BOARD_TYPE_BCM953572SDRNR2        0X0590
+/* BCM43142 */
+#define BCMA_BOARD_TYPE_BCM943142HM    0X05E0
+
 struct bcma_device {
        struct bcma_bus *bus;
        struct bcma_device_id id;
index 8390c474f69aefafdd31e8da31c1ea1ea1339516..453fcc914683a396a825d6ef7a3b636da0547392 100644 (file)
 #define  BCMA_CC_CHIPST_4706_MIPS_BENDIAN      BIT(3) /* 0: little, 1: big endian */
 #define  BCMA_CC_CHIPST_4706_PCIE1_DISABLE     BIT(5) /* PCIE1 enable strap pin */
 #define  BCMA_CC_CHIPST_5357_NAND_BOOT         BIT(4) /* NAND boot, valid for CC rev 38 and/or BCM5357 */
+#define  BCMA_CC_CHIPST_4360_XTAL_40MZ         0x00000001
 #define BCMA_CC_JCMD                   0x0030          /* Rev >= 10 only */
 #define  BCMA_CC_JCMD_START            0x80000000
 #define  BCMA_CC_JCMD_BUSY             0x80000000
@@ -607,6 +608,8 @@ void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
 
 extern u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks);
 
+extern u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc);
+
 void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value);
 
 u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask);
index 2c1bc1ea04ee05471bb536ccdd8ed6a8cf2fbc3c..1d5b02a96c46304b7821c498e179a67c534263bd 100644 (file)
@@ -26,6 +26,7 @@ void proc_id_connector(struct task_struct *task, int which_id);
 void proc_sid_connector(struct task_struct *task);
 void proc_ptrace_connector(struct task_struct *task, int which_id);
 void proc_comm_connector(struct task_struct *task);
+void proc_coredump_connector(struct task_struct *task);
 void proc_exit_connector(struct task_struct *task);
 #else
 static inline void proc_fork_connector(struct task_struct *task)
@@ -48,6 +49,9 @@ static inline void proc_ptrace_connector(struct task_struct *task,
                                         int ptrace_id)
 {}
 
+static inline void proc_coredump_connector(struct task_struct *task)
+{}
+
 static inline void proc_exit_connector(struct task_struct *task)
 {}
 #endif /* CONFIG_PROC_EVENTS */
index c45eabc135e1f00b7825d017b37df1666acb203e..d1248f401a56d3210d8d9297e8ecaa893821f870 100644 (file)
@@ -48,8 +48,21 @@ extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen);
 extern int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned len);
 
 #ifdef CONFIG_BPF_JIT
+#include <linux/linkage.h>
+#include <linux/printk.h>
+
 extern void bpf_jit_compile(struct sk_filter *fp);
 extern void bpf_jit_free(struct sk_filter *fp);
+
+static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
+                               u32 pass, void *image)
+{
+       pr_err("flen=%u proglen=%u pass=%u image=%p\n",
+              flen, proglen, pass, image);
+       if (image)
+               print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_ADDRESS,
+                              16, 1, image, proglen, false);
+}
 #define SK_RUN_FILTER(FILTER, SKB) (*FILTER->bpf_func)(SKB, FILTER->insns)
 #else
 static inline void bpf_jit_compile(struct sk_filter *fp)
@@ -126,6 +139,7 @@ enum {
        BPF_S_ANC_SECCOMP_LD_W,
        BPF_S_ANC_VLAN_TAG,
        BPF_S_ANC_VLAN_TAG_PRESENT,
+       BPF_S_ANC_PAY_OFFSET,
 };
 
 #endif /* __LINUX_FILTER_H__ */
index 7e24fe0cfbcd773ba8361bf713bf59543b1f750f..4cf0c9e4dd996b4e728510fa737800764fe70f14 100644 (file)
 #define IEEE80211_CTL_EXT_SSW_FBACK    0x9000
 #define IEEE80211_CTL_EXT_SSW_ACK      0xa000
 
+
+#define IEEE80211_SN_MASK              ((IEEE80211_SCTL_SEQ) >> 4)
+#define IEEE80211_MAX_SN               IEEE80211_SN_MASK
+#define IEEE80211_SN_MODULO            (IEEE80211_MAX_SN + 1)
+
+static inline int ieee80211_sn_less(u16 sn1, u16 sn2)
+{
+       return ((sn1 - sn2) & IEEE80211_SN_MASK) > (IEEE80211_SN_MODULO >> 1);
+}
+
+static inline u16 ieee80211_sn_add(u16 sn1, u16 sn2)
+{
+       return (sn1 + sn2) & IEEE80211_SN_MASK;
+}
+
+static inline u16 ieee80211_sn_inc(u16 sn)
+{
+       return ieee80211_sn_add(sn, 1);
+}
+
+static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
+{
+       return (sn1 - sn2) & IEEE80211_SN_MASK;
+}
+
+#define IEEE80211_SEQ_TO_SN(seq)       (((seq) & IEEE80211_SCTL_SEQ) >> 4)
+#define IEEE80211_SN_TO_SEQ(ssn)       (((ssn) << 4) & IEEE80211_SCTL_SEQ)
+
 /* miscellaneous IEEE 802.11 constants */
 #define IEEE80211_MAX_FRAG_THRESHOLD   2352
 #define IEEE80211_MAX_RTS_THRESHOLD    2353
@@ -185,7 +213,7 @@ struct ieee80211_hdr {
        u8 addr3[6];
        __le16 seq_ctrl;
        u8 addr4[6];
-} __packed;
+} __packed __aligned(2);
 
 struct ieee80211_hdr_3addr {
        __le16 frame_control;
@@ -194,7 +222,7 @@ struct ieee80211_hdr_3addr {
        u8 addr2[6];
        u8 addr3[6];
        __le16 seq_ctrl;
-} __packed;
+} __packed __aligned(2);
 
 struct ieee80211_qos_hdr {
        __le16 frame_control;
@@ -204,7 +232,7 @@ struct ieee80211_qos_hdr {
        u8 addr3[6];
        __le16 seq_ctrl;
        __le16 qos_ctrl;
-} __packed;
+} __packed __aligned(2);
 
 /**
  * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
@@ -581,7 +609,7 @@ struct ieee80211s_hdr {
        __le32 seqnum;
        u8 eaddr1[6];
        u8 eaddr2[6];
-} __packed;
+} __packed __aligned(2);
 
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4       0x1
@@ -875,7 +903,7 @@ struct ieee80211_mgmt {
                        } u;
                } __packed action;
        } u;
-} __packed;
+} __packed __aligned(2);
 
 /* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */
 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
@@ -906,20 +934,20 @@ struct ieee80211_rts {
        __le16 duration;
        u8 ra[6];
        u8 ta[6];
-} __packed;
+} __packed __aligned(2);
 
 struct ieee80211_cts {
        __le16 frame_control;
        __le16 duration;
        u8 ra[6];
-} __packed;
+} __packed __aligned(2);
 
 struct ieee80211_pspoll {
        __le16 frame_control;
        __le16 aid;
        u8 bssid[6];
        u8 ta[6];
-} __packed;
+} __packed __aligned(2);
 
 /* TDLS */
 
@@ -1290,11 +1318,6 @@ struct ieee80211_vht_operation {
 } __packed;
 
 
-#define IEEE80211_VHT_MCS_ZERO_TO_SEVEN_SUPPORT 0
-#define IEEE80211_VHT_MCS_ZERO_TO_EIGHT_SUPPORT 1
-#define IEEE80211_VHT_MCS_ZERO_TO_NINE_SUPPORT  2
-#define IEEE80211_VHT_MCS_NOT_SUPPORTED 3
-
 /* 802.11ac VHT Capabilities */
 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895                 0x00000000
 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991                 0x00000001
@@ -1310,10 +1333,11 @@ struct ieee80211_vht_operation {
 #define IEEE80211_VHT_CAP_RXSTBC_2                             0x00000200
 #define IEEE80211_VHT_CAP_RXSTBC_3                             0x00000300
 #define IEEE80211_VHT_CAP_RXSTBC_4                             0x00000400
+#define IEEE80211_VHT_CAP_RXSTBC_MASK                          0x00000700
 #define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE                        0x00000800
 #define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE                        0x00001000
 #define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX              0x00006000
-#define IEEE80211_VHT_CAP_SOUNDING_DIMENTION_MAX               0x00030000
+#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX              0x00030000
 #define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE                        0x00080000
 #define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE                        0x00100000
 #define IEEE80211_VHT_CAP_VHT_TXOP_PS                          0x00200000
index 89b4614a47220e498b604034090eda7f10963b2c..f563907ed7767fdf231558e2e042e670b5d62c1d 100644 (file)
@@ -33,7 +33,15 @@ static inline struct arphdr *arp_hdr(const struct sk_buff *skb)
 
 static inline int arp_hdr_len(struct net_device *dev)
 {
-       /* ARP header, plus 2 device addresses, plus 2 IP addresses. */
-       return sizeof(struct arphdr) + (dev->addr_len + sizeof(u32)) * 2;
+       switch (dev->type) {
+#if IS_ENABLED(CONFIG_FIREWIRE_NET)
+       case ARPHRD_IEEE1394:
+               /* ARP header, device address and 2 IP addresses */
+               return sizeof(struct arphdr) + dev->addr_len + sizeof(u32) * 2;
+#endif
+       default:
+               /* ARP header, plus 2 device addresses, plus 2 IP addresses. */
+               return sizeof(struct arphdr) + (dev->addr_len + sizeof(u32)) * 2;
+       }
 }
 #endif /* _LINUX_IF_ARP_H */
index cfd21e3d55068704d00ccdca64a430ce1ac7b22b..4474557904f69eef849b329b41b91ba4f72bb678 100644 (file)
@@ -112,6 +112,10 @@ struct team_mode_ops {
        void (*port_disabled)(struct team *team, struct team_port *port);
 };
 
+extern int team_modeop_port_enter(struct team *team, struct team_port *port);
+extern void team_modeop_port_change_dev_addr(struct team *team,
+                                            struct team_port *port);
+
 enum team_option_type {
        TEAM_OPTION_TYPE_U32,
        TEAM_OPTION_TYPE_STRING,
@@ -236,7 +240,26 @@ static inline struct team_port *team_get_port_by_index_rcu(struct team *team,
        return NULL;
 }
 
-extern int team_port_set_team_dev_addr(struct team_port *port);
+static inline struct team_port *
+team_get_first_port_txable_rcu(struct team *team, struct team_port *port)
+{
+       struct team_port *cur;
+
+       if (likely(team_port_txable(port)))
+               return port;
+       cur = port;
+       list_for_each_entry_continue_rcu(cur, &team->port_list, list)
+               if (team_port_txable(port))
+                       return cur;
+       list_for_each_entry_rcu(cur, &team->port_list, list) {
+               if (cur == port)
+                       break;
+               if (team_port_txable(port))
+                       return cur;
+       }
+       return NULL;
+}
+
 extern int team_options_register(struct team *team,
                                 const struct team_option *option,
                                 size_t option_count);
index 218a3b686d90600f313d1e423a752b9cc36a9dad..52bd03b389625c4f449052d021547712a0022cbe 100644 (file)
@@ -86,15 +86,15 @@ static inline int is_vlan_dev(struct net_device *dev)
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
 
 extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev,
-                                              u16 vlan_id);
+                                              __be16 vlan_proto, u16 vlan_id);
 extern struct net_device *vlan_dev_real_dev(const struct net_device *dev);
 extern u16 vlan_dev_vlan_id(const struct net_device *dev);
 
 extern bool vlan_do_receive(struct sk_buff **skb);
 extern struct sk_buff *vlan_untag(struct sk_buff *skb);
 
-extern int vlan_vid_add(struct net_device *dev, unsigned short vid);
-extern void vlan_vid_del(struct net_device *dev, unsigned short vid);
+extern int vlan_vid_add(struct net_device *dev, __be16 proto, u16 vid);
+extern void vlan_vid_del(struct net_device *dev, __be16 proto, u16 vid);
 
 extern int vlan_vids_add_by_dev(struct net_device *dev,
                                const struct net_device *by_dev);
@@ -104,7 +104,8 @@ extern void vlan_vids_del_by_dev(struct net_device *dev,
 extern bool vlan_uses_dev(const struct net_device *dev);
 #else
 static inline struct net_device *
-__vlan_find_dev_deep(struct net_device *real_dev, u16 vlan_id)
+__vlan_find_dev_deep(struct net_device *real_dev,
+                    __be16 vlan_proto, u16 vlan_id)
 {
        return NULL;
 }
@@ -131,12 +132,12 @@ static inline struct sk_buff *vlan_untag(struct sk_buff *skb)
        return skb;
 }
 
-static inline int vlan_vid_add(struct net_device *dev, unsigned short vid)
+static inline int vlan_vid_add(struct net_device *dev, __be16 proto, u16 vid)
 {
        return 0;
 }
 
-static inline void vlan_vid_del(struct net_device *dev, unsigned short vid)
+static inline void vlan_vid_del(struct net_device *dev, __be16 proto, u16 vid)
 {
 }
 
@@ -157,9 +158,20 @@ static inline bool vlan_uses_dev(const struct net_device *dev)
 }
 #endif
 
+static inline bool vlan_hw_offload_capable(netdev_features_t features,
+                                          __be16 proto)
+{
+       if (proto == htons(ETH_P_8021Q) && features & NETIF_F_HW_VLAN_CTAG_TX)
+               return true;
+       if (proto == htons(ETH_P_8021AD) && features & NETIF_F_HW_VLAN_STAG_TX)
+               return true;
+       return false;
+}
+
 /**
  * vlan_insert_tag - regular VLAN tag inserting
  * @skb: skbuff to tag
+ * @vlan_proto: VLAN encapsulation protocol
  * @vlan_tci: VLAN TCI to insert
  *
  * Inserts the VLAN tag into @skb as part of the payload
@@ -170,7 +182,8 @@ static inline bool vlan_uses_dev(const struct net_device *dev)
  *
  * Does not change skb->protocol so this function can be used during receive.
  */
-static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb, u16 vlan_tci)
+static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
+                                             __be16 vlan_proto, u16 vlan_tci)
 {
        struct vlan_ethhdr *veth;
 
@@ -185,7 +198,7 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb, u16 vlan_tci)
        skb->mac_header -= VLAN_HLEN;
 
        /* first, the ethernet type */
-       veth->h_vlan_proto = htons(ETH_P_8021Q);
+       veth->h_vlan_proto = vlan_proto;
 
        /* now, the TCI */
        veth->h_vlan_TCI = htons(vlan_tci);
@@ -204,24 +217,28 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb, u16 vlan_tci)
  * Following the skb_unshare() example, in case of error, the calling function
  * doesn't have to worry about freeing the original skb.
  */
-static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci)
+static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb,
+                                            __be16 vlan_proto, u16 vlan_tci)
 {
-       skb = vlan_insert_tag(skb, vlan_tci);
+       skb = vlan_insert_tag(skb, vlan_proto, vlan_tci);
        if (skb)
-               skb->protocol = htons(ETH_P_8021Q);
+               skb->protocol = vlan_proto;
        return skb;
 }
 
 /**
  * __vlan_hwaccel_put_tag - hardware accelerated VLAN inserting
  * @skb: skbuff to tag
+ * @vlan_proto: VLAN encapsulation protocol
  * @vlan_tci: VLAN TCI to insert
  *
  * Puts the VLAN TCI in @skb->vlan_tci and lets the device do the rest
  */
 static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb,
+                                                    __be16 vlan_proto,
                                                     u16 vlan_tci)
 {
+       skb->vlan_proto = vlan_proto;
        skb->vlan_tci = VLAN_TAG_PRESENT | vlan_tci;
        return skb;
 }
@@ -236,12 +253,13 @@ static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb,
  * Assumes skb->dev is the target that will xmit this frame.
  * Returns a VLAN tagged skb.
  */
-static inline struct sk_buff *vlan_put_tag(struct sk_buff *skb, u16 vlan_tci)
+static inline struct sk_buff *vlan_put_tag(struct sk_buff *skb,
+                                          __be16 vlan_proto, u16 vlan_tci)
 {
-       if (skb->dev->features & NETIF_F_HW_VLAN_TX) {
-               return __vlan_hwaccel_put_tag(skb, vlan_tci);
+       if (vlan_hw_offload_capable(skb->dev->features, vlan_proto)) {
+               return __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci);
        } else {
-               return __vlan_put_tag(skb, vlan_tci);
+               return __vlan_put_tag(skb, vlan_proto, vlan_tci);
        }
 }
 
@@ -256,9 +274,9 @@ static inline int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)
 {
        struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data;
 
-       if (veth->h_vlan_proto != htons(ETH_P_8021Q)) {
+       if (veth->h_vlan_proto != htons(ETH_P_8021Q) &&
+           veth->h_vlan_proto != htons(ETH_P_8021AD))
                return -EINVAL;
-       }
 
        *vlan_tci = ntohs(veth->h_vlan_TCI);
        return 0;
@@ -294,7 +312,7 @@ static inline int __vlan_hwaccel_get_tag(const struct sk_buff *skb,
  */
 static inline int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)
 {
-       if (skb->dev->features & NETIF_F_HW_VLAN_TX) {
+       if (skb->dev->features & NETIF_F_HW_VLAN_CTAG_TX) {
                return __vlan_hwaccel_get_tag(skb, vlan_tci);
        } else {
                return __vlan_get_tag(skb, vlan_tci);
@@ -339,7 +357,7 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb,
         */
 
        proto = vhdr->h_vlan_encapsulated_proto;
-       if (ntohs(proto) >= 1536) {
+       if (ntohs(proto) >= ETH_P_802_3_MIN) {
                skb->protocol = proto;
                return;
        }
index e83512f63df550a64cba3fb74c51c5c3b5dc9030..bbca12804d12434ebfb100487c460c2ed21f99ed 100644 (file)
@@ -330,6 +330,24 @@ static inline ktime_t ktime_sub_us(const ktime_t kt, const u64 usec)
 
 extern ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs);
 
+/**
+ * ktime_to_timespec_cond - convert a ktime_t variable to timespec
+ *                         format only if the variable contains data
+ * @kt:                the ktime_t variable to convert
+ * @ts:                the timespec variable to store the result in
+ *
+ * Returns true if there was a successful conversion, false if kt was 0.
+ */
+static inline bool ktime_to_timespec_cond(const ktime_t kt, struct timespec *ts)
+{
+       if (kt.tv64) {
+               *ts = ktime_to_timespec(kt);
+               return true;
+       } else {
+               return false;
+       }
+}
+
 /*
  * The resolution of the clocks. The resolution value is returned in
  * the clock_getres() system call to give application programmers an
index 9dbb41a4e2504632174803d1f31f4c49c61d160d..8752dbbc6135e448cdc0fb66318c7e0a5104a274 100644 (file)
@@ -19,6 +19,7 @@
 #define PHY_ID_KSZ9021         0x00221610
 #define PHY_ID_KS8737          0x00221720
 #define PHY_ID_KSZ8021         0x00221555
+#define PHY_ID_KSZ8031         0x00221556
 #define PHY_ID_KSZ8041         0x00221510
 #define PHY_ID_KSZ8051         0x00221550
 /* same id: ks8001 Rev. A/B, and ks8721 Rev 3. */
index 811f91cf5e8cbf19dd15449060b3336d7e94b8fe..1bc5a750b330ce1d84f12265a4e0b9f26d397d28 100644 (file)
@@ -140,6 +140,7 @@ enum {
        MLX4_DEV_CAP_FLAG_VEP_UC_STEER  = 1LL << 41,
        MLX4_DEV_CAP_FLAG_VEP_MC_STEER  = 1LL << 42,
        MLX4_DEV_CAP_FLAG_COUNTERS      = 1LL << 48,
+       MLX4_DEV_CAP_FLAG_SET_ETH_SCHED = 1LL << 53,
        MLX4_DEV_CAP_FLAG_SENSE_SUPPORT = 1LL << 55,
        MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV = 1LL << 59,
        MLX4_DEV_CAP_FLAG_64B_EQE       = 1LL << 61,
index 49258e0ed1c679e4dfacc488b8e94aa49e7d815e..141d395bbb5f1a215483c90b2a10d5bd0bc58ff7 100644 (file)
@@ -19,7 +19,6 @@
 
 struct mv643xx_eth_shared_platform_data {
        struct mbus_dram_target_info    *dram;
-       struct platform_device  *shared_smi;
        /*
         * Max packet size for Tx IP/Layer 4 checksum, when set to 0, default
         * limit of 9KiB will be used.
index 3dd39340430e6b0ff369d8f10642d1e9f2301f26..cbaa027ef5a772e583593b6b3ab538dd3feff62a 100644 (file)
@@ -22,9 +22,12 @@ enum {
        NETIF_F_IPV6_CSUM_BIT,          /* Can checksum TCP/UDP over IPV6 */
        NETIF_F_HIGHDMA_BIT,            /* Can DMA to high memory. */
        NETIF_F_FRAGLIST_BIT,           /* Scatter/gather IO. */
-       NETIF_F_HW_VLAN_TX_BIT,         /* Transmit VLAN hw acceleration */
-       NETIF_F_HW_VLAN_RX_BIT,         /* Receive VLAN hw acceleration */
-       NETIF_F_HW_VLAN_FILTER_BIT,     /* Receive filtering on VLAN */
+       NETIF_F_HW_VLAN_CTAG_TX_BIT,    /* Transmit VLAN CTAG HW acceleration */
+       NETIF_F_HW_VLAN_CTAG_RX_BIT,    /* Receive VLAN CTAG HW acceleration */
+       NETIF_F_HW_VLAN_CTAG_FILTER_BIT,/* Receive filtering on VLAN CTAGs */
+       NETIF_F_HW_VLAN_STAG_TX_BIT,    /* Transmit VLAN STAG HW acceleration */
+       NETIF_F_HW_VLAN_STAG_RX_BIT,    /* Receive VLAN STAG HW acceleration */
+       NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */
        NETIF_F_VLAN_CHALLENGED_BIT,    /* Device cannot handle VLAN packets */
        NETIF_F_GSO_BIT,                /* Enable software GSO. */
        NETIF_F_LLTX_BIT,               /* LockLess TX - deprecated. Please */
@@ -42,9 +45,9 @@ enum {
        NETIF_F_TSO6_BIT,               /* ... TCPv6 segmentation */
        NETIF_F_FSO_BIT,                /* ... FCoE segmentation */
        NETIF_F_GSO_GRE_BIT,            /* ... GRE with TSO */
-       /**/NETIF_F_GSO_LAST,           /* [can't be last bit, see GSO_MASK] */
-       NETIF_F_GSO_RESERVED2           /* ... free (fill GSO_MASK to 8 bits) */
-               = NETIF_F_GSO_LAST,
+       NETIF_F_GSO_UDP_TUNNEL_BIT,     /* ... UDP TUNNEL with TSO */
+       /**/NETIF_F_GSO_LAST =          /* last bit, see GSO_MASK */
+               NETIF_F_GSO_UDP_TUNNEL_BIT,
 
        NETIF_F_FCOE_CRC_BIT,           /* FCoE CRC32 */
        NETIF_F_SCTP_CSUM_BIT,          /* SCTP checksum offload */
@@ -80,9 +83,12 @@ enum {
 #define NETIF_F_GSO_ROBUST     __NETIF_F(GSO_ROBUST)
 #define NETIF_F_HIGHDMA                __NETIF_F(HIGHDMA)
 #define NETIF_F_HW_CSUM                __NETIF_F(HW_CSUM)
-#define NETIF_F_HW_VLAN_FILTER __NETIF_F(HW_VLAN_FILTER)
-#define NETIF_F_HW_VLAN_RX     __NETIF_F(HW_VLAN_RX)
-#define NETIF_F_HW_VLAN_TX     __NETIF_F(HW_VLAN_TX)
+#define NETIF_F_HW_VLAN_CTAG_FILTER __NETIF_F(HW_VLAN_CTAG_FILTER)
+#define NETIF_F_HW_VLAN_CTAG_RX        __NETIF_F(HW_VLAN_CTAG_RX)
+#define NETIF_F_HW_VLAN_CTAG_TX        __NETIF_F(HW_VLAN_CTAG_TX)
+#define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
+#define NETIF_F_HW_VLAN_STAG_RX        __NETIF_F(HW_VLAN_STAG_RX)
+#define NETIF_F_HW_VLAN_STAG_TX        __NETIF_F(HW_VLAN_STAG_TX)
 #define NETIF_F_IP_CSUM                __NETIF_F(IP_CSUM)
 #define NETIF_F_IPV6_CSUM      __NETIF_F(IPV6_CSUM)
 #define NETIF_F_LLTX           __NETIF_F(LLTX)
@@ -102,7 +108,8 @@ enum {
 #define NETIF_F_VLAN_CHALLENGED        __NETIF_F(VLAN_CHALLENGED)
 #define NETIF_F_RXFCS          __NETIF_F(RXFCS)
 #define NETIF_F_RXALL          __NETIF_F(RXALL)
-#define NETIF_F_GRE_GSO                __NETIF_F(GSO_GRE)
+#define NETIF_F_GSO_GRE                __NETIF_F(GSO_GRE)
+#define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
 
 /* Features valid for ethtool to change */
 /* = all defined minus driver/device-class-related */
index 6151e903eef0a4a380ca03cb24f26e5121543758..f8898a435dc51609097aedc4a4ad3954d26a71f8 100644 (file)
@@ -144,8 +144,6 @@ static inline bool dev_xmit_complete(int rc)
 # else
 #  define LL_MAX_HEADER 96
 # endif
-#elif IS_ENABLED(CONFIG_TR)
-# define LL_MAX_HEADER 48
 #else
 # define LL_MAX_HEADER 32
 #endif
@@ -211,6 +209,7 @@ struct netdev_hw_addr {
 #define NETDEV_HW_ADDR_T_UNICAST       4
 #define NETDEV_HW_ADDR_T_MULTICAST     5
        bool                    global_use;
+       int                     sync_cnt;
        int                     refcount;
        int                     synced;
        struct rcu_head         rcu_head;
@@ -785,13 +784,13 @@ struct netdev_fcoe_hbainfo {
  *     3. Update dev->stats asynchronously and atomically, and define
  *        neither operation.
  *
- * int (*ndo_vlan_rx_add_vid)(struct net_device *dev, unsigned short vid);
- *     If device support VLAN filtering (dev->features & NETIF_F_HW_VLAN_FILTER)
- *     this function is called when a VLAN id is registered.
+ * int (*ndo_vlan_rx_add_vid)(struct net_device *dev, __be16 proto, u16t vid);
+ *     If device support VLAN filtering this function is called when a
+ *     VLAN id is registered.
  *
  * int (*ndo_vlan_rx_kill_vid)(struct net_device *dev, unsigned short vid);
- *     If device support VLAN filtering (dev->features & NETIF_F_HW_VLAN_FILTER)
- *     this function is called when a VLAN id is unregistered.
+ *     If device support VLAN filtering this function is called when a
+ *     VLAN id is unregistered.
  *
  * void (*ndo_poll_controller)(struct net_device *dev);
  *
@@ -935,9 +934,9 @@ struct net_device_ops {
        struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
 
        int                     (*ndo_vlan_rx_add_vid)(struct net_device *dev,
-                                                      unsigned short vid);
+                                                      __be16 proto, u16 vid);
        int                     (*ndo_vlan_rx_kill_vid)(struct net_device *dev,
-                                                       unsigned short vid);
+                                                       __be16 proto, u16 vid);
 #ifdef CONFIG_NET_POLL_CONTROLLER
        void                    (*ndo_poll_controller)(struct net_device *dev);
        int                     (*ndo_netpoll_setup)(struct net_device *dev,
@@ -1073,6 +1072,8 @@ struct net_device {
        struct list_head        dev_list;
        struct list_head        napi_list;
        struct list_head        unreg_list;
+       struct list_head        upper_dev_list; /* List of upper devices */
+
 
        /* currently active device features */
        netdev_features_t       features;
@@ -1145,6 +1146,13 @@ struct net_device {
        spinlock_t              addr_list_lock;
        struct netdev_hw_addr_list      uc;     /* Unicast mac addresses */
        struct netdev_hw_addr_list      mc;     /* Multicast mac addresses */
+       struct netdev_hw_addr_list      dev_addrs; /* list of device
+                                                   * hw addresses
+                                                   */
+#ifdef CONFIG_SYSFS
+       struct kset             *queues_kset;
+#endif
+
        bool                    uc_promisc;
        unsigned int            promiscuity;
        unsigned int            allmulti;
@@ -1177,21 +1185,11 @@ struct net_device {
                                                 * avoid dirtying this cache line.
                                                 */
 
-       struct list_head        upper_dev_list; /* List of upper devices */
-
        /* Interface address info used in eth_type_trans() */
        unsigned char           *dev_addr;      /* hw address, (before bcast
                                                   because most packets are
                                                   unicast) */
 
-       struct netdev_hw_addr_list      dev_addrs; /* list of device
-                                                     hw addresses */
-
-       unsigned char           broadcast[MAX_ADDR_LEN];        /* hw bcast add */
-
-#ifdef CONFIG_SYSFS
-       struct kset             *queues_kset;
-#endif
 
 #ifdef CONFIG_RPS
        struct netdev_rx_queue  *_rx;
@@ -1202,18 +1200,14 @@ struct net_device {
        /* Number of RX queues currently active in device */
        unsigned int            real_num_rx_queues;
 
-#ifdef CONFIG_RFS_ACCEL
-       /* CPU reverse-mapping for RX completion interrupts, indexed
-        * by RX queue number.  Assigned by driver.  This must only be
-        * set if the ndo_rx_flow_steer operation is defined. */
-       struct cpu_rmap         *rx_cpu_rmap;
-#endif
 #endif
 
        rx_handler_func_t __rcu *rx_handler;
        void __rcu              *rx_handler_data;
 
        struct netdev_queue __rcu *ingress_queue;
+       unsigned char           broadcast[MAX_ADDR_LEN];        /* hw bcast add */
+
 
 /*
  * Cache lines mostly used on transmit path
@@ -1235,6 +1229,12 @@ struct net_device {
 #ifdef CONFIG_XPS
        struct xps_dev_maps __rcu *xps_maps;
 #endif
+#ifdef CONFIG_RFS_ACCEL
+       /* CPU reverse-mapping for RX completion interrupts, indexed
+        * by RX queue number.  Assigned by driver.  This must only be
+        * set if the ndo_rx_flow_steer operation is defined. */
+       struct cpu_rmap         *rx_cpu_rmap;
+#endif
 
        /* These may be needed for future network-power-down code. */
 
@@ -1475,6 +1475,11 @@ static inline void *netdev_priv(const struct net_device *dev)
  */
 #define SET_NETDEV_DEVTYPE(net, devtype)       ((net)->dev.type = (devtype))
 
+/* Default NAPI poll() weight
+ * Device drivers are strongly advised to not use bigger value
+ */
+#define NAPI_POLL_WEIGHT 64
+
 /**
  *     netif_napi_add - initialize a napi context
  *     @dev:  network device
@@ -1612,6 +1617,9 @@ extern seqcount_t devnet_rename_seq;      /* Device rename seq */
                list_for_each_entry_continue(d, &(net)->dev_base_head, dev_list)
 #define for_each_netdev_continue_rcu(net, d)           \
        list_for_each_entry_continue_rcu(d, &(net)->dev_base_head, dev_list)
+#define for_each_netdev_in_bond_rcu(bond, slave)       \
+               for_each_netdev_rcu(&init_net, slave)   \
+                       if (netdev_master_upper_dev_get_rcu(slave) == bond)
 #define net_device_entry(lh)   list_entry(lh, struct net_device, dev_list)
 
 static inline struct net_device *next_net_device(struct net_device *dev)
@@ -1684,7 +1692,6 @@ extern int                netdev_refcnt_read(const struct net_device *dev);
 extern void            free_netdev(struct net_device *dev);
 extern void            synchronize_net(void);
 extern int             init_dummy_netdev(struct net_device *dev);
-extern void            netdev_resync_ops(struct net_device *dev);
 
 extern struct net_device       *dev_get_by_index(struct net *net, int ifindex);
 extern struct net_device       *__dev_get_by_index(struct net *net, int ifindex);
@@ -2621,6 +2628,7 @@ extern int dev_uc_add(struct net_device *dev, const unsigned char *addr);
 extern int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr);
 extern int dev_uc_del(struct net_device *dev, const unsigned char *addr);
 extern int dev_uc_sync(struct net_device *to, struct net_device *from);
+extern int dev_uc_sync_multiple(struct net_device *to, struct net_device *from);
 extern void dev_uc_unsync(struct net_device *to, struct net_device *from);
 extern void dev_uc_flush(struct net_device *dev);
 extern void dev_uc_init(struct net_device *dev);
@@ -2632,6 +2640,7 @@ extern int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr);
 extern int dev_mc_del(struct net_device *dev, const unsigned char *addr);
 extern int dev_mc_del_global(struct net_device *dev, const unsigned char *addr);
 extern int dev_mc_sync(struct net_device *to, struct net_device *from);
+extern int dev_mc_sync_multiple(struct net_device *to, struct net_device *from);
 extern void dev_mc_unsync(struct net_device *to, struct net_device *from);
 extern void dev_mc_flush(struct net_device *dev);
 extern void dev_mc_init(struct net_device *dev);
@@ -2678,6 +2687,19 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features)
 {
        return __skb_gso_segment(skb, features, true);
 }
+__be16 skb_network_protocol(struct sk_buff *skb);
+
+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)));
+}
 
 #ifdef CONFIG_BUG
 extern void netdev_rx_csum_fault(struct net_device *dev);
@@ -2756,6 +2778,11 @@ static inline void netif_set_gso_max_size(struct net_device *dev,
        dev->gso_max_size = size;
 }
 
+static inline bool netif_is_bond_master(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)
 {
        return dev->flags & IFF_SLAVE && dev->priv_flags & IFF_BONDING;
index ee142846f56a80882243b2dfc4468e9fdfbc5d3f..0060fde3160e0d029d29f527b3b4330850ebb356 100644 (file)
@@ -289,11 +289,6 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
 #endif
 }
 
-#ifdef CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-extern struct proc_dir_entry *proc_net_netfilter;
-#endif
-
 #else /* !CONFIG_NETFILTER */
 #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
 #define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) (okfn)(skb)
index ecbb8e4959128147b359c2749f314775c9226101..cadb7402d7a713fdcedbe4b3cb77c6095854f3ff 100644 (file)
@@ -29,10 +29,13 @@ extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
 extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
 
 extern int nfnetlink_has_listeners(struct net *net, unsigned int group);
-extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group,
-                         int echo, gfp_t flags);
-extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
-extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags);
+extern struct sk_buff *nfnetlink_alloc_skb(struct net *net, unsigned int size,
+                                          u32 dst_portid, gfp_t gfp_mask);
+extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid,
+                         unsigned int group, int echo, gfp_t flags);
+extern int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error);
+extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net,
+                            u32 portid, int flags);
 
 extern void nfnl_lock(__u8 subsys_id);
 extern void nfnl_unlock(__u8 subsys_id);
index e0f746b7b95c5292b03ca073b02ee2aa905de21a..6358da5eeee8f8d89b619557631613762d09c883 100644 (file)
@@ -15,11 +15,18 @@ static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb)
        return (struct nlmsghdr *)skb->data;
 }
 
+enum netlink_skb_flags {
+       NETLINK_SKB_MMAPED      = 0x1,          /* Packet data is mmaped */
+       NETLINK_SKB_TX          = 0x2,          /* Packet was sent by userspace */
+       NETLINK_SKB_DELIVERED   = 0x4,          /* Packet was delivered */
+};
+
 struct netlink_skb_parms {
        struct scm_creds        creds;          /* Skb credentials      */
        __u32                   portid;
        __u32                   dst_group;
-       struct sock             *ssk;
+       __u32                   flags;
+       struct sock             *sk;
 };
 
 #define NETLINK_CB(skb)                (*(struct netlink_skb_parms*)&((skb)->cb))
@@ -57,6 +64,8 @@ extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group)
 extern void netlink_clear_multicast_users(struct sock *sk, unsigned int group);
 extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
 extern int netlink_has_listeners(struct sock *sk, unsigned int group);
+extern struct sk_buff *netlink_alloc_skb(struct sock *ssk, unsigned int size,
+                                        u32 dst_portid, gfp_t gfp_mask);
 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
 extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
                             __u32 group, gfp_t allocation);
index f47464188710e8271e37faaa8a457da95e3533df..61bf53b02779200323e19c9c6671ec6b78cd1fde 100644 (file)
 #include <linux/of.h>
 extern const int of_get_phy_mode(struct device_node *np);
 extern const void *of_get_mac_address(struct device_node *np);
+#else
+static inline const int of_get_phy_mode(struct device_node *np)
+{
+       return -ENODEV;
+}
+
+static inline const void *of_get_mac_address(struct device_node *np)
+{
+       return NULL;
+}
 #endif
 
 #endif /* __LINUX_OF_NET_H */
index d42e174bd0c85860de4521c0c67df289c7e4489e..e6b240b6196cae3a7dad22693af3857849fb0c8f 100644 (file)
 #ifndef _LINUX_OPENVSWITCH_H
 #define _LINUX_OPENVSWITCH_H 1
 
-#include <linux/types.h>
-
-/**
- * struct ovs_header - header for OVS Generic Netlink messages.
- * @dp_ifindex: ifindex of local port for datapath (0 to make a request not
- * specific to a datapath).
- *
- * Attributes following the header are specific to a particular OVS Generic
- * Netlink family, but all of the OVS families use this header.
- */
-
-struct ovs_header {
-       int dp_ifindex;
-};
-
-/* Datapaths. */
-
-#define OVS_DATAPATH_FAMILY  "ovs_datapath"
-#define OVS_DATAPATH_MCGROUP "ovs_datapath"
-#define OVS_DATAPATH_VERSION 0x1
-
-enum ovs_datapath_cmd {
-       OVS_DP_CMD_UNSPEC,
-       OVS_DP_CMD_NEW,
-       OVS_DP_CMD_DEL,
-       OVS_DP_CMD_GET,
-       OVS_DP_CMD_SET
-};
-
-/**
- * enum ovs_datapath_attr - attributes for %OVS_DP_* commands.
- * @OVS_DP_ATTR_NAME: Name of the network device that serves as the "local
- * port".  This is the name of the network device whose dp_ifindex is given in
- * the &struct ovs_header.  Always present in notifications.  Required in
- * %OVS_DP_NEW requests.  May be used as an alternative to specifying
- * dp_ifindex in other requests (with a dp_ifindex of 0).
- * @OVS_DP_ATTR_UPCALL_PID: The Netlink socket in userspace that is initially
- * set on the datapath port (for OVS_ACTION_ATTR_MISS).  Only valid on
- * %OVS_DP_CMD_NEW requests. A value of zero indicates that upcalls should
- * not be sent.
- * @OVS_DP_ATTR_STATS: Statistics about packets that have passed through the
- * datapath.  Always present in notifications.
- *
- * These attributes follow the &struct ovs_header within the Generic Netlink
- * payload for %OVS_DP_* commands.
- */
-enum ovs_datapath_attr {
-       OVS_DP_ATTR_UNSPEC,
-       OVS_DP_ATTR_NAME,       /* name of dp_ifindex netdev */
-       OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */
-       OVS_DP_ATTR_STATS,      /* struct ovs_dp_stats */
-       __OVS_DP_ATTR_MAX
-};
-
-#define OVS_DP_ATTR_MAX (__OVS_DP_ATTR_MAX - 1)
-
-struct ovs_dp_stats {
-       __u64 n_hit;             /* Number of flow table matches. */
-       __u64 n_missed;          /* Number of flow table misses. */
-       __u64 n_lost;            /* Number of misses not sent to userspace. */
-       __u64 n_flows;           /* Number of flows present */
-};
-
-struct ovs_vport_stats {
-       __u64   rx_packets;             /* total packets received       */
-       __u64   tx_packets;             /* total packets transmitted    */
-       __u64   rx_bytes;               /* total bytes received         */
-       __u64   tx_bytes;               /* total bytes transmitted      */
-       __u64   rx_errors;              /* bad packets received         */
-       __u64   tx_errors;              /* packet transmit problems     */
-       __u64   rx_dropped;             /* no space in linux buffers    */
-       __u64   tx_dropped;             /* no space available in linux  */
-};
-
-/* Fixed logical ports. */
-#define OVSP_LOCAL      ((__u16)0)
-
-/* Packet transfer. */
-
-#define OVS_PACKET_FAMILY "ovs_packet"
-#define OVS_PACKET_VERSION 0x1
-
-enum ovs_packet_cmd {
-       OVS_PACKET_CMD_UNSPEC,
-
-       /* Kernel-to-user notifications. */
-       OVS_PACKET_CMD_MISS,    /* Flow table miss. */
-       OVS_PACKET_CMD_ACTION,  /* OVS_ACTION_ATTR_USERSPACE action. */
-
-       /* Userspace commands. */
-       OVS_PACKET_CMD_EXECUTE  /* Apply actions to a packet. */
-};
-
-/**
- * enum ovs_packet_attr - attributes for %OVS_PACKET_* commands.
- * @OVS_PACKET_ATTR_PACKET: Present for all notifications.  Contains the entire
- * packet as received, from the start of the Ethernet header onward.  For
- * %OVS_PACKET_CMD_ACTION, %OVS_PACKET_ATTR_PACKET reflects changes made by
- * actions preceding %OVS_ACTION_ATTR_USERSPACE, but %OVS_PACKET_ATTR_KEY is
- * the flow key extracted from the packet as originally received.
- * @OVS_PACKET_ATTR_KEY: Present for all notifications.  Contains the flow key
- * extracted from the packet as nested %OVS_KEY_ATTR_* attributes.  This allows
- * userspace to adapt its flow setup strategy by comparing its notion of the
- * flow key against the kernel's.
- * @OVS_PACKET_ATTR_ACTIONS: Contains actions for the packet.  Used
- * for %OVS_PACKET_CMD_EXECUTE.  It has nested %OVS_ACTION_ATTR_* attributes.
- * @OVS_PACKET_ATTR_USERDATA: Present for an %OVS_PACKET_CMD_ACTION
- * notification if the %OVS_ACTION_ATTR_USERSPACE action specified an
- * %OVS_USERSPACE_ATTR_USERDATA attribute.
- *
- * These attributes follow the &struct ovs_header within the Generic Netlink
- * payload for %OVS_PACKET_* commands.
- */
-enum ovs_packet_attr {
-       OVS_PACKET_ATTR_UNSPEC,
-       OVS_PACKET_ATTR_PACKET,      /* Packet data. */
-       OVS_PACKET_ATTR_KEY,         /* Nested OVS_KEY_ATTR_* attributes. */
-       OVS_PACKET_ATTR_ACTIONS,     /* Nested OVS_ACTION_ATTR_* attributes. */
-       OVS_PACKET_ATTR_USERDATA,    /* u64 OVS_ACTION_ATTR_USERSPACE arg. */
-       __OVS_PACKET_ATTR_MAX
-};
-
-#define OVS_PACKET_ATTR_MAX (__OVS_PACKET_ATTR_MAX - 1)
-
-/* Virtual ports. */
-
-#define OVS_VPORT_FAMILY  "ovs_vport"
-#define OVS_VPORT_MCGROUP "ovs_vport"
-#define OVS_VPORT_VERSION 0x1
-
-enum ovs_vport_cmd {
-       OVS_VPORT_CMD_UNSPEC,
-       OVS_VPORT_CMD_NEW,
-       OVS_VPORT_CMD_DEL,
-       OVS_VPORT_CMD_GET,
-       OVS_VPORT_CMD_SET
-};
-
-enum ovs_vport_type {
-       OVS_VPORT_TYPE_UNSPEC,
-       OVS_VPORT_TYPE_NETDEV,   /* network device */
-       OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
-       __OVS_VPORT_TYPE_MAX
-};
-
-#define OVS_VPORT_TYPE_MAX (__OVS_VPORT_TYPE_MAX - 1)
-
-/**
- * enum ovs_vport_attr - attributes for %OVS_VPORT_* commands.
- * @OVS_VPORT_ATTR_PORT_NO: 32-bit port number within datapath.
- * @OVS_VPORT_ATTR_TYPE: 32-bit %OVS_VPORT_TYPE_* constant describing the type
- * of vport.
- * @OVS_VPORT_ATTR_NAME: Name of vport.  For a vport based on a network device
- * this is the name of the network device.  Maximum length %IFNAMSIZ-1 bytes
- * plus a null terminator.
- * @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information.
- * @OVS_VPORT_ATTR_UPCALL_PID: The Netlink socket in userspace that
- * OVS_PACKET_CMD_MISS upcalls will be directed to for packets received on
- * this port.  A value of zero indicates that upcalls should not be sent.
- * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for
- * packets sent or received through the vport.
- *
- * These attributes follow the &struct ovs_header within the Generic Netlink
- * payload for %OVS_VPORT_* commands.
- *
- * For %OVS_VPORT_CMD_NEW requests, the %OVS_VPORT_ATTR_TYPE and
- * %OVS_VPORT_ATTR_NAME attributes are required.  %OVS_VPORT_ATTR_PORT_NO is
- * optional; if not specified a free port number is automatically selected.
- * Whether %OVS_VPORT_ATTR_OPTIONS is required or optional depends on the type
- * of vport.
- * and other attributes are ignored.
- *
- * For other requests, if %OVS_VPORT_ATTR_NAME is specified then it is used to
- * look up the vport to operate on; otherwise dp_idx from the &struct
- * ovs_header plus %OVS_VPORT_ATTR_PORT_NO determine the vport.
- */
-enum ovs_vport_attr {
-       OVS_VPORT_ATTR_UNSPEC,
-       OVS_VPORT_ATTR_PORT_NO, /* u32 port number within datapath */
-       OVS_VPORT_ATTR_TYPE,    /* u32 OVS_VPORT_TYPE_* constant. */
-       OVS_VPORT_ATTR_NAME,    /* string name, up to IFNAMSIZ bytes long */
-       OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */
-       OVS_VPORT_ATTR_UPCALL_PID, /* u32 Netlink PID to receive upcalls */
-       OVS_VPORT_ATTR_STATS,   /* struct ovs_vport_stats */
-       __OVS_VPORT_ATTR_MAX
-};
-
-#define OVS_VPORT_ATTR_MAX (__OVS_VPORT_ATTR_MAX - 1)
-
-/* Flows. */
-
-#define OVS_FLOW_FAMILY  "ovs_flow"
-#define OVS_FLOW_MCGROUP "ovs_flow"
-#define OVS_FLOW_VERSION 0x1
-
-enum ovs_flow_cmd {
-       OVS_FLOW_CMD_UNSPEC,
-       OVS_FLOW_CMD_NEW,
-       OVS_FLOW_CMD_DEL,
-       OVS_FLOW_CMD_GET,
-       OVS_FLOW_CMD_SET
-};
-
-struct ovs_flow_stats {
-       __u64 n_packets;         /* Number of matched packets. */
-       __u64 n_bytes;           /* Number of matched bytes. */
-};
-
-enum ovs_key_attr {
-       OVS_KEY_ATTR_UNSPEC,
-       OVS_KEY_ATTR_ENCAP,     /* Nested set of encapsulated attributes. */
-       OVS_KEY_ATTR_PRIORITY,  /* u32 skb->priority */
-       OVS_KEY_ATTR_IN_PORT,   /* u32 OVS dp port number */
-       OVS_KEY_ATTR_ETHERNET,  /* struct ovs_key_ethernet */
-       OVS_KEY_ATTR_VLAN,      /* be16 VLAN TCI */
-       OVS_KEY_ATTR_ETHERTYPE, /* be16 Ethernet type */
-       OVS_KEY_ATTR_IPV4,      /* struct ovs_key_ipv4 */
-       OVS_KEY_ATTR_IPV6,      /* struct ovs_key_ipv6 */
-       OVS_KEY_ATTR_TCP,       /* struct ovs_key_tcp */
-       OVS_KEY_ATTR_UDP,       /* struct ovs_key_udp */
-       OVS_KEY_ATTR_ICMP,      /* struct ovs_key_icmp */
-       OVS_KEY_ATTR_ICMPV6,    /* struct ovs_key_icmpv6 */
-       OVS_KEY_ATTR_ARP,       /* struct ovs_key_arp */
-       OVS_KEY_ATTR_ND,        /* struct ovs_key_nd */
-       OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
-       __OVS_KEY_ATTR_MAX
-};
-
-#define OVS_KEY_ATTR_MAX (__OVS_KEY_ATTR_MAX - 1)
-
-/**
- * enum ovs_frag_type - IPv4 and IPv6 fragment type
- * @OVS_FRAG_TYPE_NONE: Packet is not a fragment.
- * @OVS_FRAG_TYPE_FIRST: Packet is a fragment with offset 0.
- * @OVS_FRAG_TYPE_LATER: Packet is a fragment with nonzero offset.
- *
- * Used as the @ipv4_frag in &struct ovs_key_ipv4 and as @ipv6_frag &struct
- * ovs_key_ipv6.
- */
-enum ovs_frag_type {
-       OVS_FRAG_TYPE_NONE,
-       OVS_FRAG_TYPE_FIRST,
-       OVS_FRAG_TYPE_LATER,
-       __OVS_FRAG_TYPE_MAX
-};
-
-#define OVS_FRAG_TYPE_MAX (__OVS_FRAG_TYPE_MAX - 1)
-
-struct ovs_key_ethernet {
-       __u8     eth_src[6];
-       __u8     eth_dst[6];
-};
-
-struct ovs_key_ipv4 {
-       __be32 ipv4_src;
-       __be32 ipv4_dst;
-       __u8   ipv4_proto;
-       __u8   ipv4_tos;
-       __u8   ipv4_ttl;
-       __u8   ipv4_frag;       /* One of OVS_FRAG_TYPE_*. */
-};
-
-struct ovs_key_ipv6 {
-       __be32 ipv6_src[4];
-       __be32 ipv6_dst[4];
-       __be32 ipv6_label;      /* 20-bits in least-significant bits. */
-       __u8   ipv6_proto;
-       __u8   ipv6_tclass;
-       __u8   ipv6_hlimit;
-       __u8   ipv6_frag;       /* One of OVS_FRAG_TYPE_*. */
-};
-
-struct ovs_key_tcp {
-       __be16 tcp_src;
-       __be16 tcp_dst;
-};
-
-struct ovs_key_udp {
-       __be16 udp_src;
-       __be16 udp_dst;
-};
-
-struct ovs_key_icmp {
-       __u8 icmp_type;
-       __u8 icmp_code;
-};
-
-struct ovs_key_icmpv6 {
-       __u8 icmpv6_type;
-       __u8 icmpv6_code;
-};
-
-struct ovs_key_arp {
-       __be32 arp_sip;
-       __be32 arp_tip;
-       __be16 arp_op;
-       __u8   arp_sha[6];
-       __u8   arp_tha[6];
-};
-
-struct ovs_key_nd {
-       __u32 nd_target[4];
-       __u8  nd_sll[6];
-       __u8  nd_tll[6];
-};
-
-/**
- * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
- * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow
- * key.  Always present in notifications.  Required for all requests (except
- * dumps).
- * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying
- * the actions to take for packets that match the key.  Always present in
- * notifications.  Required for %OVS_FLOW_CMD_NEW requests, optional for
- * %OVS_FLOW_CMD_SET requests.
- * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this
- * flow.  Present in notifications if the stats would be nonzero.  Ignored in
- * requests.
- * @OVS_FLOW_ATTR_TCP_FLAGS: An 8-bit value giving the OR'd value of all of the
- * TCP flags seen on packets in this flow.  Only present in notifications for
- * TCP flows, and only if it would be nonzero.  Ignored in requests.
- * @OVS_FLOW_ATTR_USED: A 64-bit integer giving the time, in milliseconds on
- * the system monotonic clock, at which a packet was last processed for this
- * flow.  Only present in notifications if a packet has been processed for this
- * flow.  Ignored in requests.
- * @OVS_FLOW_ATTR_CLEAR: If present in a %OVS_FLOW_CMD_SET request, clears the
- * last-used time, accumulated TCP flags, and statistics for this flow.
- * Otherwise ignored in requests.  Never present in notifications.
- *
- * These attributes follow the &struct ovs_header within the Generic Netlink
- * payload for %OVS_FLOW_* commands.
- */
-enum ovs_flow_attr {
-       OVS_FLOW_ATTR_UNSPEC,
-       OVS_FLOW_ATTR_KEY,       /* Sequence of OVS_KEY_ATTR_* attributes. */
-       OVS_FLOW_ATTR_ACTIONS,   /* Nested OVS_ACTION_ATTR_* attributes. */
-       OVS_FLOW_ATTR_STATS,     /* struct ovs_flow_stats. */
-       OVS_FLOW_ATTR_TCP_FLAGS, /* 8-bit OR'd TCP flags. */
-       OVS_FLOW_ATTR_USED,      /* u64 msecs last used in monotonic time. */
-       OVS_FLOW_ATTR_CLEAR,     /* Flag to clear stats, tcp_flags, used. */
-       __OVS_FLOW_ATTR_MAX
-};
-
-#define OVS_FLOW_ATTR_MAX (__OVS_FLOW_ATTR_MAX - 1)
-
-/**
- * enum ovs_sample_attr - Attributes for %OVS_ACTION_ATTR_SAMPLE action.
- * @OVS_SAMPLE_ATTR_PROBABILITY: 32-bit fraction of packets to sample with
- * @OVS_ACTION_ATTR_SAMPLE.  A value of 0 samples no packets, a value of
- * %UINT32_MAX samples all packets and intermediate values sample intermediate
- * fractions of packets.
- * @OVS_SAMPLE_ATTR_ACTIONS: Set of actions to execute in sampling event.
- * Actions are passed as nested attributes.
- *
- * Executes the specified actions with the given probability on a per-packet
- * basis.
- */
-enum ovs_sample_attr {
-       OVS_SAMPLE_ATTR_UNSPEC,
-       OVS_SAMPLE_ATTR_PROBABILITY, /* u32 number */
-       OVS_SAMPLE_ATTR_ACTIONS,     /* Nested OVS_ACTION_ATTR_* attributes. */
-       __OVS_SAMPLE_ATTR_MAX,
-};
-
-#define OVS_SAMPLE_ATTR_MAX (__OVS_SAMPLE_ATTR_MAX - 1)
-
-/**
- * enum ovs_userspace_attr - Attributes for %OVS_ACTION_ATTR_USERSPACE action.
- * @OVS_USERSPACE_ATTR_PID: u32 Netlink PID to which the %OVS_PACKET_CMD_ACTION
- * message should be sent.  Required.
- * @OVS_USERSPACE_ATTR_USERDATA: If present, its u64 argument is copied to the
- * %OVS_PACKET_CMD_ACTION message as %OVS_PACKET_ATTR_USERDATA,
- */
-enum ovs_userspace_attr {
-       OVS_USERSPACE_ATTR_UNSPEC,
-       OVS_USERSPACE_ATTR_PID,       /* u32 Netlink PID to receive upcalls. */
-       OVS_USERSPACE_ATTR_USERDATA,  /* u64 optional user-specified cookie. */
-       __OVS_USERSPACE_ATTR_MAX
-};
-
-#define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
-
-/**
- * struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument.
- * @vlan_tpid: Tag protocol identifier (TPID) to push.
- * @vlan_tci: Tag control identifier (TCI) to push.  The CFI bit must be set
- * (but it will not be set in the 802.1Q header that is pushed).
- *
- * The @vlan_tpid value is typically %ETH_P_8021Q.  The only acceptable TPID
- * values are those that the kernel module also parses as 802.1Q headers, to
- * prevent %OVS_ACTION_ATTR_PUSH_VLAN followed by %OVS_ACTION_ATTR_POP_VLAN
- * from having surprising results.
- */
-struct ovs_action_push_vlan {
-       __be16 vlan_tpid;       /* 802.1Q TPID. */
-       __be16 vlan_tci;        /* 802.1Q TCI (VLAN ID and priority). */
-};
-
-/**
- * enum ovs_action_attr - Action types.
- *
- * @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
- * @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested
- * %OVS_USERSPACE_ATTR_* attributes.
- * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header.  The
- * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its
- * value.
- * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the
- * packet.
- * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet.
- * @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in
- * the nested %OVS_SAMPLE_ATTR_* attributes.
- *
- * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
- * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
- * type may not be changed.
- */
-
-enum ovs_action_attr {
-       OVS_ACTION_ATTR_UNSPEC,
-       OVS_ACTION_ATTR_OUTPUT,       /* u32 port number. */
-       OVS_ACTION_ATTR_USERSPACE,    /* Nested OVS_USERSPACE_ATTR_*. */
-       OVS_ACTION_ATTR_SET,          /* One nested OVS_KEY_ATTR_*. */
-       OVS_ACTION_ATTR_PUSH_VLAN,    /* struct ovs_action_push_vlan. */
-       OVS_ACTION_ATTR_POP_VLAN,     /* No argument. */
-       OVS_ACTION_ATTR_SAMPLE,       /* Nested OVS_SAMPLE_ATTR_*. */
-       __OVS_ACTION_ATTR_MAX
-};
-
-#define OVS_ACTION_ATTR_MAX (__OVS_ACTION_ATTR_MAX - 1)
+#include <uapi/linux/openvswitch.h>
 
 #endif /* _LINUX_OPENVSWITCH_H */
index 33999adbf8c85e91f16651c5d2dfd64ff2b58439..9e11039dd7a3b89c40a19d10ebb3c382aed5dd7e 100644 (file)
@@ -455,6 +455,14 @@ struct phy_driver {
         */
        void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);
 
+       /* Some devices (e.g. qnap TS-119P II) require PHY register changes to
+        * enable Wake on LAN, so set_wol is provided to be called in the
+        * ethernet driver's set_wol function. */
+       int (*set_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
+
+       /* See set_wol, but for checking whether Wake on LAN is enabled. */
+       void (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
+
        struct device_driver driver;
 };
 #define to_phy_driver(d) container_of(d, struct phy_driver, driver)
@@ -560,6 +568,8 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable);
 int phy_get_eee_err(struct phy_device *phydev);
 int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data);
 int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data);
+int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol);
+void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol);
 
 int __init mdio_bus_init(void);
 void mdio_bus_exit(void);
index 798fb80b024b19ec68b73ba74960dc1922c083dd..bb3cd58d71e3fbac50c9aed1afbf7ef0cbeaa108 100644 (file)
@@ -30,7 +30,7 @@ struct cpsw_platform_data {
        u32     channels;       /* number of cpdma channels (symmetric) */
        u32     slaves;         /* number of slave cpgmac ports */
        struct cpsw_slave_data  *slave_data;
-       u32     cpts_active_slave; /* time stamping slave */
+       u32     active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */
        u32     cpts_clock_mult;  /* convert input clock ticks to nanoseconds */
        u32     cpts_clock_shift; /* convert input clock ticks to nanoseconds */
        u32     ale_entries;    /* ale table size */
index 489dd7bb28ecf2920e545bf65cdb29d64aab9bdd..f28544b2f9af02b4dd2ebcfe5f9bf28a088e1f0d 100644 (file)
@@ -69,6 +69,15 @@ extern int ndo_dflt_fdb_dump(struct sk_buff *skb,
                             struct netlink_callback *cb,
                             struct net_device *dev,
                             int idx);
+extern int ndo_dflt_fdb_add(struct ndmsg *ndm,
+                           struct nlattr *tb[],
+                           struct net_device *dev,
+                           const unsigned char *addr,
+                            u16 flags);
+extern int ndo_dflt_fdb_del(struct ndmsg *ndm,
+                           struct nlattr *tb[],
+                           struct net_device *dev,
+                           const unsigned char *addr);
 
 extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
                                   struct net_device *dev, u16 mode);
index c11a28706fa499fd630c7662d6d177f91a074428..3bfe8d6ee248e8e38df18e9b0462678ee76d479f 100644 (file)
@@ -53,7 +53,9 @@
 
 #include <linux/in.h>          /* We need in_addr.  */
 #include <linux/in6.h>         /* We need in6_addr.  */
+#include <linux/skbuff.h>
 
+#include <uapi/linux/sctp.h>
 
 /* Section 3.1.  SCTP Common Header Format */
 typedef struct sctphdr {
@@ -63,14 +65,10 @@ typedef struct sctphdr {
        __le32 checksum;
 } __packed sctp_sctphdr_t;
 
-#ifdef __KERNEL__
-#include <linux/skbuff.h>
-
 static inline struct sctphdr *sctp_hdr(const struct sk_buff *skb)
 {
        return (struct sctphdr *)skb_transport_header(skb);
 }
-#endif
 
 /* Section 3.2.  Chunk Field Descriptions. */
 typedef struct sctp_chunkhdr {
index b17d765ded84cc35cb56d066e9f55b683533a8c0..fc305713fc6d4e09d05ef30dd1be1ce24407cfd4 100644 (file)
@@ -6,6 +6,7 @@
 enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN};
 enum {
        SH_ETH_REG_GIGABIT,
+       SH_ETH_REG_FAST_RCAR,
        SH_ETH_REG_FAST_SH4,
        SH_ETH_REG_FAST_SH3_SH2
 };
index b8292d8cc9fa6238ed0691194f714f827cabd058..2e0ced1af3b10458f4507560bd9c703c2661425f 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/hrtimer.h>
 #include <linux/dma-mapping.h>
 #include <linux/netdev_features.h>
+#include <net/flow_keys.h>
 
 /* Don't change this without changing skb_csum_unnecessary! */
 #define CHECKSUM_NONE 0
@@ -316,6 +317,8 @@ enum {
        SKB_GSO_FCOE = 1 << 5,
 
        SKB_GSO_GRE = 1 << 6,
+
+       SKB_GSO_UDP_TUNNEL = 1 << 7,
 };
 
 #if BITS_PER_LONG > 32
@@ -384,9 +387,11 @@ typedef unsigned char *sk_buff_data_t;
  *     @secmark: security marking
  *     @mark: Generic packet mark
  *     @dropcount: total number of sk_receive_queue overflows
+ *     @vlan_proto: vlan encapsulation protocol
  *     @vlan_tci: vlan tag control information
  *     @inner_transport_header: Inner transport layer header (encapsulation)
  *     @inner_network_header: Network layer header (encapsulation)
+ *     @inner_mac_header: Link layer header (encapsulation)
  *     @transport_header: Transport layer header
  *     @network_header: Network layer header
  *     @mac_header: Link layer header
@@ -461,6 +466,7 @@ struct sk_buff {
 
        __u32                   rxhash;
 
+       __be16                  vlan_proto;
        __u16                   vlan_tci;
 
 #ifdef CONFIG_NET_SCHED
@@ -505,6 +511,7 @@ struct sk_buff {
 
        sk_buff_data_t          inner_transport_header;
        sk_buff_data_t          inner_network_header;
+       sk_buff_data_t          inner_mac_header;
        sk_buff_data_t          transport_header;
        sk_buff_data_t          network_header;
        sk_buff_data_t          mac_header;
@@ -570,7 +577,40 @@ static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst)
        skb->_skb_refdst = (unsigned long)dst;
 }
 
-extern void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst);
+extern void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst,
+                               bool force);
+
+/**
+ * skb_dst_set_noref - sets skb dst, hopefully, without taking reference
+ * @skb: buffer
+ * @dst: dst entry
+ *
+ * Sets skb dst, assuming a reference was not taken on dst.
+ * If dst entry is cached, we do not take reference and dst_release
+ * will be avoided by refdst_drop. If dst entry is not cached, we take
+ * reference, so that last dst_release can destroy the dst immediately.
+ */
+static inline void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst)
+{
+       __skb_dst_set_noref(skb, dst, false);
+}
+
+/**
+ * skb_dst_set_noref_force - sets skb dst, without taking reference
+ * @skb: buffer
+ * @dst: dst entry
+ *
+ * Sets skb dst, assuming a reference was not taken on dst.
+ * No reference is taken and no dst_release will be called. While for
+ * cached dsts deferred reclaim is a basic feature, for entries that are
+ * not cached it is caller's job to guarantee that last dst_release for
+ * provided dst happens when nobody uses it, eg. after a RCU grace period.
+ */
+static inline void skb_dst_set_noref_force(struct sk_buff *skb,
+                                          struct dst_entry *dst)
+{
+       __skb_dst_set_noref(skb, dst, true);
+}
 
 /**
  * skb_dst_is_noref - Test if skb dst isn't refcounted
@@ -611,6 +651,12 @@ static inline struct sk_buff *alloc_skb_fclone(unsigned int size,
        return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE);
 }
 
+extern struct sk_buff *__alloc_skb_head(gfp_t priority, int node);
+static inline struct sk_buff *alloc_skb_head(gfp_t priority)
+{
+       return __alloc_skb_head(priority, -1);
+}
+
 extern struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src);
 extern int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask);
 extern struct sk_buff *skb_clone(struct sk_buff *skb,
@@ -1471,6 +1517,7 @@ static inline void skb_reserve(struct sk_buff *skb, int len)
 
 static inline void skb_reset_inner_headers(struct sk_buff *skb)
 {
+       skb->inner_mac_header = skb->mac_header;
        skb->inner_network_header = skb->network_header;
        skb->inner_transport_header = skb->transport_header;
 }
@@ -1516,6 +1563,22 @@ static inline void skb_set_inner_network_header(struct sk_buff *skb,
        skb->inner_network_header += offset;
 }
 
+static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
+{
+       return skb->head + skb->inner_mac_header;
+}
+
+static inline void skb_reset_inner_mac_header(struct sk_buff *skb)
+{
+       skb->inner_mac_header = skb->data - skb->head;
+}
+
+static inline void skb_set_inner_mac_header(struct sk_buff *skb,
+                                           const int offset)
+{
+       skb_reset_inner_mac_header(skb);
+       skb->inner_mac_header += offset;
+}
 static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
 {
        return skb->transport_header != ~0U;
@@ -1609,6 +1672,21 @@ static inline void skb_set_inner_network_header(struct sk_buff *skb,
        skb->inner_network_header = skb->data + offset;
 }
 
+static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
+{
+       return skb->inner_mac_header;
+}
+
+static inline void skb_reset_inner_mac_header(struct sk_buff *skb)
+{
+       skb->inner_mac_header = skb->data;
+}
+
+static inline void skb_set_inner_mac_header(struct sk_buff *skb,
+                                               const int offset)
+{
+       skb->inner_mac_header = skb->data + offset;
+}
 static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
 {
        return skb->transport_header != NULL;
@@ -1666,6 +1744,19 @@ static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
 }
 #endif /* NET_SKBUFF_DATA_USES_OFFSET */
 
+static inline void skb_probe_transport_header(struct sk_buff *skb,
+                                             const int offset_hint)
+{
+       struct flow_keys keys;
+
+       if (skb_transport_header_was_set(skb))
+               return;
+       else if (skb_flow_dissect(skb, &keys))
+               skb_set_transport_header(skb, keys.thoff);
+       else
+               skb_set_transport_header(skb, offset_hint);
+}
+
 static inline void skb_mac_header_rebuild(struct sk_buff *skb)
 {
        if (skb_mac_header_was_set(skb)) {
@@ -2811,6 +2902,8 @@ static inline void skb_checksum_none_assert(const struct sk_buff *skb)
 
 bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off);
 
+u32 __skb_get_poff(const struct sk_buff *skb);
+
 /**
  * skb_head_is_locked - Determine if the skb->head is locked down
  * @skb: skb to check
index 2b9f74b0ffea5bf34ea8e38140938055d0a79ec4..428c37a1f95ce2eea8d9785ef69fc6050b5b7c96 100644 (file)
@@ -298,6 +298,7 @@ struct ucred {
 #define SOL_IUCV       277
 #define SOL_CAIF       278
 #define SOL_ALG                279
+#define SOL_NFC                280
 
 /* IPX options */
 #define IPX_TYPE       1
index b2b1afbb32024ebbb80be196ea276a7ff53ae43e..aa327a8105ada0d2502d697c6f66699e8f8c516b 100644 (file)
@@ -26,6 +26,20 @@ struct at86rf230_platform_data {
        int rstn;
        int slp_tr;
        int dig2;
+
+       /* Setting the irq_type will configure the driver to request
+        * the platform irq trigger type according to the given value
+        * and configure the interrupt polarity of the device to the
+        * corresponding polarity.
+        *
+        * Allowed values are: IRQF_TRIGGER_RISING, IRQF_TRIGGER_FALLING,
+        *                     IRQF_TRIGGER_HIGH and IRQF_TRIGGER_LOW
+        *
+        * Setting it to 0, the driver does not touch the trigger type
+        * configuration of the interrupt and sets the interrupt polarity
+        * of the device to high active (the default value).
+        */
+       int irq_type;
 };
 
 #endif
index 22958d68ecfe19016e721eb552cd5f901bbbbf89..c64999fd1660093c5719fdd93c60ba4c6545c701 100644 (file)
@@ -26,9 +26,9 @@ struct ssb_sprom_core_pwr_info {
 
 struct ssb_sprom {
        u8 revision;
-       u8 il0mac[6];           /* MAC address for 802.11b/g */
-       u8 et0mac[6];           /* MAC address for Ethernet */
-       u8 et1mac[6];           /* MAC address for 802.11a */
+       u8 il0mac[6] __aligned(sizeof(u16));    /* MAC address for 802.11b/g */
+       u8 et0mac[6] __aligned(sizeof(u16));    /* MAC address for Ethernet */
+       u8 et1mac[6] __aligned(sizeof(u16));    /* MAC address for 802.11a */
        u8 et0phyaddr;          /* MII address for enet0 */
        u8 et1phyaddr;          /* MII address for enet1 */
        u8 et0mdcport;          /* MDIO for enet0 */
@@ -340,13 +340,61 @@ enum ssb_bustype {
 #define SSB_BOARDVENDOR_DELL   0x1028  /* Dell */
 #define SSB_BOARDVENDOR_HP     0x0E11  /* HP */
 /* board_type */
+#define SSB_BOARD_BCM94301CB   0x0406
+#define SSB_BOARD_BCM94301MP   0x0407
+#define SSB_BOARD_BU4309       0x040A
+#define SSB_BOARD_BCM94309CB   0x040B
+#define SSB_BOARD_BCM4309MP    0x040C
+#define SSB_BOARD_BU4306       0x0416
 #define SSB_BOARD_BCM94306MP   0x0418
 #define SSB_BOARD_BCM4309G     0x0421
 #define SSB_BOARD_BCM4306CB    0x0417
-#define SSB_BOARD_BCM4309MP    0x040C
+#define SSB_BOARD_BCM94306PC   0x0425  /* pcmcia 3.3v 4306 card */
+#define SSB_BOARD_BCM94306CBSG 0x042B  /* with SiGe PA */
+#define SSB_BOARD_PCSG94306    0x042D  /* with SiGe PA */
+#define SSB_BOARD_BU4704SD     0x042E  /* with sdram */
+#define SSB_BOARD_BCM94704AGR  0x042F  /* dual 11a/11g Router */
+#define SSB_BOARD_BCM94308MP   0x0430  /* 11a-only minipci */
+#define SSB_BOARD_BU4318       0x0447
+#define SSB_BOARD_CB4318       0x0448
+#define SSB_BOARD_MPG4318      0x0449
 #define SSB_BOARD_MP4318       0x044A
-#define SSB_BOARD_BU4306       0x0416
-#define SSB_BOARD_BU4309       0x040A
+#define SSB_BOARD_SD4318       0x044B
+#define SSB_BOARD_BCM94306P    0x044C  /* with SiGe */
+#define SSB_BOARD_BCM94303MP   0x044E
+#define SSB_BOARD_BCM94306MPM  0x0450
+#define SSB_BOARD_BCM94306MPL  0x0453
+#define SSB_BOARD_PC4303       0x0454  /* pcmcia */
+#define SSB_BOARD_BCM94306MPLNA        0x0457
+#define SSB_BOARD_BCM94306MPH  0x045B
+#define SSB_BOARD_BCM94306PCIV 0x045C
+#define SSB_BOARD_BCM94318MPGH 0x0463
+#define SSB_BOARD_BU4311       0x0464
+#define SSB_BOARD_BCM94311MC   0x0465
+#define SSB_BOARD_BCM94311MCAG 0x0466
+/* 4321 boards */
+#define SSB_BOARD_BU4321       0x046B
+#define SSB_BOARD_BU4321E      0x047C
+#define SSB_BOARD_MP4321       0x046C
+#define SSB_BOARD_CB2_4321     0x046D
+#define SSB_BOARD_CB2_4321_AG  0x0066
+#define SSB_BOARD_MC4321       0x046E
+/* 4325 boards */
+#define SSB_BOARD_BCM94325DEVBU        0x0490
+#define SSB_BOARD_BCM94325BGABU        0x0491
+#define SSB_BOARD_BCM94325SDGWB        0x0492
+#define SSB_BOARD_BCM94325SDGMDL       0x04AA
+#define SSB_BOARD_BCM94325SDGMDL2      0x04C6
+#define SSB_BOARD_BCM94325SDGMDL3      0x04C9
+#define SSB_BOARD_BCM94325SDABGWBA     0x04E1
+/* 4322 boards */
+#define SSB_BOARD_BCM94322MC   0x04A4
+#define SSB_BOARD_BCM94322USB  0x04A8  /* dualband */
+#define SSB_BOARD_BCM94322HM   0x04B0
+#define SSB_BOARD_BCM94322USB2D        0x04Bf  /* single band discrete front end */
+/* 4312 boards */
+#define SSB_BOARD_BU4312       0x048A
+#define SSB_BOARD_BCM4312MCGSG 0x04B5
 /* chip_package */
 #define SSB_CHIPPACK_BCM4712S  1       /* Small 200pin 4712 */
 #define SSB_CHIPPACK_BCM4712M  2       /* Medium 225pin 4712 */
index 6ecfa02ddbac9e4917725938efca0ca37b07da64..3a7256955b10b75a03fe6873f197a5615a44b0a7 100644 (file)
 #define  SSB_SPROM4_ETHPHY_ET1A_SHIFT  5
 #define  SSB_SPROM4_ETHPHY_ET0M                (1<<14) /* MDIO for enet0 */
 #define  SSB_SPROM4_ETHPHY_ET1M                (1<<15) /* MDIO for enet1 */
-#define SSB_SPROM4_ANTAVAIL            0x005D  /* Antenna available bitfields */
-#define  SSB_SPROM4_ANTAVAIL_A         0x00FF  /* A-PHY bitfield */
-#define  SSB_SPROM4_ANTAVAIL_A_SHIFT   0
-#define  SSB_SPROM4_ANTAVAIL_BG                0xFF00  /* B-PHY and G-PHY bitfield */
-#define  SSB_SPROM4_ANTAVAIL_BG_SHIFT  8
+#define SSB_SPROM4_ANTAVAIL            0x005C  /* Antenna available bitfields */
+#define  SSB_SPROM4_ANTAVAIL_BG                0x00FF  /* B-PHY and G-PHY bitfield */
+#define  SSB_SPROM4_ANTAVAIL_BG_SHIFT  0
+#define  SSB_SPROM4_ANTAVAIL_A         0xFF00  /* A-PHY bitfield */
+#define  SSB_SPROM4_ANTAVAIL_A_SHIFT   8
 #define SSB_SPROM4_AGAIN01             0x005E  /* Antenna Gain (in dBm Q5.2) */
 #define  SSB_SPROM4_AGAIN0             0x00FF  /* Antenna 0 */
 #define  SSB_SPROM4_AGAIN0_SHIFT       0
index f28408c07dc268e416f1ad0644bac66b60b5a6a1..5adbc33d1ab38c506c541e244024560c02686057 100644 (file)
@@ -90,9 +90,6 @@ struct tcp_options_received {
                sack_ok : 4,    /* SACK seen on SYN packet              */
                snd_wscale : 4, /* Window scaling received from sender  */
                rcv_wscale : 4; /* Window scaling to send to receiver   */
-       u8      cookie_plus:6,  /* bytes in authenticator/cookie option */
-               cookie_out_never:1,
-               cookie_in_always:1;
        u8      num_sacks;      /* Number of SACK blocks                */
        u16     user_mss;       /* mss requested by user in ioctl       */
        u16     mss_clamp;      /* Maximal mss, negotiated at connection setup */
@@ -102,7 +99,6 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
 {
        rx_opt->tstamp_ok = rx_opt->sack_ok = 0;
        rx_opt->wscale_ok = rx_opt->snd_wscale = 0;
-       rx_opt->cookie_plus = 0;
 }
 
 /* This is the max number of SACKS that we'll generate and process. It's safe
@@ -191,20 +187,19 @@ struct tcp_sock {
        u32     window_clamp;   /* Maximal window to advertise          */
        u32     rcv_ssthresh;   /* Current window clamp                 */
 
-       u32     frto_highmark;  /* snd_nxt when RTO occurred */
        u16     advmss;         /* Advertised MSS                       */
-       u8      frto_counter;   /* Number of new acks after RTO */
+       u8      unused;
        u8      nonagle     : 4,/* Disable Nagle algorithm?             */
                thin_lto    : 1,/* Use linear timeouts for thin streams */
                thin_dupack : 1,/* Fast retransmit on first dupack      */
                repair      : 1,
-               unused      : 1;
+               frto        : 1;/* F-RTO (RFC5682) activated in CA_Loss */
        u8      repair_queue;
        u8      do_early_retrans:1,/* Enable RFC5827 early-retransmit  */
-               early_retrans_delayed:1, /* Delayed ER timer installed */
                syn_data:1,     /* SYN includes data */
                syn_fastopen:1, /* SYN includes Fast Open option */
                syn_data_acked:1;/* data in SYN is acked by SYN-ACK */
+       u32     tlp_high_seq;   /* snd_nxt at the time of TLP retransmit. */
 
 /* RTT measurement */
        u32     srtt;           /* smoothed round trip time << 3        */
@@ -320,12 +315,6 @@ struct tcp_sock {
        struct tcp_md5sig_info  __rcu *md5sig_info;
 #endif
 
-       /* When the cookie options are generated and exchanged, then this
-        * object holds a reference to them (cookie_values->kref).  Also
-        * contains related tcp_cookie_transactions fields.
-        */
-       struct tcp_cookie_values  *cookie_values;
-
 /* TCP fastopen related information */
        struct tcp_fastopen_request *fastopen_req;
        /* fastopen_rsk points to request_sock that resulted in this big
@@ -361,10 +350,6 @@ struct tcp_timewait_sock {
 #ifdef CONFIG_TCP_MD5SIG
        struct tcp_md5sig_key     *tw_md5_key;
 #endif
-       /* Few sockets in timewait have cookies; in that case, then this
-        * object holds a reference to them (tw_cookie_values->kref).
-        */
-       struct tcp_cookie_values  *tw_cookie_values;
 };
 
 static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
index 0e5ac93bab101bea0526ee3b54f52ed3b0f72099..da46327fca173044b186943f8c7a044378d186cd 100644 (file)
@@ -72,6 +72,7 @@ struct usbnet {
 #              define EVENT_DEVICE_REPORT_IDLE 8
 #              define EVENT_NO_RUNTIME_PM      9
 #              define EVENT_RX_KILL    10
+#              define EVENT_LINK_CHANGE        11
 };
 
 static inline struct usb_driver *driver_of(struct usb_interface *intf)
@@ -245,5 +246,6 @@ extern void usbnet_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
 extern int usbnet_nway_reset(struct net_device *net);
 
 extern int usbnet_manage_power(struct usbnet *, int);
+extern void usbnet_link_change(struct usbnet *, bool, bool);
 
 #endif /* __LINUX_USB_USBNET_H */
index ff6714e6d0f503f6251ae54856796a06f2314977..2d7a5e0459084bbc10afad02a0968397d88decb9 100644 (file)
@@ -58,12 +58,6 @@ void *virtqueue_detach_unused_buf(struct virtqueue *vq);
 
 unsigned int virtqueue_get_vring_size(struct virtqueue *vq);
 
-/* FIXME: Obsolete accessor, but required for virtio_net merge. */
-static inline unsigned int virtqueue_get_queue_index(struct virtqueue *vq)
-{
-       return vq->index;
-}
-
 /**
  * virtio_device - representation of a device using virtio
  * @index: unique position on the virtio bus
diff --git a/include/linux/vm_sockets.h b/include/linux/vm_sockets.h
new file mode 100644 (file)
index 0000000..0805eec
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * VMware vSockets Driver
+ *
+ * Copyright (C) 2007-2013 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no 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.
+ */
+
+#ifndef _VM_SOCKETS_H
+#define _VM_SOCKETS_H
+
+#include <uapi/linux/vm_sockets.h>
+
+int vm_sockets_get_local_cid(void);
+
+#endif /* _VM_SOCKETS_H */
index 0a996a3517edd2e652bb389ca4093d9928e7b974..a8836e8445ccf412c219b06bf9a9a4371fe959f5 100644 (file)
@@ -29,7 +29,8 @@ struct unix_address {
 
 struct unix_skb_parms {
        struct pid              *pid;           /* Skb credentials      */
-       const struct cred       *cred;
+       kuid_t                  uid;
+       kgid_t                  gid;
        struct scm_fp_list      *fp;            /* Passed files         */
 #ifdef CONFIG_SECURITY_NETWORK
        u32                     secid;          /* Security ID          */
index 9531beee09b55342fec2266e5d9c9bba41248c2d..ed6e9552252e56cba3e4ef7896c91863e900717a 100644 (file)
@@ -232,7 +232,7 @@ struct bt_sock_list {
 };
 
 int  bt_sock_register(int proto, const struct net_proto_family *ops);
-int  bt_sock_unregister(int proto);
+void bt_sock_unregister(int proto);
 void bt_sock_link(struct bt_sock_list *l, struct sock *s);
 void bt_sock_unlink(struct bt_sock_list *l, struct sock *s);
 int  bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
@@ -260,12 +260,22 @@ struct l2cap_ctrl {
        __u8            retries;
 };
 
+struct hci_dev;
+
+typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status);
+
+struct hci_req_ctrl {
+       bool                    start;
+       hci_req_complete_t      complete;
+};
+
 struct bt_skb_cb {
        __u8 pkt_type;
        __u8 incoming;
        __u16 expect;
        __u8 force_active;
        struct l2cap_ctrl control;
+       struct hci_req_ctrl req;
 };
 #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
 
index 7f12c25f1fcaa763a697d80ebf95a06124c3adc8..b3308927a0a1b1b6aca6bb4b0483f797e414c673 100644 (file)
@@ -119,10 +119,16 @@ enum {
        HCI_CONNECTABLE,
        HCI_DISCOVERABLE,
        HCI_LINK_SECURITY,
-       HCI_PENDING_CLASS,
        HCI_PERIODIC_INQ,
+       HCI_FAST_CONNECTABLE,
 };
 
+/* A mask for the flags that are supposed to remain when a reset happens
+ * or the HCI device is closed.
+ */
+#define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \
+                             BIT(HCI_FAST_CONNECTABLE))
+
 /* HCI ioctl defines */
 #define HCIDEVUP       _IOW('H', 201, int)
 #define HCIDEVDOWN     _IOW('H', 202, int)
@@ -881,12 +887,25 @@ struct hci_rp_read_data_block_size {
        __le16   num_blocks;
 } __packed;
 
+#define HCI_OP_READ_PAGE_SCAN_ACTIVITY 0x0c1b
+struct hci_rp_read_page_scan_activity {
+       __u8     status;
+       __le16   interval;
+       __le16   window;
+} __packed;
+
 #define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY        0x0c1c
 struct hci_cp_write_page_scan_activity {
        __le16   interval;
        __le16   window;
 } __packed;
 
+#define HCI_OP_READ_PAGE_SCAN_TYPE     0x0c46
+struct hci_rp_read_page_scan_type {
+       __u8     status;
+       __u8     type;
+} __packed;
+
 #define HCI_OP_WRITE_PAGE_SCAN_TYPE    0x0c47
        #define PAGE_SCAN_TYPE_STANDARD         0x00
        #define PAGE_SCAN_TYPE_INTERLACED       0x01
index 90cf75afcb02987165ed4639bcdb169127509d31..358a6983d3bb998024db7deaad1cb578028ff77e 100644 (file)
@@ -165,6 +165,10 @@ struct hci_dev {
        __u16           voice_setting;
        __u8            io_capability;
        __s8            inq_tx_power;
+       __u16           page_scan_interval;
+       __u16           page_scan_window;
+       __u8            page_scan_type;
+
        __u16           devid_source;
        __u16           devid_vendor;
        __u16           devid_product;
@@ -248,8 +252,6 @@ struct hci_dev {
        __u32                   req_status;
        __u32                   req_result;
 
-       __u16                   init_last_cmd;
-
        struct list_head        mgmt_pending;
 
        struct discovery_state  discovery;
@@ -574,7 +576,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
        return NULL;
 }
 
-void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
+void hci_disconnect(struct hci_conn *conn, __u8 reason);
 void hci_setup_sync(struct hci_conn *conn, __u16 handle);
 void hci_sco_setup(struct hci_conn *conn, __u8 status);
 
@@ -742,8 +744,6 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
                                                                u8 *randomizer);
 int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
-int hci_update_ad(struct hci_dev *hdev);
-
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
 int hci_recv_frame(struct sk_buff *skb);
@@ -1041,6 +1041,22 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
 int hci_register_cb(struct hci_cb *hcb);
 int hci_unregister_cb(struct hci_cb *hcb);
 
+struct hci_request {
+       struct hci_dev          *hdev;
+       struct sk_buff_head     cmd_q;
+
+       /* If something goes wrong when building the HCI request, the error
+        * value is stored in this field.
+        */
+       int                     err;
+};
+
+void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
+int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
+void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param);
+void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
+void hci_req_cmd_status(struct hci_dev *hdev, u16 opcode, u8 status);
+
 int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param);
 void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
 void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
@@ -1153,7 +1169,7 @@ struct hci_sec_filter {
 #define hci_req_lock(d)                mutex_lock(&d->req_lock)
 #define hci_req_unlock(d)      mutex_unlock(&d->req_lock)
 
-void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result);
+void hci_update_ad(struct hci_request *req);
 
 void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
                                        u16 latency, u16 to_multiplier);
index e2e3ecad100830a6cb42765c65ca804304eb5fca..7afd4199d6b6cfab22c74fb6bf0544a887bbd5af 100644 (file)
@@ -158,7 +158,6 @@ struct rfcomm_session {
        struct timer_list timer;
        unsigned long    state;
        unsigned long    flags;
-       atomic_t         refcnt;
        int              initiator;
 
        /* Default DLC parameters */
@@ -276,11 +275,6 @@ static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
 void   rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src,
                                                                bdaddr_t *dst);
 
-static inline void rfcomm_session_hold(struct rfcomm_session *s)
-{
-       atomic_inc(&s->refcnt);
-}
-
 /* ---- RFCOMM sockets ---- */
 struct sockaddr_rc {
        sa_family_t     rc_family;
diff --git a/include/net/caif/caif_shm.h b/include/net/caif/caif_shm.h
deleted file mode 100644 (file)
index 5bcce55..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
- * Author: Amarnath Revanna / amarnath.bangalore.revanna@stericsson.com
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#ifndef CAIF_SHM_H_
-#define CAIF_SHM_H_
-
-struct shmdev_layer {
-       u32 shm_base_addr;
-       u32 shm_total_sz;
-       u32 shm_id;
-       u32 shm_loopback;
-       void *hmbx;
-       int (*pshmdev_mbxsend) (u32 shm_id, u32 mbx_msg);
-       int (*pshmdev_mbxsetup) (void *pshmdrv_cb,
-                               struct shmdev_layer *pshm_dev, void *pshm_drv);
-       struct net_device *pshm_netdev;
-};
-
-extern int caif_shmcore_probe(struct shmdev_layer *pshm_dev);
-extern void caif_shmcore_remove(struct net_device *pshm_netdev);
-
-#endif
index d581c6de5d64a19db05d7763766a80bac4802acc..bdba9b619064b9c2fa683c2356821348df6d793d 100644 (file)
@@ -610,23 +610,11 @@ struct cfg80211_ap_settings {
        bool radar_required;
 };
 
-/**
- * enum plink_action - actions to perform in mesh peers
- *
- * @PLINK_ACTION_INVALID: action 0 is reserved
- * @PLINK_ACTION_OPEN: start mesh peer link establishment
- * @PLINK_ACTION_BLOCK: block traffic from this mesh peer
- */
-enum plink_actions {
-       PLINK_ACTION_INVALID,
-       PLINK_ACTION_OPEN,
-       PLINK_ACTION_BLOCK,
-};
-
 /**
  * enum station_parameters_apply_mask - station parameter values to apply
  * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
  * @STATION_PARAM_APPLY_CAPABILITY: apply new capability
+ * @STATION_PARAM_APPLY_PLINK_STATE: apply new plink state
  *
  * Not all station parameters have in-band "no change" signalling,
  * for those that don't these flags will are used.
@@ -634,6 +622,7 @@ enum plink_actions {
 enum station_parameters_apply_mask {
        STATION_PARAM_APPLY_UAPSD = BIT(0),
        STATION_PARAM_APPLY_CAPABILITY = BIT(1),
+       STATION_PARAM_APPLY_PLINK_STATE = BIT(2),
 };
 
 /**
@@ -669,7 +658,7 @@ enum station_parameters_apply_mask {
  * @ext_capab_len: number of extended capabilities
  */
 struct station_parameters {
-       u8 *supported_rates;
+       const u8 *supported_rates;
        struct net_device *vlan;
        u32 sta_flags_mask, sta_flags_set;
        u32 sta_modify_mask;
@@ -678,16 +667,59 @@ struct station_parameters {
        u8 supported_rates_len;
        u8 plink_action;
        u8 plink_state;
-       struct ieee80211_ht_cap *ht_capa;
-       struct ieee80211_vht_cap *vht_capa;
+       const struct ieee80211_ht_cap *ht_capa;
+       const struct ieee80211_vht_cap *vht_capa;
        u8 uapsd_queues;
        u8 max_sp;
        enum nl80211_mesh_power_mode local_pm;
        u16 capability;
-       u8 *ext_capab;
+       const u8 *ext_capab;
        u8 ext_capab_len;
 };
 
+/**
+ * enum cfg80211_station_type - the type of station being modified
+ * @CFG80211_STA_AP_CLIENT: client of an AP interface
+ * @CFG80211_STA_AP_MLME_CLIENT: client of an AP interface that has
+ *     the AP MLME in the device
+ * @CFG80211_STA_AP_STA: AP station on managed interface
+ * @CFG80211_STA_IBSS: IBSS station
+ * @CFG80211_STA_TDLS_PEER_SETUP: TDLS peer on managed interface (dummy entry
+ *     while TDLS setup is in progress, it moves out of this state when
+ *     being marked authorized; use this only if TDLS with external setup is
+ *     supported/used)
+ * @CFG80211_STA_TDLS_PEER_ACTIVE: TDLS peer on managed interface (active
+ *     entry that is operating, has been marked authorized by userspace)
+ * @CFG80211_STA_MESH_PEER_KERNEL: peer on mesh interface (kernel managed)
+ * @CFG80211_STA_MESH_PEER_USER: peer on mesh interface (user managed)
+ */
+enum cfg80211_station_type {
+       CFG80211_STA_AP_CLIENT,
+       CFG80211_STA_AP_MLME_CLIENT,
+       CFG80211_STA_AP_STA,
+       CFG80211_STA_IBSS,
+       CFG80211_STA_TDLS_PEER_SETUP,
+       CFG80211_STA_TDLS_PEER_ACTIVE,
+       CFG80211_STA_MESH_PEER_KERNEL,
+       CFG80211_STA_MESH_PEER_USER,
+};
+
+/**
+ * cfg80211_check_station_change - validate parameter changes
+ * @wiphy: the wiphy this operates on
+ * @params: the new parameters for a station
+ * @statype: the type of station being modified
+ *
+ * Utility function for the @change_station driver method. Call this function
+ * with the appropriate station type looking up the station (and checking that
+ * it exists). It will verify whether the station change is acceptable, and if
+ * not will return an error code. Note that it may modify the parameters for
+ * backward compatibility reasons, so don't use them before calling this.
+ */
+int cfg80211_check_station_change(struct wiphy *wiphy,
+                                 struct station_parameters *params,
+                                 enum cfg80211_station_type statype);
+
 /**
  * enum station_info_flags - station information flags
  *
@@ -1119,6 +1151,7 @@ struct mesh_config {
  * @ie_len: length of vendor information elements
  * @is_authenticated: this mesh requires authentication
  * @is_secure: this mesh uses security
+ * @user_mpm: userspace handles all MPM functions
  * @dtim_period: DTIM period to use
  * @beacon_interval: beacon interval to use
  * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
@@ -1136,6 +1169,7 @@ struct mesh_setup {
        u8 ie_len;
        bool is_authenticated;
        bool is_secure;
+       bool user_mpm;
        u8 dtim_period;
        u16 beacon_interval;
        int mcast_rate[IEEE80211_NUM_BANDS];
@@ -1398,9 +1432,11 @@ struct cfg80211_auth_request {
  * enum cfg80211_assoc_req_flags - Over-ride default behaviour in association.
  *
  * @ASSOC_REQ_DISABLE_HT:  Disable HT (802.11n)
+ * @ASSOC_REQ_DISABLE_VHT:  Disable VHT
  */
 enum cfg80211_assoc_req_flags {
        ASSOC_REQ_DISABLE_HT            = BIT(0),
+       ASSOC_REQ_DISABLE_VHT           = BIT(1),
 };
 
 /**
@@ -1422,6 +1458,8 @@ enum cfg80211_assoc_req_flags {
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
  *   will be used in ht_capa.  Un-supported values will be ignored.
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
+ * @vht_capa: VHT capability override
+ * @vht_capa_mask: VHT capability mask indicating which fields to use
  */
 struct cfg80211_assoc_request {
        struct cfg80211_bss *bss;
@@ -1432,6 +1470,7 @@ struct cfg80211_assoc_request {
        u32 flags;
        struct ieee80211_ht_cap ht_capa;
        struct ieee80211_ht_cap ht_capa_mask;
+       struct ieee80211_vht_cap vht_capa, vht_capa_mask;
 };
 
 /**
@@ -1542,6 +1581,8 @@ struct cfg80211_ibss_params {
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
  *   will be used in ht_capa.  Un-supported values will be ignored.
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
+ * @vht_capa:  VHT Capability overrides
+ * @vht_capa_mask: The bits of vht_capa which are to be used.
  */
 struct cfg80211_connect_params {
        struct ieee80211_channel *channel;
@@ -1560,6 +1601,8 @@ struct cfg80211_connect_params {
        int bg_scan_period;
        struct ieee80211_ht_cap ht_capa;
        struct ieee80211_ht_cap ht_capa_mask;
+       struct ieee80211_vht_cap vht_capa;
+       struct ieee80211_vht_cap vht_capa_mask;
 };
 
 /**
@@ -1721,6 +1764,21 @@ struct cfg80211_gtk_rekey_data {
        u8 replay_ctr[NL80211_REPLAY_CTR_LEN];
 };
 
+/**
+ * struct cfg80211_update_ft_ies_params - FT IE Information
+ *
+ * This structure provides information needed to update the fast transition IE
+ *
+ * @md: The Mobility Domain ID, 2 Octet value
+ * @ie: Fast Transition IEs
+ * @ie_len: Length of ft_ie in octets
+ */
+struct cfg80211_update_ft_ies_params {
+       u16 md;
+       const u8 *ie;
+       size_t ie_len;
+};
+
 /**
  * struct cfg80211_ops - backend description for wireless configuration
  *
@@ -1781,9 +1839,8 @@ struct cfg80211_gtk_rekey_data {
  * @change_station: Modify a given station. Note that flags changes are not much
  *     validated in cfg80211, in particular the auth/assoc/authorized flags
  *     might come to the driver in invalid combinations -- make sure to check
- *     them, also against the existing state! Also, supported_rates changes are
- *     not checked in station mode -- drivers need to reject (or ignore) them
- *     for anything but TDLS peers.
+ *     them, also against the existing state! Drivers must call
+ *     cfg80211_check_station_change() to validate the information.
  * @get_station: get station information for the station identified by @mac
  * @dump_station: dump station callback -- resume dump at index @idx
  *
@@ -2168,6 +2225,8 @@ struct cfg80211_ops {
        int     (*start_radar_detection)(struct wiphy *wiphy,
                                         struct net_device *dev,
                                         struct cfg80211_chan_def *chandef);
+       int     (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
+                                struct cfg80211_update_ft_ies_params *ftie);
 };
 
 /*
@@ -2485,6 +2544,8 @@ struct wiphy_wowlan_support {
  * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
  * @ht_capa_mod_mask:  Specify what ht_cap values can be over-ridden.
  *     If null, then none can be over-ridden.
+ * @vht_capa_mod_mask:  Specify what VHT capabilities can be over-ridden.
+ *     If null, then none can be over-ridden.
  *
  * @max_acl_mac_addrs: Maximum number of MAC addresses that the device
  *     supports for ACL.
@@ -2593,6 +2654,7 @@ struct wiphy {
        struct dentry *debugfsdir;
 
        const struct ieee80211_ht_cap *ht_capa_mod_mask;
+       const struct ieee80211_vht_cap *vht_capa_mod_mask;
 
 #ifdef CONFIG_NET_NS
        /* the network namespace this phy lives in currently */
@@ -4001,6 +4063,30 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate);
  */
 void cfg80211_unregister_wdev(struct wireless_dev *wdev);
 
+/**
+ * struct cfg80211_ft_event - FT Information Elements
+ * @ies: FT IEs
+ * @ies_len: length of the FT IE in bytes
+ * @target_ap: target AP's MAC address
+ * @ric_ies: RIC IE
+ * @ric_ies_len: length of the RIC IE in bytes
+ */
+struct cfg80211_ft_event_params {
+       const u8 *ies;
+       size_t ies_len;
+       const u8 *target_ap;
+       const u8 *ric_ies;
+       size_t ric_ies_len;
+};
+
+/**
+ * cfg80211_ft_event - notify userspace about FT IE and RIC IE
+ * @netdev: network device
+ * @ft_event: IE information
+ */
+void cfg80211_ft_event(struct net_device *netdev,
+                      struct cfg80211_ft_event_params *ft_event);
+
 /**
  * cfg80211_get_p2p_attr - find and copy a P2P attribute from IE buffer
  * @ies: the input IE buffer
index 2581638f4a3d6ac803d156ec4dd333eaf1a0cdc6..0fee0617fb7d243728583ee351de284f36aca14a 100644 (file)
@@ -24,7 +24,7 @@ struct cgroup_cls_state
        u32 classid;
 };
 
-extern void sock_update_classid(struct sock *sk, struct task_struct *task);
+extern void sock_update_classid(struct sock *sk);
 
 #if IS_BUILTIN(CONFIG_NET_CLS_CGROUP)
 static inline u32 task_cls_classid(struct task_struct *p)
@@ -61,7 +61,7 @@ static inline u32 task_cls_classid(struct task_struct *p)
 }
 #endif
 #else /* !CGROUP_NET_CLS_CGROUP */
-static inline void sock_update_classid(struct sock *sk, struct task_struct *task)
+static inline void sock_update_classid(struct sock *sk)
 {
 }
 
index 1ee9d4bda30d6794c937db4b479a8bc9d172eec1..74004af31c482c2bef8c9240b47e82c2f4a9bfbd 100644 (file)
@@ -1,24 +1,9 @@
 #ifndef _NET_DN_FIB_H
 #define _NET_DN_FIB_H
 
-/* WARNING: The ordering of these elements must match ordering
- *          of RTA_* rtnetlink attribute numbers.
- */
-struct dn_kern_rta {
-        void            *rta_dst;
-        void            *rta_src;
-        int             *rta_iif;
-        int             *rta_oif;
-        void            *rta_gw;
-        u32             *rta_priority;
-        void            *rta_prefsrc;
-        struct rtattr   *rta_mx;
-        struct rtattr   *rta_mp;
-        unsigned char   *rta_protoinfo;
-        u32             *rta_flow;
-        struct rta_cacheinfo *rta_ci;
-       struct rta_session *rta_sess;
-};
+#include <linux/netlink.h>
+
+extern const struct nla_policy rtm_dn_policy[];
 
 struct dn_fib_res {
        struct fib_rule *r;
@@ -93,10 +78,10 @@ struct dn_fib_table {
        u32 n;
 
        int (*insert)(struct dn_fib_table *t, struct rtmsg *r, 
-                       struct dn_kern_rta *rta, struct nlmsghdr *n, 
+                       struct nlattr *attrs[], struct nlmsghdr *n,
                        struct netlink_skb_parms *req);
        int (*delete)(struct dn_fib_table *t, struct rtmsg *r,
-                       struct dn_kern_rta *rta, struct nlmsghdr *n,
+                       struct nlattr *attrs[], struct nlmsghdr *n,
                        struct netlink_skb_parms *req);
        int (*lookup)(struct dn_fib_table *t, const struct flowidn *fld,
                        struct dn_fib_res *res);
@@ -116,13 +101,12 @@ extern void dn_fib_cleanup(void);
 extern int dn_fib_ioctl(struct socket *sock, unsigned int cmd, 
                        unsigned long arg);
 extern struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, 
-                               struct dn_kern_rta *rta, 
+                               struct nlattr *attrs[],
                                const struct nlmsghdr *nlh, int *errp);
 extern int dn_fib_semantic_match(int type, struct dn_fib_info *fi, 
                        const struct flowidn *fld,
                        struct dn_fib_res *res);
 extern void dn_fib_release_info(struct dn_fib_info *fi);
-extern __le16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type);
 extern void dn_fib_flush(void);
 extern void dn_fib_select_multipath(const struct flowidn *fld,
                                        struct dn_fib_res *res);
diff --git a/include/net/firewire.h b/include/net/firewire.h
new file mode 100644 (file)
index 0000000..31bcbfe
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _NET_FIREWIRE_H
+#define _NET_FIREWIRE_H
+
+/* Pseudo L2 address */
+#define FWNET_ALEN     16
+union fwnet_hwaddr {
+       u8 u[FWNET_ALEN];
+       /* "Hardware address" defined in RFC2734/RF3146 */
+       struct {
+               __be64 uniq_id;         /* EUI-64                       */
+               u8 max_rec;             /* max packet size              */
+               u8 sspd;                /* max speed                    */
+               __be16 fifo_hi;         /* hi 16bits of FIFO addr       */
+               __be32 fifo_lo;         /* lo 32bits of FIFO addr       */
+       } __packed uc;
+};
+
+/* Pseudo L2 Header */
+#define FWNET_HLEN     18
+struct fwnet_header {
+       u8 h_dest[FWNET_ALEN];  /* destination address */
+       __be16 h_proto;         /* packet type ID field */
+} __packed;
+
+#endif
index 82665474bcb7c6e1389b2741fd9eda141f617270..9f03a390c826a44a700f42715c48fe6f11eef9ca 100644 (file)
@@ -2,6 +2,7 @@
 #define __LINUX_GRE_H
 
 #include <linux/skbuff.h>
+#include <net/ip_tunnels.h>
 
 #define GREPROTO_CISCO         0
 #define GREPROTO_PPTP          1
@@ -12,7 +13,57 @@ struct gre_protocol {
        void (*err_handler)(struct sk_buff *skb, u32 info);
 };
 
+struct gre_base_hdr {
+       __be16 flags;
+       __be16 protocol;
+};
+#define GRE_HEADER_SECTION 4
+
 int gre_add_protocol(const struct gre_protocol *proto, u8 version);
 int gre_del_protocol(const struct gre_protocol *proto, u8 version);
 
+static inline __be16 gre_flags_to_tnl_flags(__be16 flags)
+{
+       __be16 tflags = 0;
+
+       if (flags & GRE_CSUM)
+               tflags |= TUNNEL_CSUM;
+       if (flags & GRE_ROUTING)
+               tflags |= TUNNEL_ROUTING;
+       if (flags & GRE_KEY)
+               tflags |= TUNNEL_KEY;
+       if (flags & GRE_SEQ)
+               tflags |= TUNNEL_SEQ;
+       if (flags & GRE_STRICT)
+               tflags |= TUNNEL_STRICT;
+       if (flags & GRE_REC)
+               tflags |= TUNNEL_REC;
+       if (flags & GRE_VERSION)
+               tflags |= TUNNEL_VERSION;
+
+       return tflags;
+}
+
+static inline __be16 tnl_flags_to_gre_flags(__be16 tflags)
+{
+       __be16 flags = 0;
+
+       if (tflags & TUNNEL_CSUM)
+               flags |= GRE_CSUM;
+       if (tflags & TUNNEL_ROUTING)
+               flags |= GRE_ROUTING;
+       if (tflags & TUNNEL_KEY)
+               flags |= GRE_KEY;
+       if (tflags & TUNNEL_SEQ)
+               flags |= GRE_SEQ;
+       if (tflags & TUNNEL_STRICT)
+               flags |= GRE_STRICT;
+       if (tflags & TUNNEL_REC)
+               flags |= GRE_REC;
+       if (tflags & TUNNEL_VERSION)
+               flags |= GRE_VERSION;
+
+       return flags;
+}
+
 #endif
index d104c882fc296ceac67f9acd03e47abb09a347b3..8196d5d4035970849c4caba4ee0c2c0928ec24a0 100644 (file)
@@ -85,6 +85,8 @@ struct wpan_phy;
  * Use wpan_wpy_put to put that reference.
  */
 struct ieee802154_mlme_ops {
+       /* The following fields are optional (can be NULL). */
+
        int (*assoc_req)(struct net_device *dev,
                        struct ieee802154_addr *addr,
                        u8 channel, u8 page, u8 cap);
@@ -101,6 +103,8 @@ struct ieee802154_mlme_ops {
        int (*scan_req)(struct net_device *dev,
                        u8 type, u32 channels, u8 page, u8 duration);
 
+       /* The fields below are required. */
+
        struct wpan_phy *(*get_phy)(const struct net_device *dev);
 
        /*
@@ -110,7 +114,6 @@ struct ieee802154_mlme_ops {
        u16 (*get_pan_id)(const struct net_device *dev);
        u16 (*get_short_addr)(const struct net_device *dev);
        u8 (*get_dsn)(const struct net_device *dev);
-       u8 (*get_bsn)(const struct net_device *dev);
 };
 
 /* The IEEE 802.15.4 standard defines 2 type of the devices:
index 93563221d29a728e9a8afef579d90f3a3b8587aa..100fb8cec17c0157c331fc12366d22f593368295 100644 (file)
@@ -71,6 +71,8 @@ struct inet6_ifaddr {
        struct inet6_ifaddr     *ifpub;
        int                     regen_count;
 #endif
+       bool                    tokenized;
+
        struct rcu_head         rcu;
 };
 
@@ -187,6 +189,8 @@ struct inet6_dev {
        struct list_head        tempaddr_list;
 #endif
 
+       struct in6_addr         token;
+
        struct neigh_parms      *nd_parms;
        struct inet6_dev        *next;
        struct ipv6_devconf     cnf;
index 183292722f6e587eee72bf3ff5c201dfe25afbaf..de2c78529afaf81f00abff62bb29708f7f6941a2 100644 (file)
@@ -133,6 +133,8 @@ struct inet_connection_sock {
 #define ICSK_TIME_RETRANS      1       /* Retransmit timer */
 #define ICSK_TIME_DACK         2       /* Delayed ack timer */
 #define ICSK_TIME_PROBE0       3       /* Zero window probe timer */
+#define ICSK_TIME_EARLY_RETRANS 4      /* Early retransmit timer */
+#define ICSK_TIME_LOSS_PROBE   5       /* Tail loss probe timer */
 
 static inline struct inet_connection_sock *inet_csk(const struct sock *sk)
 {
@@ -222,7 +224,8 @@ static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what,
                when = max_when;
        }
 
-       if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0) {
+       if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0 ||
+           what == ICSK_TIME_EARLY_RETRANS || what ==  ICSK_TIME_LOSS_PROBE) {
                icsk->icsk_pending = what;
                icsk->icsk_timeout = jiffies + when;
                sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);
index 0a1dcc2fa2f58c5be1f177f579fa4fa57c138dfd..6f41b45e819e907328c18018560d063af6ec60f7 100644 (file)
@@ -50,10 +50,16 @@ struct inet_frag_queue {
  */
 #define INETFRAGS_MAXDEPTH             128
 
+struct inet_frag_bucket {
+       struct hlist_head       chain;
+       spinlock_t              chain_lock;
+};
+
 struct inet_frags {
-       struct hlist_head       hash[INETFRAGS_HASHSZ];
+       struct inet_frag_bucket hash[INETFRAGS_HASHSZ];
        /* This rwlock is a global lock (seperate per IPv4, IPv6 and
         * netfilter). Important to keep this on a seperate cacheline.
+        * Its primarily a rebuild protection rwlock.
         */
        rwlock_t                lock ____cacheline_aligned_in_smp;
        int                     secret_interval;
@@ -143,6 +149,7 @@ static inline void inet_frag_lru_del(struct inet_frag_queue *q)
 {
        spin_lock(&q->net->lru_lock);
        list_del(&q->lru_list);
+       q->net->nqueues--;
        spin_unlock(&q->net->lru_lock);
 }
 
@@ -151,6 +158,19 @@ static inline void inet_frag_lru_add(struct netns_frags *nf,
 {
        spin_lock(&nf->lru_lock);
        list_add_tail(&q->lru_list, &nf->lru_list);
+       q->net->nqueues++;
        spin_unlock(&nf->lru_lock);
 }
+
+/* RFC 3168 support :
+ * We want to check ECN values of all fragments, do detect invalid combinations.
+ * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value.
+ */
+#define        IPFRAG_ECN_NOT_ECT      0x01 /* one frag had ECN_NOT_ECT */
+#define        IPFRAG_ECN_ECT_1        0x02 /* one frag had ECN_ECT_1 */
+#define        IPFRAG_ECN_ECT_0        0x04 /* one frag had ECN_ECT_0 */
+#define        IPFRAG_ECN_CE           0x08 /* one frag had ECN_CE */
+
+extern const u8 ip_frag_ecn_table[16];
+
 #endif
index e03047f7090bb3419c2aa6f8b39f3a7ff0494f8a..4da5de10d1d49f62bbbe33e770e4c95f2b5c9ddd 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/ipv6.h>
 #include <linux/netdevice.h>
+#include <linux/if_tunnel.h>
 #include <linux/ip6_tunnel.h>
 
 #define IP6TUNNEL_ERR_TIMEO (30*HZ)
@@ -68,4 +69,24 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw);
 __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
                             const struct in6_addr *raddr);
 
+static inline void ip6tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct net_device_stats *stats = &dev->stats;
+       int pkt_len, err;
+
+       nf_reset(skb);
+       pkt_len = skb->len;
+       err = ip6_local_out(skb);
+
+       if (net_xmit_eval(err) == 0) {
+               struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats);
+               u64_stats_update_begin(&tstats->syncp);
+               tstats->tx_bytes += pkt_len;
+               tstats->tx_packets++;
+               u64_stats_update_end(&tstats->syncp);
+       } else {
+               stats->tx_errors++;
+               stats->tx_aborted_errors++;
+       }
+}
 #endif
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
new file mode 100644 (file)
index 0000000..4b6f0b2
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef __NET_IP_TUNNELS_H
+#define __NET_IP_TUNNELS_H 1
+
+#include <linux/if_tunnel.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/u64_stats_sync.h>
+#include <net/dsfield.h>
+#include <net/gro_cells.h>
+#include <net/inet_ecn.h>
+#include <net/ip.h>
+#include <net/rtnetlink.h>
+
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#endif
+
+/* Keep error state on tunnel for 30 sec */
+#define IPTUNNEL_ERR_TIMEO     (30*HZ)
+
+/* 6rd prefix/relay information */
+#ifdef CONFIG_IPV6_SIT_6RD
+struct ip_tunnel_6rd_parm {
+       struct in6_addr         prefix;
+       __be32                  relay_prefix;
+       u16                     prefixlen;
+       u16                     relay_prefixlen;
+};
+#endif
+
+struct ip_tunnel_prl_entry {
+       struct ip_tunnel_prl_entry __rcu *next;
+       __be32                          addr;
+       u16                             flags;
+       struct rcu_head                 rcu_head;
+};
+
+struct ip_tunnel {
+       struct ip_tunnel __rcu  *next;
+       struct hlist_node hash_node;
+       struct net_device       *dev;
+
+       int             err_count;      /* Number of arrived ICMP errors */
+       unsigned long   err_time;       /* Time when the last ICMP error
+                                        * arrived */
+
+       /* These four fields used only by GRE */
+       __u32           i_seqno;        /* The last seen seqno  */
+       __u32           o_seqno;        /* The last output seqno */
+       int             hlen;           /* Precalculated header length */
+       int             mlink;
+
+       struct ip_tunnel_parm parms;
+
+       /* for SIT */
+#ifdef CONFIG_IPV6_SIT_6RD
+       struct ip_tunnel_6rd_parm ip6rd;
+#endif
+       struct ip_tunnel_prl_entry __rcu *prl;  /* potential router list */
+       unsigned int            prl_count;      /* # of entries in PRL */
+       int                     ip_tnl_net_id;
+       struct gro_cells        gro_cells;
+};
+
+#define TUNNEL_CSUM    __cpu_to_be16(0x01)
+#define TUNNEL_ROUTING __cpu_to_be16(0x02)
+#define TUNNEL_KEY     __cpu_to_be16(0x04)
+#define TUNNEL_SEQ     __cpu_to_be16(0x08)
+#define TUNNEL_STRICT  __cpu_to_be16(0x10)
+#define TUNNEL_REC     __cpu_to_be16(0x20)
+#define TUNNEL_VERSION __cpu_to_be16(0x40)
+#define TUNNEL_NO_KEY  __cpu_to_be16(0x80)
+
+struct tnl_ptk_info {
+       __be16 flags;
+       __be16 proto;
+       __be32 key;
+       __be32 seq;
+};
+
+#define PACKET_RCVD    0
+#define PACKET_REJECT  1
+
+#define IP_TNL_HASH_BITS   10
+#define IP_TNL_HASH_SIZE   (1 << IP_TNL_HASH_BITS)
+
+struct ip_tunnel_net {
+       struct hlist_head *tunnels;
+       struct net_device *fb_tunnel_dev;
+};
+
+int ip_tunnel_init(struct net_device *dev);
+void ip_tunnel_uninit(struct net_device *dev);
+void  ip_tunnel_dellink(struct net_device *dev, struct list_head *head);
+int __net_init ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
+                                 struct rtnl_link_ops *ops, char *devname);
+
+void __net_exit ip_tunnel_delete_net(struct ip_tunnel_net *itn);
+
+void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
+                   const struct iphdr *tnl_params);
+int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd);
+int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu);
+
+struct rtnl_link_stats64 *ip_tunnel_get_stats64(struct net_device *dev,
+                                               struct rtnl_link_stats64 *tot);
+struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
+                                  int link, __be16 flags,
+                                  __be32 remote, __be32 local,
+                                  __be32 key);
+
+int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
+                 const struct tnl_ptk_info *tpi, bool log_ecn_error);
+int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[],
+                        struct ip_tunnel_parm *p);
+int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[],
+                     struct ip_tunnel_parm *p);
+void ip_tunnel_setup(struct net_device *dev, int net_id);
+
+/* Extract dsfield from inner protocol */
+static inline u8 ip_tunnel_get_dsfield(const struct iphdr *iph,
+                                      const struct sk_buff *skb)
+{
+       if (skb->protocol == htons(ETH_P_IP))
+               return iph->tos;
+       else if (skb->protocol == htons(ETH_P_IPV6))
+               return ipv6_get_dsfield((const struct ipv6hdr *)iph);
+       else
+               return 0;
+}
+
+/* Propogate ECN bits out */
+static inline u8 ip_tunnel_ecn_encap(u8 tos, const struct iphdr *iph,
+                                    const struct sk_buff *skb)
+{
+       u8 inner = ip_tunnel_get_dsfield(iph, skb);
+
+       return INET_ECN_encapsulate(tos, inner);
+}
+
+static inline void tunnel_ip_select_ident(struct sk_buff *skb,
+                                         const struct iphdr  *old_iph,
+                                         struct dst_entry *dst)
+{
+       struct iphdr *iph = ip_hdr(skb);
+
+       /* Use inner packet iph-id if possible. */
+       if (skb->protocol == htons(ETH_P_IP) && old_iph->id)
+               iph->id = old_iph->id;
+       else
+               __ip_select_ident(iph, dst,
+                                 (skb_shinfo(skb)->gso_segs ?: 1) - 1);
+}
+
+static inline void iptunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       int err;
+       int pkt_len = skb->len - skb_transport_offset(skb);
+       struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats);
+
+       nf_reset(skb);
+
+       err = ip_local_out(skb);
+       if (likely(net_xmit_eval(err) == 0)) {
+               u64_stats_update_begin(&tstats->syncp);
+               tstats->tx_bytes += pkt_len;
+               tstats->tx_packets++;
+               u64_stats_update_end(&tstats->syncp);
+       } else {
+               dev->stats.tx_errors++;
+               dev->stats.tx_aborted_errors++;
+       }
+}
+#endif /* __NET_IP_TUNNELS_H */
index fce8e6b66d558d8ff8bb629c4da5e358113a675e..f9f5b057b480fbacf7a43801aa9a2e81a1605728 100644 (file)
@@ -233,6 +233,21 @@ static inline void ip_vs_addr_copy(int af, union nf_inet_addr *dst,
        dst->ip = src->ip;
 }
 
+static inline void ip_vs_addr_set(int af, union nf_inet_addr *dst,
+                                 const union nf_inet_addr *src)
+{
+#ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6) {
+               dst->in6 = src->in6;
+               return;
+       }
+#endif
+       dst->ip = src->ip;
+       dst->all[1] = 0;
+       dst->all[2] = 0;
+       dst->all[3] = 0;
+}
+
 static inline int ip_vs_addr_equal(int af, const union nf_inet_addr *a,
                                   const union nf_inet_addr *b)
 {
@@ -344,8 +359,6 @@ static inline const char *ip_vs_dbg_addr(int af, char *buf, size_t buf_len,
 #define LeaveFunction(level)   do {} while (0)
 #endif
 
-#define        IP_VS_WAIT_WHILE(expr)  while (expr) { cpu_relax(); }
-
 
 /*
  *      The port number of FTP service (in network order).
@@ -459,7 +472,7 @@ struct ip_vs_estimator {
 struct ip_vs_stats {
        struct ip_vs_stats_user ustats;         /* statistics */
        struct ip_vs_estimator  est;            /* estimator */
-       struct ip_vs_cpu_stats  *cpustats;      /* per cpu counters */
+       struct ip_vs_cpu_stats __percpu *cpustats;      /* per cpu counters */
        spinlock_t              lock;           /* spin lock */
        struct ip_vs_stats_user ustats0;        /* reset values */
 };
@@ -566,20 +579,19 @@ struct ip_vs_conn_param {
  */
 struct ip_vs_conn {
        struct hlist_node       c_list;         /* hashed list heads */
-#ifdef CONFIG_NET_NS
-       struct net              *net;           /* Name space */
-#endif
        /* Protocol, addresses and port numbers */
-       u16                     af;             /* address family */
        __be16                  cport;
-       __be16                  vport;
        __be16                  dport;
-       __u32                   fwmark;         /* Fire wall mark from skb */
+       __be16                  vport;
+       u16                     af;             /* address family */
        union nf_inet_addr      caddr;          /* client address */
        union nf_inet_addr      vaddr;          /* virtual address */
        union nf_inet_addr      daddr;          /* destination address */
        volatile __u32          flags;          /* status flags */
        __u16                   protocol;       /* Which protocol (TCP/UDP) */
+#ifdef CONFIG_NET_NS
+       struct net              *net;           /* Name space */
+#endif
 
        /* counter and timer */
        atomic_t                refcnt;         /* reference count */
@@ -593,6 +605,7 @@ struct ip_vs_conn {
                                                 * state transition triggerd
                                                 * synchronization
                                                 */
+       __u32                   fwmark;         /* Fire wall mark from skb */
        unsigned long           sync_endtime;   /* jiffies + sent_retries */
 
        /* Control members */
@@ -620,6 +633,8 @@ struct ip_vs_conn {
        const struct ip_vs_pe   *pe;
        char                    *pe_data;
        __u8                    pe_data_len;
+
+       struct rcu_head         rcu_head;
 };
 
 /*
@@ -695,10 +710,9 @@ struct ip_vs_dest_user_kern {
  *     and the forwarding entries
  */
 struct ip_vs_service {
-       struct list_head        s_list;   /* for normal service table */
-       struct list_head        f_list;   /* for fwmark-based service table */
+       struct hlist_node       s_list;   /* for normal service table */
+       struct hlist_node       f_list;   /* for fwmark-based service table */
        atomic_t                refcnt;   /* reference counter */
-       atomic_t                usecnt;   /* use counter */
 
        u16                     af;       /* address family */
        __u16                   protocol; /* which protocol (TCP/UDP) */
@@ -713,25 +727,35 @@ struct ip_vs_service {
        struct list_head        destinations;  /* real server d-linked list */
        __u32                   num_dests;     /* number of servers */
        struct ip_vs_stats      stats;         /* statistics for the service */
-       struct ip_vs_app        *inc;     /* bind conns to this app inc */
 
        /* for scheduling */
-       struct ip_vs_scheduler  *scheduler;    /* bound scheduler object */
-       rwlock_t                sched_lock;    /* lock sched_data */
+       struct ip_vs_scheduler __rcu *scheduler; /* bound scheduler object */
+       spinlock_t              sched_lock;    /* lock sched_data */
        void                    *sched_data;   /* scheduler application data */
 
        /* alternate persistence engine */
-       struct ip_vs_pe         *pe;
+       struct ip_vs_pe __rcu   *pe;
+
+       struct rcu_head         rcu_head;
 };
 
+/* Information for cached dst */
+struct ip_vs_dest_dst {
+       struct dst_entry        *dst_cache;     /* destination cache entry */
+       u32                     dst_cookie;
+       union nf_inet_addr      dst_saddr;
+       struct rcu_head         rcu_head;
+};
 
+/* In grace period after removing */
+#define IP_VS_DEST_STATE_REMOVING      0x01
 /*
  *     The real server destination forwarding entry
  *     with ip address, port number, and so on.
  */
 struct ip_vs_dest {
        struct list_head        n_list;   /* for the dests in the service */
-       struct list_head        d_list;   /* for table with all the dests */
+       struct hlist_node       d_list;   /* for table with all the dests */
 
        u16                     af;             /* address family */
        __be16                  port;           /* port number of the server */
@@ -742,6 +766,7 @@ struct ip_vs_dest {
 
        atomic_t                refcnt;         /* reference counter */
        struct ip_vs_stats      stats;          /* statistics */
+       unsigned long           state;          /* state flags */
 
        /* connection counters and thresholds */
        atomic_t                activeconns;    /* active connections */
@@ -752,10 +777,7 @@ struct ip_vs_dest {
 
        /* for destination cache */
        spinlock_t              dst_lock;       /* lock of dst_cache */
-       struct dst_entry        *dst_cache;     /* destination cache entry */
-       u32                     dst_rtos;       /* RT_TOS(tos) for dst */
-       u32                     dst_cookie;
-       union nf_inet_addr      dst_saddr;
+       struct ip_vs_dest_dst __rcu *dest_dst;  /* cached dst info */
 
        /* for virtual service */
        struct ip_vs_service    *svc;           /* service it belongs to */
@@ -763,6 +785,10 @@ struct ip_vs_dest {
        __be16                  vport;          /* virtual port number */
        union nf_inet_addr      vaddr;          /* virtual IP address */
        __u32                   vfwmark;        /* firewall mark of service */
+
+       struct list_head        t_list;         /* in dest_trash */
+       struct rcu_head         rcu_head;
+       unsigned int            in_rs_table:1;  /* we are in rs_table */
 };
 
 
@@ -778,9 +804,13 @@ struct ip_vs_scheduler {
        /* scheduler initializing service */
        int (*init_service)(struct ip_vs_service *svc);
        /* scheduling service finish */
-       int (*done_service)(struct ip_vs_service *svc);
-       /* scheduler updating service */
-       int (*update_service)(struct ip_vs_service *svc);
+       void (*done_service)(struct ip_vs_service *svc);
+       /* dest is linked */
+       int (*add_dest)(struct ip_vs_service *svc, struct ip_vs_dest *dest);
+       /* dest is unlinked */
+       int (*del_dest)(struct ip_vs_service *svc, struct ip_vs_dest *dest);
+       /* dest is updated */
+       int (*upd_dest)(struct ip_vs_service *svc, struct ip_vs_dest *dest);
 
        /* selecting a server from the given service */
        struct ip_vs_dest* (*schedule)(struct ip_vs_service *svc,
@@ -819,6 +849,7 @@ struct ip_vs_app {
        struct ip_vs_app        *app;           /* its real application */
        __be16                  port;           /* port number in net order */
        atomic_t                usecnt;         /* usage counter */
+       struct rcu_head         rcu_head;
 
        /*
         * output hook: Process packet in inout direction, diff set for TCP.
@@ -881,6 +912,9 @@ struct ipvs_master_sync_state {
        struct netns_ipvs       *ipvs;
 };
 
+/* How much time to keep dests in trash */
+#define IP_VS_DEST_TRASH_PERIOD                (120 * HZ)
+
 /* IPVS in network namespace */
 struct netns_ipvs {
        int                     gen;            /* Generation */
@@ -892,7 +926,7 @@ struct netns_ipvs {
        #define IP_VS_RTAB_SIZE (1 << IP_VS_RTAB_BITS)
        #define IP_VS_RTAB_MASK (IP_VS_RTAB_SIZE - 1)
 
-       struct list_head        rs_table[IP_VS_RTAB_SIZE];
+       struct hlist_head       rs_table[IP_VS_RTAB_SIZE];
        /* ip_vs_app */
        struct list_head        app_list;
        /* ip_vs_proto */
@@ -904,7 +938,6 @@ struct netns_ipvs {
        #define TCP_APP_TAB_SIZE        (1 << TCP_APP_TAB_BITS)
        #define TCP_APP_TAB_MASK        (TCP_APP_TAB_SIZE - 1)
        struct list_head        tcp_apps[TCP_APP_TAB_SIZE];
-       spinlock_t              tcp_app_lock;
 #endif
        /* ip_vs_proto_udp */
 #ifdef CONFIG_IP_VS_PROTO_UDP
@@ -912,7 +945,6 @@ struct netns_ipvs {
        #define UDP_APP_TAB_SIZE        (1 << UDP_APP_TAB_BITS)
        #define UDP_APP_TAB_MASK        (UDP_APP_TAB_SIZE - 1)
        struct list_head        udp_apps[UDP_APP_TAB_SIZE];
-       spinlock_t              udp_app_lock;
 #endif
        /* ip_vs_proto_sctp */
 #ifdef CONFIG_IP_VS_PROTO_SCTP
@@ -921,7 +953,6 @@ struct netns_ipvs {
        #define SCTP_APP_TAB_MASK       (SCTP_APP_TAB_SIZE - 1)
        /* Hash table for SCTP application incarnations  */
        struct list_head        sctp_apps[SCTP_APP_TAB_SIZE];
-       spinlock_t              sctp_app_lock;
 #endif
        /* ip_vs_conn */
        atomic_t                conn_count;      /*  connection counter */
@@ -931,9 +962,10 @@ struct netns_ipvs {
 
        int                     num_services;    /* no of virtual services */
 
-       rwlock_t                rs_lock;         /* real services table */
        /* Trash for destinations */
        struct list_head        dest_trash;
+       spinlock_t              dest_trash_lock;
+       struct timer_list       dest_trash_timer; /* expiration timer */
        /* Service counters */
        atomic_t                ftpsvc_counter;
        atomic_t                nullsvc_counter;
@@ -1181,9 +1213,19 @@ struct ip_vs_conn * ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb,
                                             const struct ip_vs_iphdr *iph,
                                             int inverse);
 
+/* Get reference to gain full access to conn.
+ * By default, RCU read-side critical sections have access only to
+ * conn fields and its PE data, see ip_vs_conn_rcu_free() for reference.
+ */
+static inline bool __ip_vs_conn_get(struct ip_vs_conn *cp)
+{
+       return atomic_inc_not_zero(&cp->refcnt);
+}
+
 /* put back the conn without restarting its timer */
 static inline void __ip_vs_conn_put(struct ip_vs_conn *cp)
 {
+       smp_mb__before_atomic_dec();
        atomic_dec(&cp->refcnt);
 }
 extern void ip_vs_conn_put(struct ip_vs_conn *cp);
@@ -1298,8 +1340,6 @@ extern void ip_vs_app_inc_put(struct ip_vs_app *inc);
 extern int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb);
 extern int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb);
 
-void ip_vs_bind_pe(struct ip_vs_service *svc, struct ip_vs_pe *pe);
-void ip_vs_unbind_pe(struct ip_vs_service *svc);
 int register_ip_vs_pe(struct ip_vs_pe *pe);
 int unregister_ip_vs_pe(struct ip_vs_pe *pe);
 struct ip_vs_pe *ip_vs_pe_getbyname(const char *name);
@@ -1346,7 +1386,8 @@ extern int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler);
 extern int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler);
 extern int ip_vs_bind_scheduler(struct ip_vs_service *svc,
                                struct ip_vs_scheduler *scheduler);
-extern int ip_vs_unbind_scheduler(struct ip_vs_service *svc);
+extern void ip_vs_unbind_scheduler(struct ip_vs_service *svc,
+                                  struct ip_vs_scheduler *sched);
 extern struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name);
 extern void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler);
 extern struct ip_vs_conn *
@@ -1366,17 +1407,12 @@ extern struct ip_vs_stats ip_vs_stats;
 extern int sysctl_ip_vs_sync_ver;
 
 extern struct ip_vs_service *
-ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol,
+ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol,
                  const union nf_inet_addr *vaddr, __be16 vport);
 
-static inline void ip_vs_service_put(struct ip_vs_service *svc)
-{
-       atomic_dec(&svc->usecnt);
-}
-
-extern struct ip_vs_dest *
-ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol,
-                         const union nf_inet_addr *daddr, __be16 dport);
+extern bool
+ip_vs_has_real_service(struct net *net, int af, __u16 protocol,
+                      const union nf_inet_addr *daddr, __be16 dport);
 
 extern int ip_vs_use_count_inc(void);
 extern void ip_vs_use_count_dec(void);
@@ -1388,8 +1424,18 @@ extern struct ip_vs_dest *
 ip_vs_find_dest(struct net *net, int af, const union nf_inet_addr *daddr,
                __be16 dport, const union nf_inet_addr *vaddr, __be16 vport,
                __u16 protocol, __u32 fwmark, __u32 flags);
-extern struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp);
+extern void ip_vs_try_bind_dest(struct ip_vs_conn *cp);
 
+static inline void ip_vs_dest_hold(struct ip_vs_dest *dest)
+{
+       atomic_inc(&dest->refcnt);
+}
+
+static inline void ip_vs_dest_put(struct ip_vs_dest *dest)
+{
+       smp_mb__before_atomic_dec();
+       atomic_dec(&dest->refcnt);
+}
 
 /*
  *      IPVS sync daemon data and function prototypes
@@ -1428,7 +1474,7 @@ extern int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
 extern int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                           struct ip_vs_protocol *pp, int offset,
                           unsigned int hooknum, struct ip_vs_iphdr *iph);
-extern void ip_vs_dst_reset(struct ip_vs_dest *dest);
+extern void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
 
 #ifdef CONFIG_IP_VS_IPV6
 extern int ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
diff --git a/include/net/ipip.h b/include/net/ipip.h
deleted file mode 100644 (file)
index 982141c..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef __NET_IPIP_H
-#define __NET_IPIP_H 1
-
-#include <linux/if_tunnel.h>
-#include <net/gro_cells.h>
-#include <net/ip.h>
-
-/* Keep error state on tunnel for 30 sec */
-#define IPTUNNEL_ERR_TIMEO     (30*HZ)
-
-/* 6rd prefix/relay information */
-struct ip_tunnel_6rd_parm {
-       struct in6_addr         prefix;
-       __be32                  relay_prefix;
-       u16                     prefixlen;
-       u16                     relay_prefixlen;
-};
-
-struct ip_tunnel {
-       struct ip_tunnel __rcu  *next;
-       struct net_device       *dev;
-
-       int                     err_count;      /* Number of arrived ICMP errors */
-       unsigned long           err_time;       /* Time when the last ICMP error arrived */
-
-       /* These four fields used only by GRE */
-       __u32                   i_seqno;        /* The last seen seqno  */
-       __u32                   o_seqno;        /* The last output seqno */
-       int                     hlen;           /* Precalculated GRE header length */
-       int                     mlink;
-
-       struct ip_tunnel_parm   parms;
-
-       /* for SIT */
-#ifdef CONFIG_IPV6_SIT_6RD
-       struct ip_tunnel_6rd_parm       ip6rd;
-#endif
-       struct ip_tunnel_prl_entry __rcu *prl;          /* potential router list */
-       unsigned int                    prl_count;      /* # of entries in PRL */
-
-       struct gro_cells                gro_cells;
-};
-
-struct ip_tunnel_prl_entry {
-       struct ip_tunnel_prl_entry __rcu *next;
-       __be32                          addr;
-       u16                             flags;
-       struct rcu_head                 rcu_head;
-};
-
-static inline void iptunnel_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-       int err;
-       struct iphdr *iph = ip_hdr(skb);
-       int pkt_len = skb->len - skb_transport_offset(skb);
-       struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats);
-
-       nf_reset(skb);
-       skb->ip_summed = CHECKSUM_NONE;
-       ip_select_ident(iph, skb_dst(skb), NULL);
-
-       err = ip_local_out(skb);
-       if (likely(net_xmit_eval(err) == 0)) {
-               u64_stats_update_begin(&tstats->syncp);
-               tstats->tx_bytes += pkt_len;
-               tstats->tx_packets++;
-               u64_stats_update_end(&tstats->syncp);
-       } else {
-               dev->stats.tx_errors++;
-               dev->stats.tx_aborted_errors++;
-       }
-}
-
-static inline void tunnel_ip_select_ident(struct sk_buff *skb,
-                                         const struct iphdr  *old_iph,
-                                         struct dst_entry *dst)
-{
-       struct iphdr *iph = ip_hdr(skb);
-
-       /* Use inner packet iph-id if possible. */
-       if (skb->protocol == htons(ETH_P_IP) && old_iph->id)
-               iph->id = old_iph->id;
-       else
-               __ip_select_ident(iph, dst,
-                                 (skb_shinfo(skb)->gso_segs ?: 1) - 1);
-}
-#endif
index 64d12e77719afd26b95333a8a145b50301cef440..0810aa57c78015ac35fd8ea9caaddb0c2ab791a3 100644 (file)
@@ -217,7 +217,7 @@ struct ipv6_txoptions {
 };
 
 struct ip6_flowlabel {
-       struct ip6_flowlabel    *next;
+       struct ip6_flowlabel __rcu *next;
        __be32                  label;
        atomic_t                users;
        struct in6_addr         dst;
@@ -238,9 +238,9 @@ struct ip6_flowlabel {
 #define IPV6_FLOWLABEL_MASK    cpu_to_be32(0x000FFFFF)
 
 struct ipv6_fl_socklist {
-       struct ipv6_fl_socklist *next;
-       struct ip6_flowlabel    *fl;
-       struct rcu_head         rcu;
+       struct ipv6_fl_socklist __rcu   *next;
+       struct ip6_flowlabel            *fl;
+       struct rcu_head                 rcu;
 };
 
 extern struct ip6_flowlabel    *fl6_sock_lookup(struct sock *sk, __be32 label);
@@ -320,6 +320,18 @@ static inline int ipv6_addr_src_scope(const struct in6_addr *addr)
        return __ipv6_addr_src_scope(__ipv6_addr_type(addr));
 }
 
+static inline bool __ipv6_addr_needs_scope_id(int type)
+{
+       return type & IPV6_ADDR_LINKLOCAL ||
+              (type & IPV6_ADDR_MULTICAST &&
+               (type & (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL)));
+}
+
+static inline __u32 ipv6_iface_scope_id(const struct in6_addr *addr, int iface)
+{
+       return __ipv6_addr_needs_scope_id(__ipv6_addr_type(addr)) ? iface : 0;
+}
+
 static inline int ipv6_addr_cmp(const struct in6_addr *a1, const struct in6_addr *a2)
 {
        return memcmp(a1, a2, sizeof(struct in6_addr));
@@ -466,6 +478,7 @@ struct ip6_create_arg {
        u32 user;
        const struct in6_addr *src;
        const struct in6_addr *dst;
+       u8 ecn;
 };
 
 void ip6_frag_init(struct inet_frag_queue *q, void *a);
@@ -485,6 +498,7 @@ struct frag_queue {
        int                     iif;
        unsigned int            csum;
        __u16                   nhoffset;
+       u8                      ecn;
 };
 
 void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq,
index f7eba1300d82a3f8e0d96a41ae5ab6a39addb0e1..dd73b8c6746bdd1654c08bc5e2c10f77b59a0a1f 100644 (file)
@@ -93,9 +93,11 @@ struct device;
  * enum ieee80211_max_queues - maximum number of queues
  *
  * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues.
+ * @IEEE80211_MAX_QUEUE_MAP: bitmap with maximum queues set
  */
 enum ieee80211_max_queues {
        IEEE80211_MAX_QUEUES =          16,
+       IEEE80211_MAX_QUEUE_MAP =       BIT(IEEE80211_MAX_QUEUES) - 1,
 };
 
 #define IEEE80211_INVAL_HW_QUEUE       0xff
@@ -1067,6 +1069,9 @@ enum ieee80211_vif_flags {
  *     path needing to access it; even though the netdev carrier will always
  *     be off when it is %NULL there can still be races and packets could be
  *     processed after it switches back to %NULL.
+ * @debugfs_dir: debugfs dentry, can be used by drivers to create own per
+ *      interface debug files. Note that it will be NULL for the virtual
+ *     monitor interface (if that is requested.)
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *).
  */
@@ -1083,6 +1088,10 @@ struct ieee80211_vif {
 
        u32 driver_flags;
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+       struct dentry *debugfs_dir;
+#endif
+
        /* must be last */
        u8 drv_priv[0] __aligned(sizeof(void *));
 };
@@ -1101,8 +1110,6 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
  * These flags are used for communication about keys between the driver
  * and mac80211, with the @flags parameter of &struct ieee80211_key_conf.
  *
- * @IEEE80211_KEY_FLAG_WMM_STA: Set by mac80211, this flag indicates
- *     that the STA this key will be used with could be using QoS.
  * @IEEE80211_KEY_FLAG_GENERATE_IV: This flag should be set by the
  *     driver to indicate that it requires IV generation for this
  *     particular key.
@@ -1127,7 +1134,6 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
  *     %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
  */
 enum ieee80211_key_flags {
-       IEEE80211_KEY_FLAG_WMM_STA      = 1<<0,
        IEEE80211_KEY_FLAG_GENERATE_IV  = 1<<1,
        IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2,
        IEEE80211_KEY_FLAG_PAIRWISE     = 1<<3,
@@ -1231,9 +1237,8 @@ enum ieee80211_sta_rx_bandwidth {
  * @addr: MAC address
  * @aid: AID we assigned to the station if we're an AP
  * @supp_rates: Bitmap of supported rates (per band)
- * @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities
- * @vht_cap: VHT capabilities of this STA; Not restricting any capabilities
- *     of remote STA. Taking as is.
+ * @ht_cap: HT capabilities of this STA; restricted to our own capabilities
+ * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
  * @wme: indicates whether the STA supports WME. Only valid during AP-mode.
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *), size is determined in hw information.
@@ -1950,14 +1955,14 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
  * filter those response frames except in the case of frames that
  * are buffered in the driver -- those must remain buffered to avoid
  * reordering. Because it is possible that no frames are released
- * in this case, the driver must call ieee80211_sta_eosp_irqsafe()
+ * in this case, the driver must call ieee80211_sta_eosp()
  * to indicate to mac80211 that the service period ended anyway.
  *
  * Finally, if frames from multiple TIDs are released from mac80211
  * but the driver might reorder them, it must clear & set the flags
  * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP)
  * and also take care of the EOSP and MORE_DATA bits in the frame.
- * The driver may also use ieee80211_sta_eosp_irqsafe() in this case.
+ * The driver may also use ieee80211_sta_eosp() in this case.
  */
 
 /**
@@ -2134,6 +2139,24 @@ enum ieee80211_rate_control_changed {
        IEEE80211_RC_NSS_CHANGED        = BIT(3),
 };
 
+/**
+ * enum ieee80211_roc_type - remain on channel type
+ *
+ * With the support for multi channel contexts and multi channel operations,
+ * remain on channel operations might be limited/deferred/aborted by other
+ * flows/operations which have higher priority (and vise versa).
+ * Specifying the ROC type can be used by devices to prioritize the ROC
+ * operations compared to other operations/flows.
+ *
+ * @IEEE80211_ROC_TYPE_NORMAL: There are no special requirements for this ROC.
+ * @IEEE80211_ROC_TYPE_MGMT_TX: The remain on channel request is required
+ *     for sending managment frames offchannel.
+ */
+enum ieee80211_roc_type {
+       IEEE80211_ROC_TYPE_NORMAL = 0,
+       IEEE80211_ROC_TYPE_MGMT_TX,
+};
+
 /**
  * struct ieee80211_ops - callbacks from mac80211 to the driver
  *
@@ -2212,18 +2235,6 @@ enum ieee80211_rate_control_changed {
  *     MAC address of the device going away.
  *     Hence, this callback must be implemented. It can sleep.
  *
- * @add_interface_debugfs: Drivers can use this callback to add debugfs files
- *     when a vif is added to mac80211. This callback and
- *     @remove_interface_debugfs should be within a CONFIG_MAC80211_DEBUGFS
- *     conditional. @remove_interface_debugfs must be provided for cleanup.
- *     This callback can sleep.
- *
- * @remove_interface_debugfs: Remove the debugfs files which were added using
- *     @add_interface_debugfs. This callback must remove all debugfs entries
- *     that were added because mac80211 only removes interface debugfs when the
- *     interface is destroyed, not when it is removed from the driver.
- *     This callback can sleep.
- *
  * @config: Handler for configuration requests. IEEE 802.11 code calls this
  *     function to change hardware configuration, e.g., channel.
  *     This function should never fail but returns a negative error code
@@ -2245,6 +2256,9 @@ enum ieee80211_rate_control_changed {
  *     See the section "Frame filtering" for more information.
  *     This callback must be implemented and can sleep.
  *
+ * @set_multicast_list: Configure the device's interface specific RX multicast
+ *     filter. This callback is optional. This callback must be atomic.
+ *
  * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit
  *     must be set or cleared for a given STA. Must be atomic.
  *
@@ -2426,8 +2440,11 @@ enum ieee80211_rate_control_changed {
  * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep.
  *
  * @flush: Flush all pending frames from the hardware queue, making sure
- *     that the hardware queues are empty. If the parameter @drop is set
- *     to %true, pending frames may be dropped. The callback can sleep.
+ *     that the hardware queues are empty. The @queues parameter is a bitmap
+ *     of queues to flush, which is useful if different virtual interfaces
+ *     use different hardware queues; it may also indicate all queues.
+ *     If the parameter @drop is set to %true, pending frames may be dropped.
+ *     The callback can sleep.
  *
  * @channel_switch: Drivers that need (or want) to offload the channel
  *     switch operation for CSAs received from the AP may implement this
@@ -2492,7 +2509,7 @@ enum ieee80211_rate_control_changed {
  *     setting the EOSP flag in the QoS header of the frames. Also, when the
  *     service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP
  *     on the last frame in the SP. Alternatively, it may call the function
- *     ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP.
+ *     ieee80211_sta_eosp() to inform mac80211 of the end of the SP.
  *     This callback must be atomic.
  * @allow_buffered_frames: Prepare device to allow the given number of frames
  *     to go out to the given station. The frames will be sent by mac80211
@@ -2503,7 +2520,7 @@ enum ieee80211_rate_control_changed {
  *     them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag
  *     on the last frame and clear it on all others and also handle the EOSP
  *     bit in the QoS header correctly. Alternatively, it can also call the
- *     ieee80211_sta_eosp_irqsafe() function.
+ *     ieee80211_sta_eosp() function.
  *     The @tids parameter is a bitmap and tells the driver which TIDs the
  *     frames will be on; it will at most have two bits set.
  *     This callback must be atomic.
@@ -2591,6 +2608,10 @@ struct ieee80211_ops {
                                 unsigned int changed_flags,
                                 unsigned int *total_flags,
                                 u64 multicast);
+       void (*set_multicast_list)(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif, bool allmulti,
+                                  struct netdev_hw_addr_list *mc_list);
+
        int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
                       bool set);
        int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -2637,12 +2658,6 @@ struct ieee80211_ops {
                                   struct ieee80211_vif *vif,
                                   struct ieee80211_sta *sta,
                                   struct dentry *dir);
-       void (*add_interface_debugfs)(struct ieee80211_hw *hw,
-                                     struct ieee80211_vif *vif,
-                                     struct dentry *dir);
-       void (*remove_interface_debugfs)(struct ieee80211_hw *hw,
-                                        struct ieee80211_vif *vif,
-                                        struct dentry *dir);
 #endif
        void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum sta_notify_cmd, struct ieee80211_sta *sta);
@@ -2677,7 +2692,7 @@ struct ieee80211_ops {
                             struct netlink_callback *cb,
                             void *data, int len);
 #endif
-       void (*flush)(struct ieee80211_hw *hw, bool drop);
+       void (*flush)(struct ieee80211_hw *hw, u32 queues, bool drop);
        void (*channel_switch)(struct ieee80211_hw *hw,
                               struct ieee80211_channel_switch *ch_switch);
        int (*napi_poll)(struct ieee80211_hw *hw, int budget);
@@ -2687,7 +2702,8 @@ struct ieee80211_ops {
        int (*remain_on_channel)(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_channel *chan,
-                                int duration);
+                                int duration,
+                                enum ieee80211_roc_type type);
        int (*cancel_remain_on_channel)(struct ieee80211_hw *hw);
        int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx);
        void (*get_ringparam)(struct ieee80211_hw *hw,
@@ -3842,14 +3858,17 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
  * %IEEE80211_TX_STATUS_EOSP bit and call this function instead.
  * This applies for PS-Poll as well as uAPSD.
  *
- * Note that there is no non-_irqsafe version right now as
- * it wasn't needed, but just like _tx_status() and _rx()
- * must not be mixed in irqsafe/non-irqsafe versions, this
- * function must not be mixed with those either. Use the
- * all irqsafe, or all non-irqsafe, don't mix! If you need
- * the non-irqsafe version of this, you need to add it.
+ * Note that just like with _tx_status() and _rx() drivers must
+ * not mix calls to irqsafe/non-irqsafe versions, this function
+ * must not be mixed with those either. Use the all irqsafe, or
+ * all non-irqsafe, don't mix!
+ *
+ * NB: the _irqsafe version of this function doesn't exist, no
+ *     driver needs it right now. Don't call this function if
+ *     you'd need the _irqsafe version, look at the git history
+ *     and restore the _irqsafe version!
  */
-void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta);
+void ieee80211_sta_eosp(struct ieee80211_sta *pubsta);
 
 /**
  * ieee80211_iter_keys - iterate keys programmed into the device
index de644bcd861343961d2ef6de675250416b98725b..b176978274828206b784e7003e04c871bef582a3 100644 (file)
@@ -17,6 +17,7 @@
 #include <net/netns/ipv6.h>
 #include <net/netns/sctp.h>
 #include <net/netns/dccp.h>
+#include <net/netns/netfilter.h>
 #include <net/netns/x_tables.h>
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 #include <net/netns/conntrack.h>
@@ -94,6 +95,7 @@ struct net {
        struct netns_dccp       dccp;
 #endif
 #ifdef CONFIG_NETFILTER
+       struct netns_nf         nf;
        struct netns_xt         xt;
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        struct netns_ct         ct;
index caca0c4d6b4bf4c606438df0a3de24369ffb4738..644d9c223d249617291edce40d11c75cec6fd0fd 100644 (file)
@@ -184,7 +184,7 @@ extern int nf_conntrack_hash_check_insert(struct nf_conn *ct);
 extern void nf_ct_delete_from_lists(struct nf_conn *ct);
 extern void nf_ct_dying_timeout(struct nf_conn *ct);
 
-extern void nf_conntrack_flush_report(struct net *net, u32 pid, int report);
+extern void nf_conntrack_flush_report(struct net *net, u32 portid, int report);
 
 extern bool nf_ct_get_tuplepr(const struct sk_buff *skb,
                              unsigned int nhoff, u_int16_t l3num,
index 930275fa2ea68f082e5974586abc39c5138706c4..fb2b6234e9375846bdb37727717c7dbd432550d9 100644 (file)
@@ -27,6 +27,7 @@ extern unsigned int nf_conntrack_in(struct net *net,
 
 extern int nf_conntrack_init_net(struct net *net);
 extern void nf_conntrack_cleanup_net(struct net *net);
+extern void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list);
 
 extern int nf_conntrack_proto_pernet_init(struct net *net);
 extern void nf_conntrack_proto_pernet_fini(struct net *net);
index cbbae7621e229806bab08c8d622c599ea4db9364..3f3aecbc8632e935e16e6517bc23eb8e07a9b16d 100644 (file)
@@ -88,7 +88,7 @@ nf_ct_find_expectation(struct net *net, u16 zone,
                       const struct nf_conntrack_tuple *tuple);
 
 void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
-                               u32 pid, int report);
+                               u32 portid, int report);
 static inline void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
 {
        nf_ct_unlink_expect_report(exp, 0, 0);
@@ -106,7 +106,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *, unsigned int, u_int8_t,
                       u_int8_t, const __be16 *, const __be16 *);
 void nf_ct_expect_put(struct nf_conntrack_expect *exp);
 int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, 
-                               u32 pid, int report);
+                               u32 portid, int report);
 static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect)
 {
        return nf_ct_expect_related_report(expect, 0, 0);
index e991bd0a27afcaa87c4afcb5622188b9b5070f30..31f1fb9eb78478a6db6339065b69b280adb26916 100644 (file)
@@ -49,12 +49,18 @@ struct nf_logger {
 int nf_log_register(u_int8_t pf, struct nf_logger *logger);
 void nf_log_unregister(struct nf_logger *logger);
 
-int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger);
-void nf_log_unbind_pf(u_int8_t pf);
+void nf_log_set(struct net *net, u_int8_t pf,
+               const struct nf_logger *logger);
+void nf_log_unset(struct net *net, const struct nf_logger *logger);
+
+int nf_log_bind_pf(struct net *net, u_int8_t pf,
+                  const struct nf_logger *logger);
+void nf_log_unbind_pf(struct net *net, u_int8_t pf);
 
 /* Calls the registered backend logging function */
-__printf(7, 8)
-void nf_log_packet(u_int8_t pf,
+__printf(8, 9)
+void nf_log_packet(struct net *net,
+                  u_int8_t pf,
                   unsigned int hooknum,
                   const struct sk_buff *skb,
                   const struct net_device *in,
index 1242f371718b7aa0db39f62269b806b1babe4883..005e2c2e39a9022bad13f4205343263f1821cb22 100644 (file)
@@ -71,6 +71,7 @@ struct netns_ipv6 {
        struct fib_rules_ops    *mr6_rules_ops;
 #endif
 #endif
+       atomic_t                dev_addr_genid;
 };
 
 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
diff --git a/include/net/netns/netfilter.h b/include/net/netns/netfilter.h
new file mode 100644 (file)
index 0000000..8874002
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __NETNS_NETFILTER_H
+#define __NETNS_NETFILTER_H
+
+#include <linux/proc_fs.h>
+#include <linux/netfilter.h>
+
+struct nf_logger;
+
+struct netns_nf {
+#if defined CONFIG_PROC_FS
+       struct proc_dir_entry *proc_netfilter;
+#endif
+       const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO];
+#ifdef CONFIG_SYSCTL
+       struct ctl_table_header *nf_log_dir_header;
+#endif
+};
+#endif
index 1d04b6f0fbd42e2dff23663c8ac21b8b8d6a16cf..50ab8c26ab59b7ebe840e50d6f696d16f32969b8 100644 (file)
@@ -29,7 +29,7 @@ struct cgroup_netprio_state {
        struct cgroup_subsys_state css;
 };
 
-extern void sock_update_netprioidx(struct sock *sk, struct task_struct *task);
+extern void sock_update_netprioidx(struct sock *sk);
 
 #if IS_BUILTIN(CONFIG_NETPRIO_CGROUP)
 
@@ -68,7 +68,7 @@ static inline u32 task_netprioidx(struct task_struct *p)
        return 0;
 }
 
-#define sock_update_netprioidx(sk, task)
+#define sock_update_netprioidx(sk)
 
 #endif /* CONFIG_NETPRIO_CGROUP */
 
index a51dbd17c2de0cede7d134667e641477030519b0..59795e42c8b61464512184990c3a12698c9a184f 100644 (file)
@@ -27,19 +27,13 @@ struct sk_buff;
 struct dst_entry;
 struct proto;
 
-/* empty to "strongly type" an otherwise void parameter.
- */
-struct request_values {
-};
-
 struct request_sock_ops {
        int             family;
        int             obj_size;
        struct kmem_cache       *slab;
        char            *slab_name;
        int             (*rtx_syn_ack)(struct sock *sk,
-                                      struct request_sock *req,
-                                      struct request_values *rvp);
+                                      struct request_sock *req);
        void            (*send_ack)(struct sock *sk, struct sk_buff *skb,
                                    struct request_sock *req);
        void            (*send_reset)(struct sock *sk,
@@ -54,7 +48,7 @@ extern int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req);
 /* struct request_sock - mini sock to represent a connection request
  */
 struct request_sock {
-       struct request_sock             *dl_next; /* Must be first member! */
+       struct request_sock             *dl_next;
        u16                             mss;
        u8                              num_retrans; /* number of retransmits */
        u8                              cookie_ts:1; /* syncookie: encode tcpopts in timestamp */
index 5a15fabd6a750eb2fca1e832285a5de0179a5085..702664833a53d65c24e3b60316bbf37804e013c3 100644 (file)
@@ -4,7 +4,7 @@
 #include <linux/rtnetlink.h>
 #include <net/netlink.h>
 
-typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, void *);
+typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *);
 typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *);
 typedef u16 (*rtnl_calcit_func)(struct sk_buff *, struct nlmsghdr *);
 
index b11708105681e04f5c24f7ed206f732e2c37dc02..8de2d37d2077f2df685baec887b1f836a8fa6514 100644 (file)
@@ -26,7 +26,6 @@ struct scm_fp_list {
 
 struct scm_cookie {
        struct pid              *pid;           /* Skb credentials */
-       const struct cred       *cred;
        struct scm_fp_list      *fp;            /* Passed files         */
        struct scm_creds        creds;          /* Skb credentials      */
 #ifdef CONFIG_SECURITY_NETWORK
@@ -51,23 +50,18 @@ static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_co
 #endif /* CONFIG_SECURITY_NETWORK */
 
 static __inline__ void scm_set_cred(struct scm_cookie *scm,
-                                   struct pid *pid, const struct cred *cred)
+                                   struct pid *pid, kuid_t uid, kgid_t gid)
 {
        scm->pid  = get_pid(pid);
-       scm->cred = cred ? get_cred(cred) : NULL;
        scm->creds.pid = pid_vnr(pid);
-       scm->creds.uid = cred ? cred->uid : INVALID_UID;
-       scm->creds.gid = cred ? cred->gid : INVALID_GID;
+       scm->creds.uid = uid;
+       scm->creds.gid = gid;
 }
 
 static __inline__ void scm_destroy_cred(struct scm_cookie *scm)
 {
        put_pid(scm->pid);
        scm->pid  = NULL;
-
-       if (scm->cred)
-               put_cred(scm->cred);
-       scm->cred = NULL;
 }
 
 static __inline__ void scm_destroy(struct scm_cookie *scm)
@@ -81,8 +75,10 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
                               struct scm_cookie *scm, bool forcecreds)
 {
        memset(scm, 0, sizeof(*scm));
+       scm->creds.uid = INVALID_UID;
+       scm->creds.gid = INVALID_GID;
        if (forcecreds)
-               scm_set_cred(scm, task_tgid(current), current_cred());
+               scm_set_cred(scm, task_tgid(current), current_uid(), current_gid());
        unix_get_peersec_dgram(sock, scm);
        if (msg->msg_controllen <= 0)
                return 0;
index a7dd5c50df7986372e3ee76cd3470c6a76c69b72..ca50e0751e47aabda8beac5a8af9eeff07dc88db 100644 (file)
@@ -49,7 +49,6 @@
 
 #include <linux/sctp.h>
 #include <linux/ipv6.h> /* For ipv6hdr. */
-#include <net/sctp/user.h>
 #include <net/tcp_states.h>  /* For TCP states used in sctp_sock_state_t */
 
 /* Value used for stream negotiation. */
index 0e0f9d2322e3c6a9e9997361fc3cee4806760824..1bd4c4144fe8b7c360ec45900b1e35018d0c1662 100644 (file)
@@ -399,7 +399,6 @@ struct sctp_stream {
 struct sctp_ssnmap {
        struct sctp_stream in;
        struct sctp_stream out;
-       int malloced;
 };
 
 struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out,
@@ -715,8 +714,7 @@ struct sctp_packet {
            has_sack:1,         /* This packet contains a SACK chunk. */
            has_auth:1,         /* This packet contains an AUTH chunk */
            has_data:1,         /* This packet contains at least 1 DATA chunk */
-           ipfragok:1,         /* So let ip fragment this packet */
-           malloced:1;         /* Is it malloced? */
+           ipfragok:1;         /* So let ip fragment this packet */
 };
 
 struct sctp_packet *sctp_packet_init(struct sctp_packet *,
@@ -780,10 +778,7 @@ struct sctp_transport {
                hb_sent:1,
 
                /* Is the Path MTU update pending on this tranport */
-               pmtu_pending:1,
-
-               /* Is this structure kfree()able? */
-               malloced:1;
+               pmtu_pending:1;
 
        /* Has this transport moved the ctsn since we last sacked */
        __u32 sack_generation;
@@ -992,8 +987,6 @@ struct sctp_inq {
         * messages.
         */
        struct work_struct immediate;
-
-       int malloced;        /* Is this structure kfree()able?  */
 };
 
 void sctp_inq_init(struct sctp_inq *);
@@ -1062,9 +1055,6 @@ struct sctp_outq {
 
        /* Is this structure empty?  */
        char empty;
-
-       /* Are we kfree()able? */
-       char malloced;
 };
 
 void sctp_outq_init(struct sctp_association *, struct sctp_outq *);
@@ -1102,8 +1092,6 @@ struct sctp_bind_addr {
         *      peer(s) in INIT and INIT ACK chunks.
         */
        struct list_head address_list;
-
-       int malloced;        /* Are we kfree()able?  */
 };
 
 void sctp_bind_addr_init(struct sctp_bind_addr *, __u16 port);
@@ -1174,11 +1162,9 @@ struct sctp_ep_common {
        /* Some fields to help us manage this object.
         *   refcnt   - Reference count access to this object.
         *   dead     - Do not attempt to use this object.
-        *   malloced - Do we need to kfree this object?
         */
        atomic_t    refcnt;
-       char        dead;
-       char        malloced;
+       bool        dead;
 
        /* What socket does this endpoint belong to?  */
        struct sock *sk;
index ff1b8ba73ab10b5678562919e6c43db0f768821b..00e50ba3f24b71a2db54d810ee9889f674444a7b 100644 (file)
@@ -49,7 +49,6 @@
 
 /* A structure to carry information to the ULP (e.g. Sockets API) */
 struct sctp_ulpq {
-       char malloced;
        char pd_mode;
        struct sctp_association *asoc;
        struct sk_buff_head reasm;
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
deleted file mode 100644 (file)
index 9a0ae09..0000000
+++ /dev/null
@@ -1,782 +0,0 @@
-/* SCTP kernel implementation
- * (C) Copyright IBM Corp. 2001, 2004
- * Copyright (c) 1999-2000 Cisco, Inc.
- * Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2002 Intel Corp.
- *
- * This file is part of the SCTP kernel implementation
- *
- * This header represents the structures and constants needed to support
- * the SCTP Extension to the Sockets API.
- *
- * This SCTP implementation 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, or (at your option)
- * any later version.
- *
- * This SCTP implementation is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- *                 ************************
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING.  If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Please send any bug reports or fixes you make to the
- * email address(es):
- *    lksctp developers <lksctp-developers@lists.sourceforge.net>
- *
- * Or submit a bug report through the following website:
- *    http://www.sf.net/projects/lksctp
- *
- * Written or modified by:
- *    La Monte H.P. Yarroll    <piggy@acm.org>
- *    R. Stewart               <randall@sctp.chicago.il.us>
- *    K. Morneau               <kmorneau@cisco.com>
- *    Q. Xie                   <qxie1@email.mot.com>
- *    Karl Knutson             <karl@athena.chicago.il.us>
- *    Jon Grimm                <jgrimm@us.ibm.com>
- *    Daisy Chang              <daisyc@us.ibm.com>
- *    Ryan Layer               <rmlayer@us.ibm.com>
- *    Ardelle Fan             <ardelle.fan@intel.com>
- *    Sridhar Samudrala        <sri@us.ibm.com>
- *
- * Any bugs reported given to us we will try to fix... any fixes shared will
- * be incorporated into the next SCTP release.
- */
-
-#ifndef __net_sctp_user_h__
-#define __net_sctp_user_h__
-
-#include <linux/types.h>
-#include <linux/socket.h>
-
-typedef __s32 sctp_assoc_t;
-
-/* The following symbols come from the Sockets API Extensions for
- * SCTP <draft-ietf-tsvwg-sctpsocket-07.txt>.
- */
-#define SCTP_RTOINFO   0
-#define SCTP_ASSOCINFO  1
-#define SCTP_INITMSG   2
-#define SCTP_NODELAY   3               /* Get/set nodelay option. */
-#define SCTP_AUTOCLOSE 4
-#define SCTP_SET_PEER_PRIMARY_ADDR 5
-#define SCTP_PRIMARY_ADDR      6
-#define SCTP_ADAPTATION_LAYER  7
-#define SCTP_DISABLE_FRAGMENTS 8
-#define SCTP_PEER_ADDR_PARAMS  9
-#define SCTP_DEFAULT_SEND_PARAM        10
-#define SCTP_EVENTS    11
-#define SCTP_I_WANT_MAPPED_V4_ADDR 12  /* Turn on/off mapped v4 addresses  */
-#define SCTP_MAXSEG    13              /* Get/set maximum fragment. */
-#define SCTP_STATUS    14
-#define SCTP_GET_PEER_ADDR_INFO        15
-#define SCTP_DELAYED_ACK_TIME  16
-#define SCTP_DELAYED_ACK SCTP_DELAYED_ACK_TIME
-#define SCTP_DELAYED_SACK SCTP_DELAYED_ACK_TIME
-#define SCTP_CONTEXT   17
-#define SCTP_FRAGMENT_INTERLEAVE       18
-#define SCTP_PARTIAL_DELIVERY_POINT    19 /* Set/Get partial delivery point */
-#define SCTP_MAX_BURST 20              /* Set/Get max burst */
-#define SCTP_AUTH_CHUNK        21      /* Set only: add a chunk type to authenticate */
-#define SCTP_HMAC_IDENT        22
-#define SCTP_AUTH_KEY  23
-#define SCTP_AUTH_ACTIVE_KEY   24
-#define SCTP_AUTH_DELETE_KEY   25
-#define SCTP_PEER_AUTH_CHUNKS  26      /* Read only */
-#define SCTP_LOCAL_AUTH_CHUNKS 27      /* Read only */
-#define SCTP_GET_ASSOC_NUMBER  28      /* Read only */
-#define SCTP_GET_ASSOC_ID_LIST 29      /* Read only */
-#define SCTP_AUTO_ASCONF       30
-#define SCTP_PEER_ADDR_THLDS   31
-
-/* Internal Socket Options. Some of the sctp library functions are
- * implemented using these socket options.
- */
-#define SCTP_SOCKOPT_BINDX_ADD 100     /* BINDX requests for adding addrs */
-#define SCTP_SOCKOPT_BINDX_REM 101     /* BINDX requests for removing addrs. */
-#define SCTP_SOCKOPT_PEELOFF   102     /* peel off association. */
-/* Options 104-106 are deprecated and removed. Do not use this space */
-#define SCTP_SOCKOPT_CONNECTX_OLD      107     /* CONNECTX old requests. */
-#define SCTP_GET_PEER_ADDRS    108             /* Get all peer address. */
-#define SCTP_GET_LOCAL_ADDRS   109             /* Get all local address. */
-#define SCTP_SOCKOPT_CONNECTX  110             /* CONNECTX requests. */
-#define SCTP_SOCKOPT_CONNECTX3 111     /* CONNECTX requests (updated) */
-#define SCTP_GET_ASSOC_STATS   112     /* Read only */
-
-/*
- * 5.2.1 SCTP Initiation Structure (SCTP_INIT)
- *
- *   This cmsghdr structure provides information for initializing new
- *   SCTP associations with sendmsg().  The SCTP_INITMSG socket option
- *   uses this same data structure.  This structure is not used for
- *   recvmsg().
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   ----------------------
- *   IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
- *
- */
-struct sctp_initmsg {
-       __u16 sinit_num_ostreams;
-       __u16 sinit_max_instreams;
-       __u16 sinit_max_attempts;
-       __u16 sinit_max_init_timeo;
-};
-
-/*
- * 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
- *
- *   This cmsghdr structure specifies SCTP options for sendmsg() and
- *   describes SCTP header information about a received message through
- *   recvmsg().
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   ----------------------
- *   IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
- *
- */
-struct sctp_sndrcvinfo {
-       __u16 sinfo_stream;
-       __u16 sinfo_ssn;
-       __u16 sinfo_flags;
-       __u32 sinfo_ppid;
-       __u32 sinfo_context;
-       __u32 sinfo_timetolive;
-       __u32 sinfo_tsn;
-       __u32 sinfo_cumtsn;
-       sctp_assoc_t sinfo_assoc_id;
-};
-
-/*
- *  sinfo_flags: 16 bits (unsigned integer)
- *
- *   This field may contain any of the following flags and is composed of
- *   a bitwise OR of these values.
- */
-
-enum sctp_sinfo_flags {
-       SCTP_UNORDERED = 1,  /* Send/receive message unordered. */
-       SCTP_ADDR_OVER = 2,  /* Override the primary destination. */
-       SCTP_ABORT=4,        /* Send an ABORT message to the peer. */
-       SCTP_SACK_IMMEDIATELY = 8,      /* SACK should be sent without delay */
-       SCTP_EOF=MSG_FIN,    /* Initiate graceful shutdown process. */  
-};
-
-
-/* These are cmsg_types.  */
-typedef enum sctp_cmsg_type {
-       SCTP_INIT,              /* 5.2.1 SCTP Initiation Structure */
-       SCTP_SNDRCV,            /* 5.2.2 SCTP Header Information Structure */
-} sctp_cmsg_t;
-
-
-/*
- * 5.3.1.1 SCTP_ASSOC_CHANGE
- *
- *   Communication notifications inform the ULP that an SCTP association
- *   has either begun or ended. The identifier for a new association is
- *   provided by this notificaion. The notification information has the
- *   following format:
- *
- */
-struct sctp_assoc_change {
-       __u16 sac_type;
-       __u16 sac_flags;
-       __u32 sac_length;
-       __u16 sac_state;
-       __u16 sac_error;
-       __u16 sac_outbound_streams;
-       __u16 sac_inbound_streams;
-       sctp_assoc_t sac_assoc_id;
-       __u8 sac_info[0];
-};
-
-/*
- *   sac_state: 32 bits (signed integer)
- *
- *   This field holds one of a number of values that communicate the
- *   event that happened to the association.  They include:
- *
- *   Note:  The following state names deviate from the API draft as
- *   the names clash too easily with other kernel symbols.
- */
-enum sctp_sac_state {
-       SCTP_COMM_UP,
-       SCTP_COMM_LOST,
-       SCTP_RESTART,
-       SCTP_SHUTDOWN_COMP,
-       SCTP_CANT_STR_ASSOC,
-};
-
-/*
- * 5.3.1.2 SCTP_PEER_ADDR_CHANGE
- *
- *   When a destination address on a multi-homed peer encounters a change
- *   an interface details event is sent.  The information has the
- *   following structure:
- */
-struct sctp_paddr_change {
-       __u16 spc_type;
-       __u16 spc_flags;
-       __u32 spc_length;
-       struct sockaddr_storage spc_aaddr;
-       int spc_state;
-       int spc_error;
-       sctp_assoc_t spc_assoc_id;
-} __attribute__((packed, aligned(4)));
-
-/*
- *    spc_state:  32 bits (signed integer)
- *
- *   This field holds one of a number of values that communicate the
- *   event that happened to the address.  They include:
- */
-enum sctp_spc_state {
-       SCTP_ADDR_AVAILABLE,
-       SCTP_ADDR_UNREACHABLE,
-       SCTP_ADDR_REMOVED,
-       SCTP_ADDR_ADDED,
-       SCTP_ADDR_MADE_PRIM,
-       SCTP_ADDR_CONFIRMED,
-};
-
-
-/*
- * 5.3.1.3 SCTP_REMOTE_ERROR
- *
- *   A remote peer may send an Operational Error message to its peer.
- *   This message indicates a variety of error conditions on an
- *   association. The entire error TLV as it appears on the wire is
- *   included in a SCTP_REMOTE_ERROR event.  Please refer to the SCTP
- *   specification [SCTP] and any extensions for a list of possible
- *   error formats. SCTP error TLVs have the format:
- */
-struct sctp_remote_error {
-       __u16 sre_type;
-       __u16 sre_flags;
-       __u32 sre_length;
-       __u16 sre_error;
-       sctp_assoc_t sre_assoc_id;
-       __u8 sre_data[0];
-};
-
-
-/*
- * 5.3.1.4 SCTP_SEND_FAILED
- *
- *   If SCTP cannot deliver a message it may return the message as a
- *   notification.
- */
-struct sctp_send_failed {
-       __u16 ssf_type;
-       __u16 ssf_flags;
-       __u32 ssf_length;
-       __u32 ssf_error;
-       struct sctp_sndrcvinfo ssf_info;
-       sctp_assoc_t ssf_assoc_id;
-       __u8 ssf_data[0];
-};
-
-/*
- *   ssf_flags: 16 bits (unsigned integer)
- *
- *   The flag value will take one of the following values
- *
- *   SCTP_DATA_UNSENT  - Indicates that the data was never put on
- *                       the wire.
- *
- *   SCTP_DATA_SENT    - Indicates that the data was put on the wire.
- *                       Note that this does not necessarily mean that the
- *                       data was (or was not) successfully delivered.
- */
-enum sctp_ssf_flags {
-       SCTP_DATA_UNSENT,
-       SCTP_DATA_SENT,
-};
-
-/*
- * 5.3.1.5 SCTP_SHUTDOWN_EVENT
- *
- *   When a peer sends a SHUTDOWN, SCTP delivers this notification to
- *   inform the application that it should cease sending data.
- */
-struct sctp_shutdown_event {
-       __u16 sse_type;
-       __u16 sse_flags;
-       __u32 sse_length;
-       sctp_assoc_t sse_assoc_id;
-};
-
-/*
- * 5.3.1.6 SCTP_ADAPTATION_INDICATION
- *
- *   When a peer sends a Adaptation Layer Indication parameter , SCTP
- *   delivers this notification to inform the application
- *   that of the peers requested adaptation layer.
- */
-struct sctp_adaptation_event {
-       __u16 sai_type;
-       __u16 sai_flags;
-       __u32 sai_length;
-       __u32 sai_adaptation_ind;
-       sctp_assoc_t sai_assoc_id;
-};
-
-/*
- * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
- *
- *   When a receiver is engaged in a partial delivery of a
- *   message this notification will be used to indicate
- *   various events.
- */
-struct sctp_pdapi_event {
-       __u16 pdapi_type;
-       __u16 pdapi_flags;
-       __u32 pdapi_length;
-       __u32 pdapi_indication;
-       sctp_assoc_t pdapi_assoc_id;
-};
-
-enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
-
-struct sctp_authkey_event {
-       __u16 auth_type;
-       __u16 auth_flags;
-       __u32 auth_length;
-       __u16 auth_keynumber;
-       __u16 auth_altkeynumber;
-       __u32 auth_indication;
-       sctp_assoc_t auth_assoc_id;
-};
-
-enum { SCTP_AUTH_NEWKEY = 0, };
-
-/*
- * 6.1.9. SCTP_SENDER_DRY_EVENT
- *
- * When the SCTP stack has no more user data to send or retransmit, this
- * notification is given to the user. Also, at the time when a user app
- * subscribes to this event, if there is no data to be sent or
- * retransmit, the stack will immediately send up this notification.
- */
-struct sctp_sender_dry_event {
-       __u16 sender_dry_type;
-       __u16 sender_dry_flags;
-       __u32 sender_dry_length;
-       sctp_assoc_t sender_dry_assoc_id;
-};
-
-/*
- * Described in Section 7.3
- *   Ancillary Data and Notification Interest Options
- */
-struct sctp_event_subscribe {
-       __u8 sctp_data_io_event;
-       __u8 sctp_association_event;
-       __u8 sctp_address_event;
-       __u8 sctp_send_failure_event;
-       __u8 sctp_peer_error_event;
-       __u8 sctp_shutdown_event;
-       __u8 sctp_partial_delivery_event;
-       __u8 sctp_adaptation_layer_event;
-       __u8 sctp_authentication_event;
-       __u8 sctp_sender_dry_event;
-};
-
-/*
- * 5.3.1 SCTP Notification Structure
- *
- *   The notification structure is defined as the union of all
- *   notification types.
- *
- */
-union sctp_notification {
-       struct {
-               __u16 sn_type;             /* Notification type. */
-               __u16 sn_flags;
-               __u32 sn_length;
-       } sn_header;
-       struct sctp_assoc_change sn_assoc_change;
-       struct sctp_paddr_change sn_paddr_change;
-       struct sctp_remote_error sn_remote_error;
-       struct sctp_send_failed sn_send_failed;
-       struct sctp_shutdown_event sn_shutdown_event;
-       struct sctp_adaptation_event sn_adaptation_event;
-       struct sctp_pdapi_event sn_pdapi_event;
-       struct sctp_authkey_event sn_authkey_event;
-       struct sctp_sender_dry_event sn_sender_dry_event;
-};
-
-/* Section 5.3.1
- * All standard values for sn_type flags are greater than 2^15.
- * Values from 2^15 and down are reserved.
- */
-
-enum sctp_sn_type {
-       SCTP_SN_TYPE_BASE     = (1<<15),
-       SCTP_ASSOC_CHANGE,
-       SCTP_PEER_ADDR_CHANGE,
-       SCTP_SEND_FAILED,
-       SCTP_REMOTE_ERROR,
-       SCTP_SHUTDOWN_EVENT,
-       SCTP_PARTIAL_DELIVERY_EVENT,
-       SCTP_ADAPTATION_INDICATION,
-       SCTP_AUTHENTICATION_EVENT,
-#define SCTP_AUTHENTICATION_INDICATION SCTP_AUTHENTICATION_EVENT
-       SCTP_SENDER_DRY_EVENT,
-};
-
-/* Notification error codes used to fill up the error fields in some
- * notifications.
- * SCTP_PEER_ADDRESS_CHAGE     : spc_error
- * SCTP_ASSOC_CHANGE           : sac_error
- * These names should be potentially included in the draft 04 of the SCTP
- * sockets API specification.
- */
-typedef enum sctp_sn_error {
-       SCTP_FAILED_THRESHOLD,
-       SCTP_RECEIVED_SACK,
-       SCTP_HEARTBEAT_SUCCESS,
-       SCTP_RESPONSE_TO_USER_REQ,
-       SCTP_INTERNAL_ERROR,
-       SCTP_SHUTDOWN_GUARD_EXPIRES,
-       SCTP_PEER_FAULTY,
-} sctp_sn_error_t;
-
-/*
- * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO)
- *
- *   The protocol parameters used to initialize and bound retransmission
- *   timeout (RTO) are tunable.  See [SCTP] for more information on how
- *   these parameters are used in RTO calculation. 
- */
-struct sctp_rtoinfo {
-       sctp_assoc_t    srto_assoc_id;
-       __u32           srto_initial;
-       __u32           srto_max;
-       __u32           srto_min;
-};
-
-/*
- * 7.1.2 Association Parameters (SCTP_ASSOCINFO)
- *
- *   This option is used to both examine and set various association and
- *   endpoint parameters.
- */
-struct sctp_assocparams {
-       sctp_assoc_t    sasoc_assoc_id;
-       __u16           sasoc_asocmaxrxt;
-       __u16           sasoc_number_peer_destinations;
-       __u32           sasoc_peer_rwnd;
-       __u32           sasoc_local_rwnd;
-       __u32           sasoc_cookie_life;
-};
-
-/*
- * 7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
- *
- *  Requests that the peer mark the enclosed address as the association
- *  primary. The enclosed address must be one of the association's
- *  locally bound addresses. The following structure is used to make a
- *   set primary request:
- */
-struct sctp_setpeerprim {
-       sctp_assoc_t            sspp_assoc_id;
-       struct sockaddr_storage sspp_addr;
-} __attribute__((packed, aligned(4)));
-
-/*
- * 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
- *
- *  Requests that the local SCTP stack use the enclosed peer address as
- *  the association primary. The enclosed address must be one of the
- *  association peer's addresses. The following structure is used to
- *  make a set peer primary request:
- */
-struct sctp_prim {
-       sctp_assoc_t            ssp_assoc_id;
-       struct sockaddr_storage ssp_addr;
-} __attribute__((packed, aligned(4)));
-
-/*
- * 7.1.11 Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER)
- *
- * Requests that the local endpoint set the specified Adaptation Layer
- * Indication parameter for all future INIT and INIT-ACK exchanges.
- */
-struct sctp_setadaptation {
-       __u32   ssb_adaptation_ind;
-};
-
-/*
- * 7.1.13 Peer Address Parameters  (SCTP_PEER_ADDR_PARAMS)
- *
- *   Applications can enable or disable heartbeats for any peer address
- *   of an association, modify an address's heartbeat interval, force a
- *   heartbeat to be sent immediately, and adjust the address's maximum
- *   number of retransmissions sent before an address is considered
- *   unreachable. The following structure is used to access and modify an
- *   address's parameters:
- */
-enum  sctp_spp_flags {
-       SPP_HB_ENABLE = 1<<0,           /*Enable heartbeats*/
-       SPP_HB_DISABLE = 1<<1,          /*Disable heartbeats*/
-       SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE,
-       SPP_HB_DEMAND = 1<<2,           /*Send heartbeat immediately*/
-       SPP_PMTUD_ENABLE = 1<<3,        /*Enable PMTU discovery*/
-       SPP_PMTUD_DISABLE = 1<<4,       /*Disable PMTU discovery*/
-       SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE,
-       SPP_SACKDELAY_ENABLE = 1<<5,    /*Enable SACK*/
-       SPP_SACKDELAY_DISABLE = 1<<6,   /*Disable SACK*/
-       SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE,
-       SPP_HB_TIME_IS_ZERO = 1<<7,     /* Set HB delay to 0 */
-};
-
-struct sctp_paddrparams {
-       sctp_assoc_t            spp_assoc_id;
-       struct sockaddr_storage spp_address;
-       __u32                   spp_hbinterval;
-       __u16                   spp_pathmaxrxt;
-       __u32                   spp_pathmtu;
-       __u32                   spp_sackdelay;
-       __u32                   spp_flags;
-} __attribute__((packed, aligned(4)));
-
-/*
- * 7.1.18.  Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
- *
- * This set option adds a chunk type that the user is requesting to be
- * received only in an authenticated way.  Changes to the list of chunks
- * will only effect future associations on the socket.
- */
-struct sctp_authchunk {
-       __u8            sauth_chunk;
-};
-
-/*
- * 7.1.19.  Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
- *
- * This option gets or sets the list of HMAC algorithms that the local
- * endpoint requires the peer to use.
-*/
-struct sctp_hmacalgo {
-       __u32           shmac_num_idents;
-       __u16           shmac_idents[];
-};
-
-/*
- * 7.1.20.  Set a shared key (SCTP_AUTH_KEY)
- *
- * This option will set a shared secret key which is used to build an
- * association shared key.
- */
-struct sctp_authkey {
-       sctp_assoc_t    sca_assoc_id;
-       __u16           sca_keynumber;
-       __u16           sca_keylength;
-       __u8            sca_key[];
-};
-
-/*
- * 7.1.21.  Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
- *
- * This option will get or set the active shared key to be used to build
- * the association shared key.
- */
-
-struct sctp_authkeyid {
-       sctp_assoc_t    scact_assoc_id;
-       __u16           scact_keynumber;
-};
-
-
-/*
- * 7.1.23.  Get or set delayed ack timer (SCTP_DELAYED_SACK)
- *
- * This option will effect the way delayed acks are performed.  This
- * option allows you to get or set the delayed ack time, in
- * milliseconds.  It also allows changing the delayed ack frequency.
- * Changing the frequency to 1 disables the delayed sack algorithm.  If
- * the assoc_id is 0, then this sets or gets the endpoints default
- * values.  If the assoc_id field is non-zero, then the set or get
- * effects the specified association for the one to many model (the
- * assoc_id field is ignored by the one to one model).  Note that if
- * sack_delay or sack_freq are 0 when setting this option, then the
- * current values will remain unchanged.
- */
-struct sctp_sack_info {
-       sctp_assoc_t    sack_assoc_id;
-       uint32_t        sack_delay;
-       uint32_t        sack_freq;
-};
-
-struct sctp_assoc_value {
-    sctp_assoc_t            assoc_id;
-    uint32_t                assoc_value;
-};
-
-/*
- * 7.2.2 Peer Address Information
- *
- *   Applications can retrieve information about a specific peer address
- *   of an association, including its reachability state, congestion
- *   window, and retransmission timer values.  This information is
- *   read-only. The following structure is used to access this
- *   information:
- */
-struct sctp_paddrinfo {
-       sctp_assoc_t            spinfo_assoc_id;
-       struct sockaddr_storage spinfo_address;
-       __s32                   spinfo_state;
-       __u32                   spinfo_cwnd;
-       __u32                   spinfo_srtt;
-       __u32                   spinfo_rto;
-       __u32                   spinfo_mtu;
-} __attribute__((packed, aligned(4)));
-
-/* Peer addresses's state. */
-/* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x]
- * calls.
- * UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters.
- *              Not yet confirmed by a heartbeat and not available for data
- *             transfers.
- * ACTIVE : Peer address confirmed, active and available for data transfers.
- * INACTIVE: Peer address inactive and not available for data transfers.
- */
-enum sctp_spinfo_state {
-       SCTP_INACTIVE,
-       SCTP_PF,
-       SCTP_ACTIVE,
-       SCTP_UNCONFIRMED,
-       SCTP_UNKNOWN = 0xffff  /* Value used for transport state unknown */
-};
-
-/*
- * 7.2.1 Association Status (SCTP_STATUS)
- *
- *   Applications can retrieve current status information about an
- *   association, including association state, peer receiver window size,
- *   number of unacked data chunks, and number of data chunks pending
- *   receipt.  This information is read-only.  The following structure is
- *   used to access this information:
- */
-struct sctp_status {
-       sctp_assoc_t            sstat_assoc_id;
-       __s32                   sstat_state;
-       __u32                   sstat_rwnd;
-       __u16                   sstat_unackdata;
-       __u16                   sstat_penddata;
-       __u16                   sstat_instrms;
-       __u16                   sstat_outstrms;
-       __u32                   sstat_fragmentation_point;
-       struct sctp_paddrinfo   sstat_primary;
-};
-
-/*
- * 7.2.3.  Get the list of chunks the peer requires to be authenticated
- *         (SCTP_PEER_AUTH_CHUNKS)
- *
- * This option gets a list of chunks for a specified association that
- * the peer requires to be received authenticated only.
- */
-struct sctp_authchunks {
-       sctp_assoc_t    gauth_assoc_id;
-       __u32           gauth_number_of_chunks;
-       uint8_t         gauth_chunks[];
-};
-
-/*
- * 8.2.6. Get the Current Identifiers of Associations
- *        (SCTP_GET_ASSOC_ID_LIST)
- *
- * This option gets the current list of SCTP association identifiers of
- * the SCTP associations handled by a one-to-many style socket.
- */
-struct sctp_assoc_ids {
-       __u32           gaids_number_of_ids;
-       sctp_assoc_t    gaids_assoc_id[];
-};
-
-/*
- * 8.3, 8.5 get all peer/local addresses in an association.
- * This parameter struct is used by SCTP_GET_PEER_ADDRS and 
- * SCTP_GET_LOCAL_ADDRS socket options used internally to implement
- * sctp_getpaddrs() and sctp_getladdrs() API. 
- */
-struct sctp_getaddrs_old {
-       sctp_assoc_t            assoc_id;
-       int                     addr_num;
-       struct sockaddr         __user *addrs;
-};
-struct sctp_getaddrs {
-       sctp_assoc_t            assoc_id; /*input*/
-       __u32                   addr_num; /*output*/
-       __u8                    addrs[0]; /*output, variable size*/
-};
-
-/* A socket user request obtained via SCTP_GET_ASSOC_STATS that retrieves
- * association stats. All stats are counts except sas_maxrto and
- * sas_obs_rto_ipaddr. maxrto is the max observed rto + transport since
- * the last call. Will return 0 when RTO was not update since last call
- */
-struct sctp_assoc_stats {
-       sctp_assoc_t    sas_assoc_id;    /* Input */
-                                        /* Transport of observed max RTO */
-       struct sockaddr_storage sas_obs_rto_ipaddr;
-       __u64           sas_maxrto;      /* Maximum Observed RTO for period */
-       __u64           sas_isacks;      /* SACKs received */
-       __u64           sas_osacks;      /* SACKs sent */
-       __u64           sas_opackets;    /* Packets sent */
-       __u64           sas_ipackets;    /* Packets received */
-       __u64           sas_rtxchunks;   /* Retransmitted Chunks */
-       __u64           sas_outofseqtsns;/* TSN received > next expected */
-       __u64           sas_idupchunks;  /* Dups received (ordered+unordered) */
-       __u64           sas_gapcnt;      /* Gap Acknowledgements Received */
-       __u64           sas_ouodchunks;  /* Unordered data chunks sent */
-       __u64           sas_iuodchunks;  /* Unordered data chunks received */
-       __u64           sas_oodchunks;   /* Ordered data chunks sent */
-       __u64           sas_iodchunks;   /* Ordered data chunks received */
-       __u64           sas_octrlchunks; /* Control chunks sent */
-       __u64           sas_ictrlchunks; /* Control chunks received */
-};
-
-/* These are bit fields for msghdr->msg_flags.  See section 5.1.  */
-/* On user space Linux, these live in <bits/socket.h> as an enum.  */
-enum sctp_msg_flags {
-       MSG_NOTIFICATION = 0x8000,
-#define MSG_NOTIFICATION MSG_NOTIFICATION
-};
-
-/*
- * 8.1 sctp_bindx()
- *
- * The flags parameter is formed from the bitwise OR of zero or more of the
- * following currently defined flags:
- */
-#define SCTP_BINDX_ADD_ADDR 0x01
-#define SCTP_BINDX_REM_ADDR 0x02
-
-/* This is the structure that is passed as an argument(optval) to
- * getsockopt(SCTP_SOCKOPT_PEELOFF).
- */
-typedef struct {
-       sctp_assoc_t associd;
-       int sd;
-} sctp_peeloff_arg_t;
-
-/*
- *  Peer Address Thresholds socket option
- */
-struct sctp_paddrthlds {
-       sctp_assoc_t spt_assoc_id;
-       struct sockaddr_storage spt_address;
-       __u16 spt_pathmaxrxt;
-       __u16 spt_pathpfthld;
-};
-#endif /* __net_sctp_user_h__ */
index 14f6e9d19dc735135c263022fa394173e89aea6e..5c97b0fc5623aa4bb44db9082d6631ad0a27fafe 100644 (file)
@@ -667,6 +667,7 @@ enum sock_flags {
                     * user-space instead.
                     */
        SOCK_FILTER_LOCKED, /* Filter cannot be changed anymore */
+       SOCK_SELECT_ERR_QUEUE, /* Wake select on error queue */
 };
 
 static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
@@ -2158,10 +2159,9 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
  * @sk:                socket sending this packet
  * @tx_flags:  filled with instructions for time stamping
  *
- * Currently only depends on SOCK_TIMESTAMPING* flags. Returns error code if
- * parameters are invalid.
+ * Currently only depends on SOCK_TIMESTAMPING* flags.
  */
-extern int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags);
+extern void sock_tx_timestamp(struct sock *sk, __u8 *tx_flags);
 
 /**
  * sk_eat_skb - Release a skb if it is no longer needed
index cf0694d4ad60f62be703eaec51db142b9c5c4e29..5bba80fbd1d9d92738d115cbc870bf0612cb0305 100644 (file)
@@ -179,7 +179,6 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
 #define TCPOPT_SACK             5       /* SACK Block */
 #define TCPOPT_TIMESTAMP       8       /* Better RTT estimations/PAWS */
 #define TCPOPT_MD5SIG          19      /* MD5 Signature (RFC2385) */
-#define TCPOPT_COOKIE          253     /* Cookie extension (experimental) */
 #define TCPOPT_EXP             254     /* Experimental */
 /* Magic number to be after the option value for sharing TCP
  * experimental options. See draft-ietf-tcpm-experimental-options-00.txt
@@ -273,7 +272,6 @@ extern int sysctl_tcp_app_win;
 extern int sysctl_tcp_adv_win_scale;
 extern int sysctl_tcp_tw_reuse;
 extern int sysctl_tcp_frto;
-extern int sysctl_tcp_frto_response;
 extern int sysctl_tcp_low_latency;
 extern int sysctl_tcp_dma_copybreak;
 extern int sysctl_tcp_nometrics_save;
@@ -284,7 +282,6 @@ extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;
 extern int sysctl_tcp_max_ssthresh;
-extern int sysctl_tcp_cookie_size;
 extern int sysctl_tcp_thin_linear_timeouts;
 extern int sysctl_tcp_thin_dupack;
 extern int sysctl_tcp_early_retrans;
@@ -373,6 +370,7 @@ extern int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 extern int tcp_sendpage(struct sock *sk, struct page *page, int offset,
                        size_t size, int flags);
 extern void tcp_release_cb(struct sock *sk);
+extern void tcp_wfree(struct sk_buff *skb);
 extern void tcp_write_timer_handler(struct sock *sk);
 extern void tcp_delack_timer_handler(struct sock *sk);
 extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
@@ -425,8 +423,6 @@ extern struct sock * tcp_check_req(struct sock *sk,struct sk_buff *skb,
                                   bool fastopen);
 extern int tcp_child_process(struct sock *parent, struct sock *child,
                             struct sk_buff *skb);
-extern bool tcp_use_frto(struct sock *sk);
-extern void tcp_enter_frto(struct sock *sk);
 extern void tcp_enter_loss(struct sock *sk, int how);
 extern void tcp_clear_retrans(struct tcp_sock *tp);
 extern void tcp_update_metrics(struct sock *sk);
@@ -454,7 +450,7 @@ extern void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req);
 extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                       size_t len, int nonblock, int flags, int *addr_len);
 extern void tcp_parse_options(const struct sk_buff *skb,
-                             struct tcp_options_received *opt_rx, const u8 **hvpp,
+                             struct tcp_options_received *opt_rx,
                              int estab, struct tcp_fastopen_cookie *foc);
 extern const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);
 
@@ -476,7 +472,6 @@ extern int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
 extern int tcp_connect(struct sock *sk);
 extern struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst,
                                        struct request_sock *req,
-                                       struct request_values *rvp,
                                        struct tcp_fastopen_cookie *foc);
 extern int tcp_disconnect(struct sock *sk, int flags);
 
@@ -543,6 +538,8 @@ extern bool tcp_syn_flood_action(struct sock *sk,
 extern void tcp_push_one(struct sock *, unsigned int mss_now);
 extern void tcp_send_ack(struct sock *sk);
 extern void tcp_send_delayed_ack(struct sock *sk);
+extern void tcp_send_loss_probe(struct sock *sk);
+extern bool tcp_schedule_loss_probe(struct sock *sk);
 
 /* tcp_input.c */
 extern void tcp_cwnd_application_limited(struct sock *sk);
@@ -756,7 +753,6 @@ enum tcp_ca_event {
        CA_EVENT_TX_START,      /* first transmit when no packets in flight */
        CA_EVENT_CWND_RESTART,  /* congestion window restart */
        CA_EVENT_COMPLETE_CWR,  /* end of congestion recovery */
-       CA_EVENT_FRTO,          /* fast recovery timeout */
        CA_EVENT_LOSS,          /* loss timeout */
        CA_EVENT_FAST_ACK,      /* in sequence ack */
        CA_EVENT_SLOW_ACK,      /* other ack */
@@ -873,8 +869,8 @@ static inline void tcp_enable_fack(struct tcp_sock *tp)
 static inline void tcp_enable_early_retrans(struct tcp_sock *tp)
 {
        tp->do_early_retrans = sysctl_tcp_early_retrans &&
-               !sysctl_tcp_thin_dupack && sysctl_tcp_reordering == 3;
-       tp->early_retrans_delayed = 0;
+               sysctl_tcp_early_retrans < 4 && !sysctl_tcp_thin_dupack &&
+               sysctl_tcp_reordering == 3;
 }
 
 static inline void tcp_disable_early_retrans(struct tcp_sock *tp)
@@ -1030,50 +1026,7 @@ static inline void tcp_prequeue_init(struct tcp_sock *tp)
 #endif
 }
 
-/* Packet is added to VJ-style prequeue for processing in process
- * context, if a reader task is waiting. Apparently, this exciting
- * idea (VJ's mail "Re: query about TCP header on tcp-ip" of 07 Sep 93)
- * failed somewhere. Latency? Burstiness? Well, at least now we will
- * see, why it failed. 8)8)                              --ANK
- *
- * NOTE: is this not too big to inline?
- */
-static inline bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-
-       if (sysctl_tcp_low_latency || !tp->ucopy.task)
-               return false;
-
-       if (skb->len <= tcp_hdrlen(skb) &&
-           skb_queue_len(&tp->ucopy.prequeue) == 0)
-               return false;
-
-       __skb_queue_tail(&tp->ucopy.prequeue, skb);
-       tp->ucopy.memory += skb->truesize;
-       if (tp->ucopy.memory > sk->sk_rcvbuf) {
-               struct sk_buff *skb1;
-
-               BUG_ON(sock_owned_by_user(sk));
-
-               while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
-                       sk_backlog_rcv(sk, skb1);
-                       NET_INC_STATS_BH(sock_net(sk),
-                                        LINUX_MIB_TCPPREQUEUEDROPPED);
-               }
-
-               tp->ucopy.memory = 0;
-       } else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
-               wake_up_interruptible_sync_poll(sk_sleep(sk),
-                                          POLLIN | POLLRDNORM | POLLRDBAND);
-               if (!inet_csk_ack_scheduled(sk))
-                       inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
-                                                 (3 * tcp_rto_min(sk)) / 4,
-                                                 TCP_RTO_MAX);
-       }
-       return true;
-}
-
+extern bool tcp_prequeue(struct sock *sk, struct sk_buff *skb);
 
 #undef STATE_TRACE
 
@@ -1630,91 +1583,6 @@ struct tcp_request_sock_ops {
 #endif
 };
 
-/* Using SHA1 for now, define some constants.
- */
-#define COOKIE_DIGEST_WORDS (SHA_DIGEST_WORDS)
-#define COOKIE_MESSAGE_WORDS (SHA_MESSAGE_BYTES / 4)
-#define COOKIE_WORKSPACE_WORDS (COOKIE_DIGEST_WORDS + COOKIE_MESSAGE_WORDS)
-
-extern int tcp_cookie_generator(u32 *bakery);
-
-/**
- *     struct tcp_cookie_values - each socket needs extra space for the
- *     cookies, together with (optional) space for any SYN data.
- *
- *     A tcp_sock contains a pointer to the current value, and this is
- *     cloned to the tcp_timewait_sock.
- *
- * @cookie_pair:       variable data from the option exchange.
- *
- * @cookie_desired:    user specified tcpct_cookie_desired.  Zero
- *                     indicates default (sysctl_tcp_cookie_size).
- *                     After cookie sent, remembers size of cookie.
- *                     Range 0, TCP_COOKIE_MIN to TCP_COOKIE_MAX.
- *
- * @s_data_desired:    user specified tcpct_s_data_desired.  When the
- *                     constant payload is specified (@s_data_constant),
- *                     holds its length instead.
- *                     Range 0 to TCP_MSS_DESIRED.
- *
- * @s_data_payload:    constant data that is to be included in the
- *                     payload of SYN or SYNACK segments when the
- *                     cookie option is present.
- */
-struct tcp_cookie_values {
-       struct kref     kref;
-       u8              cookie_pair[TCP_COOKIE_PAIR_SIZE];
-       u8              cookie_pair_size;
-       u8              cookie_desired;
-       u16             s_data_desired:11,
-                       s_data_constant:1,
-                       s_data_in:1,
-                       s_data_out:1,
-                       s_data_unused:2;
-       u8              s_data_payload[0];
-};
-
-static inline void tcp_cookie_values_release(struct kref *kref)
-{
-       kfree(container_of(kref, struct tcp_cookie_values, kref));
-}
-
-/* The length of constant payload data.  Note that s_data_desired is
- * overloaded, depending on s_data_constant: either the length of constant
- * data (returned here) or the limit on variable data.
- */
-static inline int tcp_s_data_size(const struct tcp_sock *tp)
-{
-       return (tp->cookie_values != NULL && tp->cookie_values->s_data_constant)
-               ? tp->cookie_values->s_data_desired
-               : 0;
-}
-
-/**
- *     struct tcp_extend_values - tcp_ipv?.c to tcp_output.c workspace.
- *
- *     As tcp_request_sock has already been extended in other places, the
- *     only remaining method is to pass stack values along as function
- *     parameters.  These parameters are not needed after sending SYNACK.
- *
- * @cookie_bakery:     cryptographic secret and message workspace.
- *
- * @cookie_plus:       bytes in authenticator/cookie option, copied from
- *                     struct tcp_options_received (above).
- */
-struct tcp_extend_values {
-       struct request_values           rv;
-       u32                             cookie_bakery[COOKIE_WORKSPACE_WORDS];
-       u8                              cookie_plus:6,
-                                       cookie_out_never:1,
-                                       cookie_in_always:1;
-};
-
-static inline struct tcp_extend_values *tcp_xv(struct request_values *rvp)
-{
-       return (struct tcp_extend_values *)rvp;
-}
-
 extern void tcp_v4_init(void);
 extern void tcp_init(void);
 
index 24c8886fd9699969c74908bf1e78efdcbda0694b..ae16531d0d353741ac66c83e0464341d060d5cce 100644 (file)
@@ -162,6 +162,7 @@ struct xfrm_state {
                xfrm_address_t  saddr;
                int             header_len;
                int             trailer_len;
+               u32             extra_flags;
        } props;
 
        struct xfrm_lifetime_cfg lft;
index 4ef3acbba5daeba94e21ab2a9c4700c9b9cf2108..c5d2e3a1cf68743862049482406f2a49adbfb707 100644 (file)
@@ -74,4 +74,6 @@
 
 #define SO_LOCK_FILTER         44
 
+#define SO_SELECT_ERR_QUEUE    45
+
 #endif /* __ASM_GENERIC_SOCKET_H */
index 5c8a1d25e21c15060f3740fe2e83c5c291b26d82..ab5d4992e568f31109c41c505c3d919216a61acb 100644 (file)
@@ -285,6 +285,7 @@ header-y += nvram.h
 header-y += omap3isp.h
 header-y += omapfb.h
 header-y += oom.h
+header-y += openvswitch.h
 header-y += packet_diag.h
 header-y += param.h
 header-y += parport.h
@@ -331,6 +332,7 @@ header-y += rtnetlink.h
 header-y += scc.h
 header-y += sched.h
 header-y += screen_info.h
+header-y += sctp.h
 header-y += sdla.h
 header-y += seccomp.h
 header-y += securebits.h
index 0d7b49973bb35451254d51d2d24645588678af2e..f6c271035bbd2c5eb6deb10cf89b04b6fa0f710a 100644 (file)
@@ -56,7 +56,9 @@ struct proc_event {
                PROC_EVENT_PTRACE = 0x00000100,
                PROC_EVENT_COMM = 0x00000200,
                /* "next" should be 0x00000400 */
-               /* "last" is the last process event: exit */
+               /* "last" is the last process event: exit,
+                * while "next to last" is coredumping event */
+               PROC_EVENT_COREDUMP = 0x40000000,
                PROC_EVENT_EXIT = 0x80000000
        } what;
        __u32 cpu;
@@ -110,11 +112,17 @@ struct proc_event {
                        char           comm[16];
                } comm;
 
+               struct coredump_proc_event {
+                       __kernel_pid_t process_pid;
+                       __kernel_pid_t process_tgid;
+               } coredump;
+
                struct exit_proc_event {
                        __kernel_pid_t process_pid;
                        __kernel_pid_t process_tgid;
                        __u32 exit_code, exit_signal;
                } exit;
+
        } event_data;
 };
 
index 9cfde6941099635ee567d28e240f8cbc02386f51..8eb9ccaa5b48124b716e5abe7741aef4ca711d9f 100644 (file)
@@ -129,7 +129,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */
 #define SKF_AD_ALU_XOR_X       40
 #define SKF_AD_VLAN_TAG        44
 #define SKF_AD_VLAN_TAG_PRESENT 48
-#define SKF_AD_MAX     52
+#define SKF_AD_PAY_OFFSET      52
+#define SKF_AD_MAX     56
 #define SKF_NET_OFF   (-0x100000)
 #define SKF_LL_OFF    (-0x200000)
 
index 798032d01112fbe6d168922be3e9b61a8fa2cfe2..ade07f1c491ae7e946ceafa796b1ab07e39c8b04 100644 (file)
@@ -94,6 +94,9 @@
 #define ETH_P_EDSA     0xDADA          /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_AF_IUCV   0xFBFB         /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
 
+#define ETH_P_802_3_MIN        0x0600          /* If the value in the ethernet type is less than this value
+                                        * then the frame is Ethernet II. Else it is 802.3 */
+
 /*
  *     Non DIX types. Won't clash for 1500 types.
  */
index c4edfe11f1f7ed07092d085f28d49934a58f4903..e3163544f3391be88ce4a98b1673a763d4e304c9 100644 (file)
@@ -201,6 +201,7 @@ enum {
        IFLA_INET6_MCAST,       /* MC things. What of them?     */
        IFLA_INET6_CACHEINFO,   /* time values and max reasm size */
        IFLA_INET6_ICMP6STATS,  /* statistics (icmpv6)          */
+       IFLA_INET6_TOKEN,       /* device token                 */
        __IFLA_INET6_MAX
 };
 
@@ -249,6 +250,7 @@ enum {
        IFLA_VLAN_FLAGS,
        IFLA_VLAN_EGRESS_QOS,
        IFLA_VLAN_INGRESS_QOS,
+       IFLA_VLAN_PROTOCOL,
        __IFLA_VLAN_MAX,
 };
 
@@ -295,7 +297,7 @@ enum macvlan_mode {
 enum {
        IFLA_VXLAN_UNSPEC,
        IFLA_VXLAN_ID,
-       IFLA_VXLAN_GROUP,
+       IFLA_VXLAN_REMOTE,
        IFLA_VXLAN_LINK,
        IFLA_VXLAN_LOCAL,
        IFLA_VXLAN_TTL,
index f9a60375f0d0121855af7f3a157afccc909bf547..8136658ea477021a373e963287816af0bc6474e7 100644 (file)
@@ -55,6 +55,8 @@ struct sockaddr_ll {
 #define PACKET_FANOUT_HASH             0
 #define PACKET_FANOUT_LB               1
 #define PACKET_FANOUT_CPU              2
+#define PACKET_FANOUT_ROLLOVER         3
+#define PACKET_FANOUT_FLAG_ROLLOVER    0x1000
 #define PACKET_FANOUT_FLAG_DEFRAG      0x8000
 
 struct tpacket_stats {
index adb068c53c4e964fc1ea950a61f188b9159bc178..f175212420ab0bd1f4ffbdae7cac60457b77bb3c 100644 (file)
@@ -21,6 +21,9 @@ enum {
        NDA_CACHEINFO,
        NDA_PROBES,
        NDA_VLAN,
+       NDA_PORT,
+       NDA_VNI,
+       NDA_IFINDEX,
        __NDA_MAX
 };
 
index 9eafdbbb401cd3609095c2395e541d5ddef0b869..8bb5fe657d34bbeba55f1026cbc667745c52fd42 100644 (file)
@@ -26,4 +26,13 @@ struct xt_NFQ_info_v2 {
        __u16 bypass;
 };
 
+struct xt_NFQ_info_v3 {
+       __u16 queuenum;
+       __u16 queues_total;
+       __u16 flags;
+#define NFQ_FLAG_BYPASS                0x01 /* for compatibility with v2 */
+#define NFQ_FLAG_CPU_FANOUT    0x02 /* use current CPU (no hashing) */
+#define NFQ_FLAG_MASK          0x03
+};
+
 #endif /* _XT_NFQ_TARGET_H */
index b47f61b9e082f1281023076577fa2e182aa2b616..dfd8bc2268cf274d71cecad52b4fd068a9724c97 100644 (file)
@@ -4,9 +4,9 @@
 #include <linux/types.h>
 
 struct ip6t_frag {
-       __u32 ids[2];                   /* Security Parameter Index */
+       __u32 ids[2];                   /* Identification range */
        __u32 hdrlen;                   /* Header Length */
-       __u8  flags;                    /*  */
+       __u8  flags;                    /* Flags */
        __u8  invflags;                 /* Inverse flags */
 };
 
index 78d5b8a546d63fb73cf45a2ab08a48ce02486dba..1a85940f8ab733e85f50ade2ee25f8a527316a89 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _UAPI__LINUX_NETLINK_H
 #define _UAPI__LINUX_NETLINK_H
 
+#include <linux/kernel.h>
 #include <linux/socket.h> /* for __kernel_sa_family_t */
 #include <linux/types.h>
 
@@ -78,7 +79,7 @@ struct nlmsghdr {
 #define NLMSG_ALIGNTO  4U
 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
 #define NLMSG_HDRLEN    ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
-#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
 #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
 #define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
 #define NLMSG_NEXT(nlh,len)     ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
@@ -105,11 +106,42 @@ struct nlmsgerr {
 #define NETLINK_PKTINFO                3
 #define NETLINK_BROADCAST_ERROR        4
 #define NETLINK_NO_ENOBUFS     5
+#define NETLINK_RX_RING                6
+#define NETLINK_TX_RING                7
 
 struct nl_pktinfo {
        __u32   group;
 };
 
+struct nl_mmap_req {
+       unsigned int    nm_block_size;
+       unsigned int    nm_block_nr;
+       unsigned int    nm_frame_size;
+       unsigned int    nm_frame_nr;
+};
+
+struct nl_mmap_hdr {
+       unsigned int    nm_status;
+       unsigned int    nm_len;
+       __u32           nm_group;
+       /* credentials */
+       __u32           nm_pid;
+       __u32           nm_uid;
+       __u32           nm_gid;
+};
+
+enum nl_mmap_status {
+       NL_MMAP_STATUS_UNUSED,
+       NL_MMAP_STATUS_RESERVED,
+       NL_MMAP_STATUS_VALID,
+       NL_MMAP_STATUS_COPY,
+       NL_MMAP_STATUS_SKIP,
+};
+
+#define NL_MMAP_MSG_ALIGNMENT          NLMSG_ALIGNTO
+#define NL_MMAP_MSG_ALIGN(sz)          __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
+#define NL_MMAP_HDRLEN                 NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
+
 #define NET_MAJOR 36           /* Major 36 is reserved for networking                                          */
 
 enum {
diff --git a/include/uapi/linux/netlink_diag.h b/include/uapi/linux/netlink_diag.h
new file mode 100644 (file)
index 0000000..4e31db4
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef __NETLINK_DIAG_H__
+#define __NETLINK_DIAG_H__
+
+#include <linux/types.h>
+
+struct netlink_diag_req {
+       __u8    sdiag_family;
+       __u8    sdiag_protocol;
+       __u16   pad;
+       __u32   ndiag_ino;
+       __u32   ndiag_show;
+       __u32   ndiag_cookie[2];
+};
+
+struct netlink_diag_msg {
+       __u8    ndiag_family;
+       __u8    ndiag_type;
+       __u8    ndiag_protocol;
+       __u8    ndiag_state;
+
+       __u32   ndiag_portid;
+       __u32   ndiag_dst_portid;
+       __u32   ndiag_dst_group;
+       __u32   ndiag_ino;
+       __u32   ndiag_cookie[2];
+};
+
+struct netlink_diag_ring {
+       __u32   ndr_block_size;
+       __u32   ndr_block_nr;
+       __u32   ndr_frame_size;
+       __u32   ndr_frame_nr;
+};
+
+enum {
+       NETLINK_DIAG_MEMINFO,
+       NETLINK_DIAG_GROUPS,
+       NETLINK_DIAG_RX_RING,
+       NETLINK_DIAG_TX_RING,
+
+       __NETLINK_DIAG_MAX,
+};
+
+#define NETLINK_DIAG_MAX (__NETLINK_DIAG_MAX - 1)
+
+#define NDIAG_PROTO_ALL                ((__u8) ~0)
+
+#define NDIAG_SHOW_MEMINFO     0x00000001 /* show memory info of a socket */
+#define NDIAG_SHOW_GROUPS      0x00000002 /* show groups of a netlink socket */
+#define NDIAG_SHOW_RING_CFG    0x00000004 /* show ring configuration */
+
+#endif
index 7969f46f1bb344f55d704ae06b5bcdc609b07b85..7440bc81a04b5d8af0a8ca109bb3f4662080389e 100644 (file)
@@ -90,6 +90,8 @@ enum nfc_commands {
        NFC_CMD_LLC_SET_PARAMS,
        NFC_CMD_ENABLE_SE,
        NFC_CMD_DISABLE_SE,
+       NFC_CMD_LLC_SDREQ,
+       NFC_EVENT_LLC_SDRES,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -140,11 +142,21 @@ enum nfc_attrs {
        NFC_ATTR_LLC_PARAM_RW,
        NFC_ATTR_LLC_PARAM_MIUX,
        NFC_ATTR_SE,
+       NFC_ATTR_LLC_SDP,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
 #define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1)
 
+enum nfc_sdp_attr {
+       NFC_SDP_ATTR_UNSPEC,
+       NFC_SDP_ATTR_URI,
+       NFC_SDP_ATTR_SAP,
+/* private: internal use only */
+       __NFC_SDP_ATTR_AFTER_LAST
+};
+#define NFC_SDP_ATTR_MAX (__NFC_SDP_ATTR_AFTER_LAST - 1)
+
 #define NFC_DEVICE_NAME_MAXSIZE 8
 #define NFC_NFCID1_MAXSIZE 10
 #define NFC_SENSB_RES_MAXSIZE 12
@@ -220,4 +232,8 @@ struct sockaddr_nfc_llcp {
 #define NFC_LLCP_DIRECTION_RX          0x00
 #define NFC_LLCP_DIRECTION_TX          0x01
 
+/* socket option names */
+#define NFC_LLCP_RW   0
+#define NFC_LLCP_MIUX 1
+
 #endif /*__LINUX_NFC_H */
index c46bb016f4e42c7a5d0b734512d56887d12f9d1f..79da8710448e9ec9359bcbe031377a2208e97f5b 100644 (file)
  * The station is still assumed to belong to the AP interface it was added
  * to.
  *
- * TODO: need more info?
+ * Station handling varies per interface type and depending on the driver's
+ * capabilities.
+ *
+ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+ * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+ *  - a setup station entry is added, not yet authorized, without any rate
+ *    or capability information, this just exists to avoid race conditions
+ *  - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+ *    to add rate and capability information to the station and at the same
+ *    time mark it authorized.
+ *  - %NL80211_TDLS_ENABLE_LINK is then used
+ *  - after this, the only valid operation is to remove it by tearing down
+ *    the TDLS link (%NL80211_TDLS_DISABLE_LINK)
+ *
+ * TODO: need more info for other interface types
  */
 
 /**
  * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a
  *      beacon or probe response from a compatible mesh peer.  This is only
  *      sent while no station information (sta_info) exists for the new peer
- *      candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set.  On
- *      reception of this notification, userspace may decide to create a new
- *      station (@NL80211_CMD_NEW_STATION).  To stop this notification from
+ *      candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH,
+ *      @NL80211_MESH_SETUP_USERSPACE_AMPE, or
+ *      @NL80211_MESH_SETUP_USERSPACE_MPM is set.  On reception of this
+ *      notification, userspace may decide to create a new station
+ *      (@NL80211_CMD_NEW_STATION).  To stop this notification from
  *      reoccurring, the userspace authentication daemon may want to create the
  *      new station with the AUTHENTICATED flag unset and maybe change it later
  *      depending on the authentication result.
  *     %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
  *     event.
  *
+ * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
+ *     i.e. features for the nl80211 protocol rather than device features.
+ *     Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
+ *
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ *     Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ *     to the supplicant. This will carry the target AP's MAC address along
+ *     with the relevant Information Elements. This event is used to report
+ *     received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE).
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -765,6 +793,11 @@ enum nl80211_commands {
 
        NL80211_CMD_RADAR_DETECT,
 
+       NL80211_CMD_GET_PROTOCOL_FEATURES,
+
+       NL80211_CMD_UPDATE_FT_IES,
+       NL80211_CMD_FT_EVENT,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -884,7 +917,8 @@ enum nl80211_commands {
  *     consisting of a nested array.
  *
  * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
- * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link
+ *     (see &enum nl80211_plink_action).
  * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
  * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
  *     info given for %NL80211_CMD_GET_MPATH, nested attribute described at
@@ -1167,10 +1201,10 @@ enum nl80211_commands {
  * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
  *     allows auth frames in a mesh to be passed to userspace for processing via
  *     the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
- * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as
- *     defined in &enum nl80211_plink_state. Used when userspace is
- *     driving the peer link management state machine.
- *     @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in
+ *     &enum nl80211_plink_state. Used when userspace is driving the peer link
+ *     management state machine.  @NL80211_MESH_SETUP_USERSPACE_AMPE or
+ *     @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled.
  *
  * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
  *     capabilities, the supported WoWLAN triggers
@@ -1368,6 +1402,18 @@ enum nl80211_commands {
  *     advertised to the driver, e.g., to enable TDLS off channel operations
  *     and PU-APSD.
  *
+ * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
+ *     &enum nl80211_protocol_features, the attribute is a u32.
+ *
+ * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
+ *     receiving the data for a single wiphy split across multiple
+ *     messages, given with wiphy dump message
+ *
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information
+ *     Element
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1654,6 +1700,15 @@ enum nl80211_attrs {
        NL80211_ATTR_STA_CAPABILITY,
        NL80211_ATTR_STA_EXT_CAPABILITY,
 
+       NL80211_ATTR_PROTOCOL_FEATURES,
+       NL80211_ATTR_SPLIT_WIPHY_DUMP,
+
+       NL80211_ATTR_DISABLE_VHT,
+       NL80211_ATTR_VHT_CAPABILITY_MASK,
+
+       NL80211_ATTR_MDID,
+       NL80211_ATTR_IE_RIC,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2412,8 +2467,10 @@ enum nl80211_mesh_power_mode {
  * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
  *     point.
  *
- * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
- *     open peer links when we detect compatible mesh peers.
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open
+ *     peer links when we detect compatible mesh peers. Disabled if
+ *     @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are
+ *     set.
  *
  * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
  *     containing a PREQ that an MP can send to a particular destination (path
@@ -2559,6 +2616,9 @@ enum nl80211_meshconf_params {
  *     vendor specific synchronization method or disable it to use the default
  *     neighbor offset synchronization
  *
+ * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
+ *     implement an MPM which handles peer allocation and state.
+ *
  * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
  *
  * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
@@ -2571,6 +2631,7 @@ enum nl80211_mesh_setup_params {
        NL80211_MESH_SETUP_USERSPACE_AUTH,
        NL80211_MESH_SETUP_USERSPACE_AMPE,
        NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
+       NL80211_MESH_SETUP_USERSPACE_MPM,
 
        /* keep last */
        __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
@@ -3307,6 +3368,23 @@ enum nl80211_plink_state {
        MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
 };
 
+/**
+ * enum nl80211_plink_action - actions to perform in mesh peers
+ *
+ * @NL80211_PLINK_ACTION_NO_ACTION: perform no action
+ * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment
+ * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+ * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+ */
+enum plink_actions {
+       NL80211_PLINK_ACTION_NO_ACTION,
+       NL80211_PLINK_ACTION_OPEN,
+       NL80211_PLINK_ACTION_BLOCK,
+
+       NUM_NL80211_PLINK_ACTIONS,
+};
+
+
 #define NL80211_KCK_LEN                        16
 #define NL80211_KEK_LEN                        16
 #define NL80211_REPLAY_CTR_LEN         8
@@ -3456,6 +3534,10 @@ enum nl80211_ap_sme_features {
  *     stations the authenticated/associated bits have to be set in the mask.
  * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits
  *     (HT40, VHT 80/160 MHz) if this flag is set
+ * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh
+ *     Peering Management entity which may be implemented by registering for
+ *     beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
+ *     still generated by the driver.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
@@ -3474,6 +3556,7 @@ enum nl80211_feature_flags {
        /* bit 13 is reserved */
        NL80211_FEATURE_ADVERTISE_CHAN_LIMITS           = 1 << 14,
        NL80211_FEATURE_FULL_AP_CLIENT_STATE            = 1 << 15,
+       NL80211_FEATURE_USERSPACE_MPM                   = 1 << 16,
 };
 
 /**
@@ -3587,4 +3670,16 @@ enum nl80211_dfs_state {
        NL80211_DFS_AVAILABLE,
 };
 
+/**
+ * enum enum nl80211_protocol_features - nl80211 protocol features
+ * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
+ *     wiphy dumps (if requested by the application with the attribute
+ *     %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
+ *     wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
+ *     %NL80211_ATTR_WDEV.
+ */
+enum nl80211_protocol_features {
+       NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP =     1 << 0,
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
new file mode 100644 (file)
index 0000000..405918d
--- /dev/null
@@ -0,0 +1,456 @@
+
+/*
+ * Copyright (c) 2007-2011 Nicira Networks.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _UAPI__LINUX_OPENVSWITCH_H
+#define _UAPI__LINUX_OPENVSWITCH_H 1
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+/**
+ * struct ovs_header - header for OVS Generic Netlink messages.
+ * @dp_ifindex: ifindex of local port for datapath (0 to make a request not
+ * specific to a datapath).
+ *
+ * Attributes following the header are specific to a particular OVS Generic
+ * Netlink family, but all of the OVS families use this header.
+ */
+
+struct ovs_header {
+       int dp_ifindex;
+};
+
+/* Datapaths. */
+
+#define OVS_DATAPATH_FAMILY  "ovs_datapath"
+#define OVS_DATAPATH_MCGROUP "ovs_datapath"
+#define OVS_DATAPATH_VERSION 0x1
+
+enum ovs_datapath_cmd {
+       OVS_DP_CMD_UNSPEC,
+       OVS_DP_CMD_NEW,
+       OVS_DP_CMD_DEL,
+       OVS_DP_CMD_GET,
+       OVS_DP_CMD_SET
+};
+
+/**
+ * enum ovs_datapath_attr - attributes for %OVS_DP_* commands.
+ * @OVS_DP_ATTR_NAME: Name of the network device that serves as the "local
+ * port".  This is the name of the network device whose dp_ifindex is given in
+ * the &struct ovs_header.  Always present in notifications.  Required in
+ * %OVS_DP_NEW requests.  May be used as an alternative to specifying
+ * dp_ifindex in other requests (with a dp_ifindex of 0).
+ * @OVS_DP_ATTR_UPCALL_PID: The Netlink socket in userspace that is initially
+ * set on the datapath port (for OVS_ACTION_ATTR_MISS).  Only valid on
+ * %OVS_DP_CMD_NEW requests. A value of zero indicates that upcalls should
+ * not be sent.
+ * @OVS_DP_ATTR_STATS: Statistics about packets that have passed through the
+ * datapath.  Always present in notifications.
+ *
+ * These attributes follow the &struct ovs_header within the Generic Netlink
+ * payload for %OVS_DP_* commands.
+ */
+enum ovs_datapath_attr {
+       OVS_DP_ATTR_UNSPEC,
+       OVS_DP_ATTR_NAME,       /* name of dp_ifindex netdev */
+       OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */
+       OVS_DP_ATTR_STATS,      /* struct ovs_dp_stats */
+       __OVS_DP_ATTR_MAX
+};
+
+#define OVS_DP_ATTR_MAX (__OVS_DP_ATTR_MAX - 1)
+
+struct ovs_dp_stats {
+       __u64 n_hit;             /* Number of flow table matches. */
+       __u64 n_missed;          /* Number of flow table misses. */
+       __u64 n_lost;            /* Number of misses not sent to userspace. */
+       __u64 n_flows;           /* Number of flows present */
+};
+
+struct ovs_vport_stats {
+       __u64   rx_packets;             /* total packets received       */
+       __u64   tx_packets;             /* total packets transmitted    */
+       __u64   rx_bytes;               /* total bytes received         */
+       __u64   tx_bytes;               /* total bytes transmitted      */
+       __u64   rx_errors;              /* bad packets received         */
+       __u64   tx_errors;              /* packet transmit problems     */
+       __u64   rx_dropped;             /* no space in linux buffers    */
+       __u64   tx_dropped;             /* no space available in linux  */
+};
+
+/* Fixed logical ports. */
+#define OVSP_LOCAL      ((__u32)0)
+
+/* Packet transfer. */
+
+#define OVS_PACKET_FAMILY "ovs_packet"
+#define OVS_PACKET_VERSION 0x1
+
+enum ovs_packet_cmd {
+       OVS_PACKET_CMD_UNSPEC,
+
+       /* Kernel-to-user notifications. */
+       OVS_PACKET_CMD_MISS,    /* Flow table miss. */
+       OVS_PACKET_CMD_ACTION,  /* OVS_ACTION_ATTR_USERSPACE action. */
+
+       /* Userspace commands. */
+       OVS_PACKET_CMD_EXECUTE  /* Apply actions to a packet. */
+};
+
+/**
+ * enum ovs_packet_attr - attributes for %OVS_PACKET_* commands.
+ * @OVS_PACKET_ATTR_PACKET: Present for all notifications.  Contains the entire
+ * packet as received, from the start of the Ethernet header onward.  For
+ * %OVS_PACKET_CMD_ACTION, %OVS_PACKET_ATTR_PACKET reflects changes made by
+ * actions preceding %OVS_ACTION_ATTR_USERSPACE, but %OVS_PACKET_ATTR_KEY is
+ * the flow key extracted from the packet as originally received.
+ * @OVS_PACKET_ATTR_KEY: Present for all notifications.  Contains the flow key
+ * extracted from the packet as nested %OVS_KEY_ATTR_* attributes.  This allows
+ * userspace to adapt its flow setup strategy by comparing its notion of the
+ * flow key against the kernel's.
+ * @OVS_PACKET_ATTR_ACTIONS: Contains actions for the packet.  Used
+ * for %OVS_PACKET_CMD_EXECUTE.  It has nested %OVS_ACTION_ATTR_* attributes.
+ * @OVS_PACKET_ATTR_USERDATA: Present for an %OVS_PACKET_CMD_ACTION
+ * notification if the %OVS_ACTION_ATTR_USERSPACE action specified an
+ * %OVS_USERSPACE_ATTR_USERDATA attribute, with the same length and content
+ * specified there.
+ *
+ * These attributes follow the &struct ovs_header within the Generic Netlink
+ * payload for %OVS_PACKET_* commands.
+ */
+enum ovs_packet_attr {
+       OVS_PACKET_ATTR_UNSPEC,
+       OVS_PACKET_ATTR_PACKET,      /* Packet data. */
+       OVS_PACKET_ATTR_KEY,         /* Nested OVS_KEY_ATTR_* attributes. */
+       OVS_PACKET_ATTR_ACTIONS,     /* Nested OVS_ACTION_ATTR_* attributes. */
+       OVS_PACKET_ATTR_USERDATA,    /* OVS_ACTION_ATTR_USERSPACE arg. */
+       __OVS_PACKET_ATTR_MAX
+};
+
+#define OVS_PACKET_ATTR_MAX (__OVS_PACKET_ATTR_MAX - 1)
+
+/* Virtual ports. */
+
+#define OVS_VPORT_FAMILY  "ovs_vport"
+#define OVS_VPORT_MCGROUP "ovs_vport"
+#define OVS_VPORT_VERSION 0x1
+
+enum ovs_vport_cmd {
+       OVS_VPORT_CMD_UNSPEC,
+       OVS_VPORT_CMD_NEW,
+       OVS_VPORT_CMD_DEL,
+       OVS_VPORT_CMD_GET,
+       OVS_VPORT_CMD_SET
+};
+
+enum ovs_vport_type {
+       OVS_VPORT_TYPE_UNSPEC,
+       OVS_VPORT_TYPE_NETDEV,   /* network device */
+       OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
+       __OVS_VPORT_TYPE_MAX
+};
+
+#define OVS_VPORT_TYPE_MAX (__OVS_VPORT_TYPE_MAX - 1)
+
+/**
+ * enum ovs_vport_attr - attributes for %OVS_VPORT_* commands.
+ * @OVS_VPORT_ATTR_PORT_NO: 32-bit port number within datapath.
+ * @OVS_VPORT_ATTR_TYPE: 32-bit %OVS_VPORT_TYPE_* constant describing the type
+ * of vport.
+ * @OVS_VPORT_ATTR_NAME: Name of vport.  For a vport based on a network device
+ * this is the name of the network device.  Maximum length %IFNAMSIZ-1 bytes
+ * plus a null terminator.
+ * @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information.
+ * @OVS_VPORT_ATTR_UPCALL_PID: The Netlink socket in userspace that
+ * OVS_PACKET_CMD_MISS upcalls will be directed to for packets received on
+ * this port.  A value of zero indicates that upcalls should not be sent.
+ * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for
+ * packets sent or received through the vport.
+ *
+ * These attributes follow the &struct ovs_header within the Generic Netlink
+ * payload for %OVS_VPORT_* commands.
+ *
+ * For %OVS_VPORT_CMD_NEW requests, the %OVS_VPORT_ATTR_TYPE and
+ * %OVS_VPORT_ATTR_NAME attributes are required.  %OVS_VPORT_ATTR_PORT_NO is
+ * optional; if not specified a free port number is automatically selected.
+ * Whether %OVS_VPORT_ATTR_OPTIONS is required or optional depends on the type
+ * of vport.
+ * and other attributes are ignored.
+ *
+ * For other requests, if %OVS_VPORT_ATTR_NAME is specified then it is used to
+ * look up the vport to operate on; otherwise dp_idx from the &struct
+ * ovs_header plus %OVS_VPORT_ATTR_PORT_NO determine the vport.
+ */
+enum ovs_vport_attr {
+       OVS_VPORT_ATTR_UNSPEC,
+       OVS_VPORT_ATTR_PORT_NO, /* u32 port number within datapath */
+       OVS_VPORT_ATTR_TYPE,    /* u32 OVS_VPORT_TYPE_* constant. */
+       OVS_VPORT_ATTR_NAME,    /* string name, up to IFNAMSIZ bytes long */
+       OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */
+       OVS_VPORT_ATTR_UPCALL_PID, /* u32 Netlink PID to receive upcalls */
+       OVS_VPORT_ATTR_STATS,   /* struct ovs_vport_stats */
+       __OVS_VPORT_ATTR_MAX
+};
+
+#define OVS_VPORT_ATTR_MAX (__OVS_VPORT_ATTR_MAX - 1)
+
+/* Flows. */
+
+#define OVS_FLOW_FAMILY  "ovs_flow"
+#define OVS_FLOW_MCGROUP "ovs_flow"
+#define OVS_FLOW_VERSION 0x1
+
+enum ovs_flow_cmd {
+       OVS_FLOW_CMD_UNSPEC,
+       OVS_FLOW_CMD_NEW,
+       OVS_FLOW_CMD_DEL,
+       OVS_FLOW_CMD_GET,
+       OVS_FLOW_CMD_SET
+};
+
+struct ovs_flow_stats {
+       __u64 n_packets;         /* Number of matched packets. */
+       __u64 n_bytes;           /* Number of matched bytes. */
+};
+
+enum ovs_key_attr {
+       OVS_KEY_ATTR_UNSPEC,
+       OVS_KEY_ATTR_ENCAP,     /* Nested set of encapsulated attributes. */
+       OVS_KEY_ATTR_PRIORITY,  /* u32 skb->priority */
+       OVS_KEY_ATTR_IN_PORT,   /* u32 OVS dp port number */
+       OVS_KEY_ATTR_ETHERNET,  /* struct ovs_key_ethernet */
+       OVS_KEY_ATTR_VLAN,      /* be16 VLAN TCI */
+       OVS_KEY_ATTR_ETHERTYPE, /* be16 Ethernet type */
+       OVS_KEY_ATTR_IPV4,      /* struct ovs_key_ipv4 */
+       OVS_KEY_ATTR_IPV6,      /* struct ovs_key_ipv6 */
+       OVS_KEY_ATTR_TCP,       /* struct ovs_key_tcp */
+       OVS_KEY_ATTR_UDP,       /* struct ovs_key_udp */
+       OVS_KEY_ATTR_ICMP,      /* struct ovs_key_icmp */
+       OVS_KEY_ATTR_ICMPV6,    /* struct ovs_key_icmpv6 */
+       OVS_KEY_ATTR_ARP,       /* struct ovs_key_arp */
+       OVS_KEY_ATTR_ND,        /* struct ovs_key_nd */
+       OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
+       __OVS_KEY_ATTR_MAX
+};
+
+#define OVS_KEY_ATTR_MAX (__OVS_KEY_ATTR_MAX - 1)
+
+/**
+ * enum ovs_frag_type - IPv4 and IPv6 fragment type
+ * @OVS_FRAG_TYPE_NONE: Packet is not a fragment.
+ * @OVS_FRAG_TYPE_FIRST: Packet is a fragment with offset 0.
+ * @OVS_FRAG_TYPE_LATER: Packet is a fragment with nonzero offset.
+ *
+ * Used as the @ipv4_frag in &struct ovs_key_ipv4 and as @ipv6_frag &struct
+ * ovs_key_ipv6.
+ */
+enum ovs_frag_type {
+       OVS_FRAG_TYPE_NONE,
+       OVS_FRAG_TYPE_FIRST,
+       OVS_FRAG_TYPE_LATER,
+       __OVS_FRAG_TYPE_MAX
+};
+
+#define OVS_FRAG_TYPE_MAX (__OVS_FRAG_TYPE_MAX - 1)
+
+struct ovs_key_ethernet {
+       __u8     eth_src[ETH_ALEN];
+       __u8     eth_dst[ETH_ALEN];
+};
+
+struct ovs_key_ipv4 {
+       __be32 ipv4_src;
+       __be32 ipv4_dst;
+       __u8   ipv4_proto;
+       __u8   ipv4_tos;
+       __u8   ipv4_ttl;
+       __u8   ipv4_frag;       /* One of OVS_FRAG_TYPE_*. */
+};
+
+struct ovs_key_ipv6 {
+       __be32 ipv6_src[4];
+       __be32 ipv6_dst[4];
+       __be32 ipv6_label;      /* 20-bits in least-significant bits. */
+       __u8   ipv6_proto;
+       __u8   ipv6_tclass;
+       __u8   ipv6_hlimit;
+       __u8   ipv6_frag;       /* One of OVS_FRAG_TYPE_*. */
+};
+
+struct ovs_key_tcp {
+       __be16 tcp_src;
+       __be16 tcp_dst;
+};
+
+struct ovs_key_udp {
+       __be16 udp_src;
+       __be16 udp_dst;
+};
+
+struct ovs_key_icmp {
+       __u8 icmp_type;
+       __u8 icmp_code;
+};
+
+struct ovs_key_icmpv6 {
+       __u8 icmpv6_type;
+       __u8 icmpv6_code;
+};
+
+struct ovs_key_arp {
+       __be32 arp_sip;
+       __be32 arp_tip;
+       __be16 arp_op;
+       __u8   arp_sha[ETH_ALEN];
+       __u8   arp_tha[ETH_ALEN];
+};
+
+struct ovs_key_nd {
+       __u32 nd_target[4];
+       __u8  nd_sll[ETH_ALEN];
+       __u8  nd_tll[ETH_ALEN];
+};
+
+/**
+ * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
+ * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow
+ * key.  Always present in notifications.  Required for all requests (except
+ * dumps).
+ * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying
+ * the actions to take for packets that match the key.  Always present in
+ * notifications.  Required for %OVS_FLOW_CMD_NEW requests, optional for
+ * %OVS_FLOW_CMD_SET requests.
+ * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this
+ * flow.  Present in notifications if the stats would be nonzero.  Ignored in
+ * requests.
+ * @OVS_FLOW_ATTR_TCP_FLAGS: An 8-bit value giving the OR'd value of all of the
+ * TCP flags seen on packets in this flow.  Only present in notifications for
+ * TCP flows, and only if it would be nonzero.  Ignored in requests.
+ * @OVS_FLOW_ATTR_USED: A 64-bit integer giving the time, in milliseconds on
+ * the system monotonic clock, at which a packet was last processed for this
+ * flow.  Only present in notifications if a packet has been processed for this
+ * flow.  Ignored in requests.
+ * @OVS_FLOW_ATTR_CLEAR: If present in a %OVS_FLOW_CMD_SET request, clears the
+ * last-used time, accumulated TCP flags, and statistics for this flow.
+ * Otherwise ignored in requests.  Never present in notifications.
+ *
+ * These attributes follow the &struct ovs_header within the Generic Netlink
+ * payload for %OVS_FLOW_* commands.
+ */
+enum ovs_flow_attr {
+       OVS_FLOW_ATTR_UNSPEC,
+       OVS_FLOW_ATTR_KEY,       /* Sequence of OVS_KEY_ATTR_* attributes. */
+       OVS_FLOW_ATTR_ACTIONS,   /* Nested OVS_ACTION_ATTR_* attributes. */
+       OVS_FLOW_ATTR_STATS,     /* struct ovs_flow_stats. */
+       OVS_FLOW_ATTR_TCP_FLAGS, /* 8-bit OR'd TCP flags. */
+       OVS_FLOW_ATTR_USED,      /* u64 msecs last used in monotonic time. */
+       OVS_FLOW_ATTR_CLEAR,     /* Flag to clear stats, tcp_flags, used. */
+       __OVS_FLOW_ATTR_MAX
+};
+
+#define OVS_FLOW_ATTR_MAX (__OVS_FLOW_ATTR_MAX - 1)
+
+/**
+ * enum ovs_sample_attr - Attributes for %OVS_ACTION_ATTR_SAMPLE action.
+ * @OVS_SAMPLE_ATTR_PROBABILITY: 32-bit fraction of packets to sample with
+ * @OVS_ACTION_ATTR_SAMPLE.  A value of 0 samples no packets, a value of
+ * %UINT32_MAX samples all packets and intermediate values sample intermediate
+ * fractions of packets.
+ * @OVS_SAMPLE_ATTR_ACTIONS: Set of actions to execute in sampling event.
+ * Actions are passed as nested attributes.
+ *
+ * Executes the specified actions with the given probability on a per-packet
+ * basis.
+ */
+enum ovs_sample_attr {
+       OVS_SAMPLE_ATTR_UNSPEC,
+       OVS_SAMPLE_ATTR_PROBABILITY, /* u32 number */
+       OVS_SAMPLE_ATTR_ACTIONS,     /* Nested OVS_ACTION_ATTR_* attributes. */
+       __OVS_SAMPLE_ATTR_MAX,
+};
+
+#define OVS_SAMPLE_ATTR_MAX (__OVS_SAMPLE_ATTR_MAX - 1)
+
+/**
+ * enum ovs_userspace_attr - Attributes for %OVS_ACTION_ATTR_USERSPACE action.
+ * @OVS_USERSPACE_ATTR_PID: u32 Netlink PID to which the %OVS_PACKET_CMD_ACTION
+ * message should be sent.  Required.
+ * @OVS_USERSPACE_ATTR_USERDATA: If present, its variable-length argument is
+ * copied to the %OVS_PACKET_CMD_ACTION message as %OVS_PACKET_ATTR_USERDATA.
+ */
+enum ovs_userspace_attr {
+       OVS_USERSPACE_ATTR_UNSPEC,
+       OVS_USERSPACE_ATTR_PID,       /* u32 Netlink PID to receive upcalls. */
+       OVS_USERSPACE_ATTR_USERDATA,  /* Optional user-specified cookie. */
+       __OVS_USERSPACE_ATTR_MAX
+};
+
+#define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
+
+/**
+ * struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument.
+ * @vlan_tpid: Tag protocol identifier (TPID) to push.
+ * @vlan_tci: Tag control identifier (TCI) to push.  The CFI bit must be set
+ * (but it will not be set in the 802.1Q header that is pushed).
+ *
+ * The @vlan_tpid value is typically %ETH_P_8021Q.  The only acceptable TPID
+ * values are those that the kernel module also parses as 802.1Q headers, to
+ * prevent %OVS_ACTION_ATTR_PUSH_VLAN followed by %OVS_ACTION_ATTR_POP_VLAN
+ * from having surprising results.
+ */
+struct ovs_action_push_vlan {
+       __be16 vlan_tpid;       /* 802.1Q TPID. */
+       __be16 vlan_tci;        /* 802.1Q TCI (VLAN ID and priority). */
+};
+
+/**
+ * enum ovs_action_attr - Action types.
+ *
+ * @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
+ * @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested
+ * %OVS_USERSPACE_ATTR_* attributes.
+ * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header.  The
+ * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its
+ * value.
+ * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the
+ * packet.
+ * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet.
+ * @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in
+ * the nested %OVS_SAMPLE_ATTR_* attributes.
+ *
+ * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
+ * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
+ * type may not be changed.
+ */
+
+enum ovs_action_attr {
+       OVS_ACTION_ATTR_UNSPEC,
+       OVS_ACTION_ATTR_OUTPUT,       /* u32 port number. */
+       OVS_ACTION_ATTR_USERSPACE,    /* Nested OVS_USERSPACE_ATTR_*. */
+       OVS_ACTION_ATTR_SET,          /* One nested OVS_KEY_ATTR_*. */
+       OVS_ACTION_ATTR_PUSH_VLAN,    /* struct ovs_action_push_vlan. */
+       OVS_ACTION_ATTR_POP_VLAN,     /* No argument. */
+       OVS_ACTION_ATTR_SAMPLE,       /* Nested OVS_SAMPLE_ATTR_*. */
+       __OVS_ACTION_ATTR_MAX
+};
+
+#define OVS_ACTION_ATTR_MAX (__OVS_ACTION_ATTR_MAX - 1)
+
+#endif /* _LINUX_OPENVSWITCH_H */
index 32aef0a439ef120b14c196d092a6ae67edfb74cb..dbd71b0c7d8c93edbc80ae50ad7223db3665f00a 100644 (file)
@@ -348,6 +348,7 @@ enum {
        TCA_HTB_INIT,
        TCA_HTB_CTAB,
        TCA_HTB_RTAB,
+       TCA_HTB_DIRECT_QLEN,
        __TCA_HTB_MAX,
 };
 
diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h
new file mode 100644 (file)
index 0000000..66b466e
--- /dev/null
@@ -0,0 +1,846 @@
+/* SCTP kernel implementation
+ * (C) Copyright IBM Corp. 2001, 2004
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2002 Intel Corp.
+ *
+ * This file is part of the SCTP kernel implementation
+ *
+ * This header represents the structures and constants needed to support
+ * the SCTP Extension to the Sockets API.
+ *
+ * This SCTP implementation 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, or (at your option)
+ * any later version.
+ *
+ * This SCTP implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll    <piggy@acm.org>
+ *    R. Stewart               <randall@sctp.chicago.il.us>
+ *    K. Morneau               <kmorneau@cisco.com>
+ *    Q. Xie                   <qxie1@email.mot.com>
+ *    Karl Knutson             <karl@athena.chicago.il.us>
+ *    Jon Grimm                <jgrimm@us.ibm.com>
+ *    Daisy Chang              <daisyc@us.ibm.com>
+ *    Ryan Layer               <rmlayer@us.ibm.com>
+ *    Ardelle Fan              <ardelle.fan@intel.com>
+ *    Sridhar Samudrala        <sri@us.ibm.com>
+ *    Inaky Perez-Gonzalez     <inaky.gonzalez@intel.com>
+ *    Vlad Yasevich            <vladislav.yasevich@hp.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#ifndef _UAPI_SCTP_H
+#define _UAPI_SCTP_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+typedef __s32 sctp_assoc_t;
+
+/* The following symbols come from the Sockets API Extensions for
+ * SCTP <draft-ietf-tsvwg-sctpsocket-07.txt>.
+ */
+#define SCTP_RTOINFO   0
+#define SCTP_ASSOCINFO  1
+#define SCTP_INITMSG   2
+#define SCTP_NODELAY   3               /* Get/set nodelay option. */
+#define SCTP_AUTOCLOSE 4
+#define SCTP_SET_PEER_PRIMARY_ADDR 5
+#define SCTP_PRIMARY_ADDR      6
+#define SCTP_ADAPTATION_LAYER  7
+#define SCTP_DISABLE_FRAGMENTS 8
+#define SCTP_PEER_ADDR_PARAMS  9
+#define SCTP_DEFAULT_SEND_PARAM        10
+#define SCTP_EVENTS    11
+#define SCTP_I_WANT_MAPPED_V4_ADDR 12  /* Turn on/off mapped v4 addresses  */
+#define SCTP_MAXSEG    13              /* Get/set maximum fragment. */
+#define SCTP_STATUS    14
+#define SCTP_GET_PEER_ADDR_INFO        15
+#define SCTP_DELAYED_ACK_TIME  16
+#define SCTP_DELAYED_ACK SCTP_DELAYED_ACK_TIME
+#define SCTP_DELAYED_SACK SCTP_DELAYED_ACK_TIME
+#define SCTP_CONTEXT   17
+#define SCTP_FRAGMENT_INTERLEAVE       18
+#define SCTP_PARTIAL_DELIVERY_POINT    19 /* Set/Get partial delivery point */
+#define SCTP_MAX_BURST 20              /* Set/Get max burst */
+#define SCTP_AUTH_CHUNK        21      /* Set only: add a chunk type to authenticate */
+#define SCTP_HMAC_IDENT        22
+#define SCTP_AUTH_KEY  23
+#define SCTP_AUTH_ACTIVE_KEY   24
+#define SCTP_AUTH_DELETE_KEY   25
+#define SCTP_PEER_AUTH_CHUNKS  26      /* Read only */
+#define SCTP_LOCAL_AUTH_CHUNKS 27      /* Read only */
+#define SCTP_GET_ASSOC_NUMBER  28      /* Read only */
+#define SCTP_GET_ASSOC_ID_LIST 29      /* Read only */
+#define SCTP_AUTO_ASCONF       30
+#define SCTP_PEER_ADDR_THLDS   31
+
+/* Internal Socket Options. Some of the sctp library functions are
+ * implemented using these socket options.
+ */
+#define SCTP_SOCKOPT_BINDX_ADD 100     /* BINDX requests for adding addrs */
+#define SCTP_SOCKOPT_BINDX_REM 101     /* BINDX requests for removing addrs. */
+#define SCTP_SOCKOPT_PEELOFF   102     /* peel off association. */
+/* Options 104-106 are deprecated and removed. Do not use this space */
+#define SCTP_SOCKOPT_CONNECTX_OLD      107     /* CONNECTX old requests. */
+#define SCTP_GET_PEER_ADDRS    108             /* Get all peer address. */
+#define SCTP_GET_LOCAL_ADDRS   109             /* Get all local address. */
+#define SCTP_SOCKOPT_CONNECTX  110             /* CONNECTX requests. */
+#define SCTP_SOCKOPT_CONNECTX3 111     /* CONNECTX requests (updated) */
+#define SCTP_GET_ASSOC_STATS   112     /* Read only */
+
+/*
+ * 5.2.1 SCTP Initiation Structure (SCTP_INIT)
+ *
+ *   This cmsghdr structure provides information for initializing new
+ *   SCTP associations with sendmsg().  The SCTP_INITMSG socket option
+ *   uses this same data structure.  This structure is not used for
+ *   recvmsg().
+ *
+ *   cmsg_level    cmsg_type      cmsg_data[]
+ *   ------------  ------------   ----------------------
+ *   IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
+ *
+ */
+struct sctp_initmsg {
+       __u16 sinit_num_ostreams;
+       __u16 sinit_max_instreams;
+       __u16 sinit_max_attempts;
+       __u16 sinit_max_init_timeo;
+};
+
+/*
+ * 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+ *
+ *   This cmsghdr structure specifies SCTP options for sendmsg() and
+ *   describes SCTP header information about a received message through
+ *   recvmsg().
+ *
+ *   cmsg_level    cmsg_type      cmsg_data[]
+ *   ------------  ------------   ----------------------
+ *   IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
+ *
+ */
+struct sctp_sndrcvinfo {
+       __u16 sinfo_stream;
+       __u16 sinfo_ssn;
+       __u16 sinfo_flags;
+       __u32 sinfo_ppid;
+       __u32 sinfo_context;
+       __u32 sinfo_timetolive;
+       __u32 sinfo_tsn;
+       __u32 sinfo_cumtsn;
+       sctp_assoc_t sinfo_assoc_id;
+};
+
+/*
+ *  sinfo_flags: 16 bits (unsigned integer)
+ *
+ *   This field may contain any of the following flags and is composed of
+ *   a bitwise OR of these values.
+ */
+
+enum sctp_sinfo_flags {
+       SCTP_UNORDERED = 1,  /* Send/receive message unordered. */
+       SCTP_ADDR_OVER = 2,  /* Override the primary destination. */
+       SCTP_ABORT=4,        /* Send an ABORT message to the peer. */
+       SCTP_SACK_IMMEDIATELY = 8,      /* SACK should be sent without delay */
+       SCTP_EOF=MSG_FIN,    /* Initiate graceful shutdown process. */
+};
+
+typedef union {
+       __u8                    raw;
+       struct sctp_initmsg     init;
+       struct sctp_sndrcvinfo  sndrcv;
+} sctp_cmsg_data_t;
+
+/* These are cmsg_types.  */
+typedef enum sctp_cmsg_type {
+       SCTP_INIT,              /* 5.2.1 SCTP Initiation Structure */
+#define SCTP_INIT      SCTP_INIT
+       SCTP_SNDRCV,            /* 5.2.2 SCTP Header Information Structure */
+#define SCTP_SNDRCV    SCTP_SNDRCV
+} sctp_cmsg_t;
+
+/*
+ * 5.3.1.1 SCTP_ASSOC_CHANGE
+ *
+ *   Communication notifications inform the ULP that an SCTP association
+ *   has either begun or ended. The identifier for a new association is
+ *   provided by this notificaion. The notification information has the
+ *   following format:
+ *
+ */
+struct sctp_assoc_change {
+       __u16 sac_type;
+       __u16 sac_flags;
+       __u32 sac_length;
+       __u16 sac_state;
+       __u16 sac_error;
+       __u16 sac_outbound_streams;
+       __u16 sac_inbound_streams;
+       sctp_assoc_t sac_assoc_id;
+       __u8 sac_info[0];
+};
+
+/*
+ *   sac_state: 32 bits (signed integer)
+ *
+ *   This field holds one of a number of values that communicate the
+ *   event that happened to the association.  They include:
+ *
+ *   Note:  The following state names deviate from the API draft as
+ *   the names clash too easily with other kernel symbols.
+ */
+enum sctp_sac_state {
+       SCTP_COMM_UP,
+       SCTP_COMM_LOST,
+       SCTP_RESTART,
+       SCTP_SHUTDOWN_COMP,
+       SCTP_CANT_STR_ASSOC,
+};
+
+/*
+ * 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+ *
+ *   When a destination address on a multi-homed peer encounters a change
+ *   an interface details event is sent.  The information has the
+ *   following structure:
+ */
+struct sctp_paddr_change {
+       __u16 spc_type;
+       __u16 spc_flags;
+       __u32 spc_length;
+       struct sockaddr_storage spc_aaddr;
+       int spc_state;
+       int spc_error;
+       sctp_assoc_t spc_assoc_id;
+} __attribute__((packed, aligned(4)));
+
+/*
+ *    spc_state:  32 bits (signed integer)
+ *
+ *   This field holds one of a number of values that communicate the
+ *   event that happened to the address.  They include:
+ */
+enum sctp_spc_state {
+       SCTP_ADDR_AVAILABLE,
+       SCTP_ADDR_UNREACHABLE,
+       SCTP_ADDR_REMOVED,
+       SCTP_ADDR_ADDED,
+       SCTP_ADDR_MADE_PRIM,
+       SCTP_ADDR_CONFIRMED,
+};
+
+
+/*
+ * 5.3.1.3 SCTP_REMOTE_ERROR
+ *
+ *   A remote peer may send an Operational Error message to its peer.
+ *   This message indicates a variety of error conditions on an
+ *   association. The entire error TLV as it appears on the wire is
+ *   included in a SCTP_REMOTE_ERROR event.  Please refer to the SCTP
+ *   specification [SCTP] and any extensions for a list of possible
+ *   error formats. SCTP error TLVs have the format:
+ */
+struct sctp_remote_error {
+       __u16 sre_type;
+       __u16 sre_flags;
+       __u32 sre_length;
+       __u16 sre_error;
+       sctp_assoc_t sre_assoc_id;
+       __u8 sre_data[0];
+};
+
+
+/*
+ * 5.3.1.4 SCTP_SEND_FAILED
+ *
+ *   If SCTP cannot deliver a message it may return the message as a
+ *   notification.
+ */
+struct sctp_send_failed {
+       __u16 ssf_type;
+       __u16 ssf_flags;
+       __u32 ssf_length;
+       __u32 ssf_error;
+       struct sctp_sndrcvinfo ssf_info;
+       sctp_assoc_t ssf_assoc_id;
+       __u8 ssf_data[0];
+};
+
+/*
+ *   ssf_flags: 16 bits (unsigned integer)
+ *
+ *   The flag value will take one of the following values
+ *
+ *   SCTP_DATA_UNSENT  - Indicates that the data was never put on
+ *                       the wire.
+ *
+ *   SCTP_DATA_SENT    - Indicates that the data was put on the wire.
+ *                       Note that this does not necessarily mean that the
+ *                       data was (or was not) successfully delivered.
+ */
+enum sctp_ssf_flags {
+       SCTP_DATA_UNSENT,
+       SCTP_DATA_SENT,
+};
+
+/*
+ * 5.3.1.5 SCTP_SHUTDOWN_EVENT
+ *
+ *   When a peer sends a SHUTDOWN, SCTP delivers this notification to
+ *   inform the application that it should cease sending data.
+ */
+struct sctp_shutdown_event {
+       __u16 sse_type;
+       __u16 sse_flags;
+       __u32 sse_length;
+       sctp_assoc_t sse_assoc_id;
+};
+
+/*
+ * 5.3.1.6 SCTP_ADAPTATION_INDICATION
+ *
+ *   When a peer sends a Adaptation Layer Indication parameter , SCTP
+ *   delivers this notification to inform the application
+ *   that of the peers requested adaptation layer.
+ */
+struct sctp_adaptation_event {
+       __u16 sai_type;
+       __u16 sai_flags;
+       __u32 sai_length;
+       __u32 sai_adaptation_ind;
+       sctp_assoc_t sai_assoc_id;
+};
+
+/*
+ * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
+ *
+ *   When a receiver is engaged in a partial delivery of a
+ *   message this notification will be used to indicate
+ *   various events.
+ */
+struct sctp_pdapi_event {
+       __u16 pdapi_type;
+       __u16 pdapi_flags;
+       __u32 pdapi_length;
+       __u32 pdapi_indication;
+       sctp_assoc_t pdapi_assoc_id;
+};
+
+enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
+
+/*
+ * 5.3.1.8.  SCTP_AUTHENTICATION_EVENT
+ *
+ *  When a receiver is using authentication this message will provide
+ *  notifications regarding new keys being made active as well as errors.
+ */
+struct sctp_authkey_event {
+       __u16 auth_type;
+       __u16 auth_flags;
+       __u32 auth_length;
+       __u16 auth_keynumber;
+       __u16 auth_altkeynumber;
+       __u32 auth_indication;
+       sctp_assoc_t auth_assoc_id;
+};
+
+enum { SCTP_AUTH_NEWKEY = 0, };
+
+/*
+ * 6.1.9. SCTP_SENDER_DRY_EVENT
+ *
+ * When the SCTP stack has no more user data to send or retransmit, this
+ * notification is given to the user. Also, at the time when a user app
+ * subscribes to this event, if there is no data to be sent or
+ * retransmit, the stack will immediately send up this notification.
+ */
+struct sctp_sender_dry_event {
+       __u16 sender_dry_type;
+       __u16 sender_dry_flags;
+       __u32 sender_dry_length;
+       sctp_assoc_t sender_dry_assoc_id;
+};
+
+/*
+ * Described in Section 7.3
+ *   Ancillary Data and Notification Interest Options
+ */
+struct sctp_event_subscribe {
+       __u8 sctp_data_io_event;
+       __u8 sctp_association_event;
+       __u8 sctp_address_event;
+       __u8 sctp_send_failure_event;
+       __u8 sctp_peer_error_event;
+       __u8 sctp_shutdown_event;
+       __u8 sctp_partial_delivery_event;
+       __u8 sctp_adaptation_layer_event;
+       __u8 sctp_authentication_event;
+       __u8 sctp_sender_dry_event;
+};
+
+/*
+ * 5.3.1 SCTP Notification Structure
+ *
+ *   The notification structure is defined as the union of all
+ *   notification types.
+ *
+ */
+union sctp_notification {
+       struct {
+               __u16 sn_type;             /* Notification type. */
+               __u16 sn_flags;
+               __u32 sn_length;
+       } sn_header;
+       struct sctp_assoc_change sn_assoc_change;
+       struct sctp_paddr_change sn_paddr_change;
+       struct sctp_remote_error sn_remote_error;
+       struct sctp_send_failed sn_send_failed;
+       struct sctp_shutdown_event sn_shutdown_event;
+       struct sctp_adaptation_event sn_adaptation_event;
+       struct sctp_pdapi_event sn_pdapi_event;
+       struct sctp_authkey_event sn_authkey_event;
+       struct sctp_sender_dry_event sn_sender_dry_event;
+};
+
+/* Section 5.3.1
+ * All standard values for sn_type flags are greater than 2^15.
+ * Values from 2^15 and down are reserved.
+ */
+
+enum sctp_sn_type {
+       SCTP_SN_TYPE_BASE     = (1<<15),
+       SCTP_ASSOC_CHANGE,
+#define SCTP_ASSOC_CHANGE              SCTP_ASSOC_CHANGE
+       SCTP_PEER_ADDR_CHANGE,
+#define SCTP_PEER_ADDR_CHANGE          SCTP_PEER_ADDR_CHANGE
+       SCTP_SEND_FAILED,
+#define SCTP_SEND_FAILED               SCTP_SEND_FAILED
+       SCTP_REMOTE_ERROR,
+#define SCTP_REMOTE_ERROR              SCTP_REMOTE_ERROR
+       SCTP_SHUTDOWN_EVENT,
+#define SCTP_SHUTDOWN_EVENT            SCTP_SHUTDOWN_EVENT
+       SCTP_PARTIAL_DELIVERY_EVENT,
+#define SCTP_PARTIAL_DELIVERY_EVENT    SCTP_PARTIAL_DELIVERY_EVENT
+       SCTP_ADAPTATION_INDICATION,
+#define SCTP_ADAPTATION_INDICATION     SCTP_ADAPTATION_INDICATION
+       SCTP_AUTHENTICATION_EVENT,
+#define SCTP_AUTHENTICATION_INDICATION SCTP_AUTHENTICATION_EVENT
+       SCTP_SENDER_DRY_EVENT,
+#define SCTP_SENDER_DRY_EVENT          SCTP_SENDER_DRY_EVENT
+};
+
+/* Notification error codes used to fill up the error fields in some
+ * notifications.
+ * SCTP_PEER_ADDRESS_CHAGE     : spc_error
+ * SCTP_ASSOC_CHANGE           : sac_error
+ * These names should be potentially included in the draft 04 of the SCTP
+ * sockets API specification.
+ */
+typedef enum sctp_sn_error {
+       SCTP_FAILED_THRESHOLD,
+       SCTP_RECEIVED_SACK,
+       SCTP_HEARTBEAT_SUCCESS,
+       SCTP_RESPONSE_TO_USER_REQ,
+       SCTP_INTERNAL_ERROR,
+       SCTP_SHUTDOWN_GUARD_EXPIRES,
+       SCTP_PEER_FAULTY,
+} sctp_sn_error_t;
+
+/*
+ * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO)
+ *
+ *   The protocol parameters used to initialize and bound retransmission
+ *   timeout (RTO) are tunable.  See [SCTP] for more information on how
+ *   these parameters are used in RTO calculation.
+ */
+struct sctp_rtoinfo {
+       sctp_assoc_t    srto_assoc_id;
+       __u32           srto_initial;
+       __u32           srto_max;
+       __u32           srto_min;
+};
+
+/*
+ * 7.1.2 Association Parameters (SCTP_ASSOCINFO)
+ *
+ *   This option is used to both examine and set various association and
+ *   endpoint parameters.
+ */
+struct sctp_assocparams {
+       sctp_assoc_t    sasoc_assoc_id;
+       __u16           sasoc_asocmaxrxt;
+       __u16           sasoc_number_peer_destinations;
+       __u32           sasoc_peer_rwnd;
+       __u32           sasoc_local_rwnd;
+       __u32           sasoc_cookie_life;
+};
+
+/*
+ * 7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
+ *
+ *  Requests that the peer mark the enclosed address as the association
+ *  primary. The enclosed address must be one of the association's
+ *  locally bound addresses. The following structure is used to make a
+ *   set primary request:
+ */
+struct sctp_setpeerprim {
+       sctp_assoc_t            sspp_assoc_id;
+       struct sockaddr_storage sspp_addr;
+} __attribute__((packed, aligned(4)));
+
+/*
+ * 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
+ *
+ *  Requests that the local SCTP stack use the enclosed peer address as
+ *  the association primary. The enclosed address must be one of the
+ *  association peer's addresses. The following structure is used to
+ *  make a set peer primary request:
+ */
+struct sctp_prim {
+       sctp_assoc_t            ssp_assoc_id;
+       struct sockaddr_storage ssp_addr;
+} __attribute__((packed, aligned(4)));
+
+/* For backward compatibility use, define the old name too */
+#define sctp_setprim   sctp_prim
+
+/*
+ * 7.1.11 Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER)
+ *
+ * Requests that the local endpoint set the specified Adaptation Layer
+ * Indication parameter for all future INIT and INIT-ACK exchanges.
+ */
+struct sctp_setadaptation {
+       __u32   ssb_adaptation_ind;
+};
+
+/*
+ * 7.1.13 Peer Address Parameters  (SCTP_PEER_ADDR_PARAMS)
+ *
+ *   Applications can enable or disable heartbeats for any peer address
+ *   of an association, modify an address's heartbeat interval, force a
+ *   heartbeat to be sent immediately, and adjust the address's maximum
+ *   number of retransmissions sent before an address is considered
+ *   unreachable. The following structure is used to access and modify an
+ *   address's parameters:
+ */
+enum  sctp_spp_flags {
+       SPP_HB_ENABLE = 1<<0,           /*Enable heartbeats*/
+       SPP_HB_DISABLE = 1<<1,          /*Disable heartbeats*/
+       SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE,
+       SPP_HB_DEMAND = 1<<2,           /*Send heartbeat immediately*/
+       SPP_PMTUD_ENABLE = 1<<3,        /*Enable PMTU discovery*/
+       SPP_PMTUD_DISABLE = 1<<4,       /*Disable PMTU discovery*/
+       SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE,
+       SPP_SACKDELAY_ENABLE = 1<<5,    /*Enable SACK*/
+       SPP_SACKDELAY_DISABLE = 1<<6,   /*Disable SACK*/
+       SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE,
+       SPP_HB_TIME_IS_ZERO = 1<<7,     /* Set HB delay to 0 */
+};
+
+struct sctp_paddrparams {
+       sctp_assoc_t            spp_assoc_id;
+       struct sockaddr_storage spp_address;
+       __u32                   spp_hbinterval;
+       __u16                   spp_pathmaxrxt;
+       __u32                   spp_pathmtu;
+       __u32                   spp_sackdelay;
+       __u32                   spp_flags;
+} __attribute__((packed, aligned(4)));
+
+/*
+ * 7.1.18.  Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
+ *
+ * This set option adds a chunk type that the user is requesting to be
+ * received only in an authenticated way.  Changes to the list of chunks
+ * will only effect future associations on the socket.
+ */
+struct sctp_authchunk {
+       __u8            sauth_chunk;
+};
+
+/*
+ * 7.1.19.  Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
+ *
+ * This option gets or sets the list of HMAC algorithms that the local
+ * endpoint requires the peer to use.
+ */
+#ifndef __KERNEL__
+/* This here is only used by user space as is. It might not be a good idea
+ * to export/reveal the whole structure with reserved fields etc.
+ */
+enum {
+       SCTP_AUTH_HMAC_ID_SHA1 = 1,
+       SCTP_AUTH_HMAC_ID_SHA256 = 3,
+};
+#endif
+
+struct sctp_hmacalgo {
+       __u32           shmac_num_idents;
+       __u16           shmac_idents[];
+};
+
+/* Sadly, user and kernel space have different names for
+ * this structure member, so this is to not break anything.
+ */
+#define shmac_number_of_idents shmac_num_idents
+
+/*
+ * 7.1.20.  Set a shared key (SCTP_AUTH_KEY)
+ *
+ * This option will set a shared secret key which is used to build an
+ * association shared key.
+ */
+struct sctp_authkey {
+       sctp_assoc_t    sca_assoc_id;
+       __u16           sca_keynumber;
+       __u16           sca_keylength;
+       __u8            sca_key[];
+};
+
+/*
+ * 7.1.21.  Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
+ *
+ * This option will get or set the active shared key to be used to build
+ * the association shared key.
+ */
+
+struct sctp_authkeyid {
+       sctp_assoc_t    scact_assoc_id;
+       __u16           scact_keynumber;
+};
+
+
+/*
+ * 7.1.23.  Get or set delayed ack timer (SCTP_DELAYED_SACK)
+ *
+ * This option will effect the way delayed acks are performed.  This
+ * option allows you to get or set the delayed ack time, in
+ * milliseconds.  It also allows changing the delayed ack frequency.
+ * Changing the frequency to 1 disables the delayed sack algorithm.  If
+ * the assoc_id is 0, then this sets or gets the endpoints default
+ * values.  If the assoc_id field is non-zero, then the set or get
+ * effects the specified association for the one to many model (the
+ * assoc_id field is ignored by the one to one model).  Note that if
+ * sack_delay or sack_freq are 0 when setting this option, then the
+ * current values will remain unchanged.
+ */
+struct sctp_sack_info {
+       sctp_assoc_t    sack_assoc_id;
+       uint32_t        sack_delay;
+       uint32_t        sack_freq;
+};
+
+struct sctp_assoc_value {
+    sctp_assoc_t            assoc_id;
+    uint32_t                assoc_value;
+};
+
+/*
+ * 7.2.2 Peer Address Information
+ *
+ *   Applications can retrieve information about a specific peer address
+ *   of an association, including its reachability state, congestion
+ *   window, and retransmission timer values.  This information is
+ *   read-only. The following structure is used to access this
+ *   information:
+ */
+struct sctp_paddrinfo {
+       sctp_assoc_t            spinfo_assoc_id;
+       struct sockaddr_storage spinfo_address;
+       __s32                   spinfo_state;
+       __u32                   spinfo_cwnd;
+       __u32                   spinfo_srtt;
+       __u32                   spinfo_rto;
+       __u32                   spinfo_mtu;
+} __attribute__((packed, aligned(4)));
+
+/* Peer addresses's state. */
+/* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x]
+ * calls.
+ * UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters.
+ *              Not yet confirmed by a heartbeat and not available for data
+ *             transfers.
+ * ACTIVE : Peer address confirmed, active and available for data transfers.
+ * INACTIVE: Peer address inactive and not available for data transfers.
+ */
+enum sctp_spinfo_state {
+       SCTP_INACTIVE,
+       SCTP_PF,
+       SCTP_ACTIVE,
+       SCTP_UNCONFIRMED,
+       SCTP_UNKNOWN = 0xffff  /* Value used for transport state unknown */
+};
+
+/*
+ * 7.2.1 Association Status (SCTP_STATUS)
+ *
+ *   Applications can retrieve current status information about an
+ *   association, including association state, peer receiver window size,
+ *   number of unacked data chunks, and number of data chunks pending
+ *   receipt.  This information is read-only.  The following structure is
+ *   used to access this information:
+ */
+struct sctp_status {
+       sctp_assoc_t            sstat_assoc_id;
+       __s32                   sstat_state;
+       __u32                   sstat_rwnd;
+       __u16                   sstat_unackdata;
+       __u16                   sstat_penddata;
+       __u16                   sstat_instrms;
+       __u16                   sstat_outstrms;
+       __u32                   sstat_fragmentation_point;
+       struct sctp_paddrinfo   sstat_primary;
+};
+
+/*
+ * 7.2.3.  Get the list of chunks the peer requires to be authenticated
+ *         (SCTP_PEER_AUTH_CHUNKS)
+ *
+ * This option gets a list of chunks for a specified association that
+ * the peer requires to be received authenticated only.
+ */
+struct sctp_authchunks {
+       sctp_assoc_t    gauth_assoc_id;
+       __u32           gauth_number_of_chunks;
+       uint8_t         gauth_chunks[];
+};
+
+/* The broken spelling has been released already in lksctp-tools header,
+ * so don't break anyone, now that it's fixed.
+ */
+#define guth_number_of_chunks  gauth_number_of_chunks
+
+/* Association states.  */
+enum sctp_sstat_state {
+       SCTP_EMPTY                = 0,
+       SCTP_CLOSED               = 1,
+       SCTP_COOKIE_WAIT          = 2,
+       SCTP_COOKIE_ECHOED        = 3,
+       SCTP_ESTABLISHED          = 4,
+       SCTP_SHUTDOWN_PENDING     = 5,
+       SCTP_SHUTDOWN_SENT        = 6,
+       SCTP_SHUTDOWN_RECEIVED    = 7,
+       SCTP_SHUTDOWN_ACK_SENT    = 8,
+};
+
+/*
+ * 8.2.6. Get the Current Identifiers of Associations
+ *        (SCTP_GET_ASSOC_ID_LIST)
+ *
+ * This option gets the current list of SCTP association identifiers of
+ * the SCTP associations handled by a one-to-many style socket.
+ */
+struct sctp_assoc_ids {
+       __u32           gaids_number_of_ids;
+       sctp_assoc_t    gaids_assoc_id[];
+};
+
+/*
+ * 8.3, 8.5 get all peer/local addresses in an association.
+ * This parameter struct is used by SCTP_GET_PEER_ADDRS and
+ * SCTP_GET_LOCAL_ADDRS socket options used internally to implement
+ * sctp_getpaddrs() and sctp_getladdrs() API.
+ */
+struct sctp_getaddrs_old {
+       sctp_assoc_t            assoc_id;
+       int                     addr_num;
+#ifdef __KERNEL__
+       struct sockaddr         __user *addrs;
+#else
+       struct sockaddr         *addrs;
+#endif
+};
+
+struct sctp_getaddrs {
+       sctp_assoc_t            assoc_id; /*input*/
+       __u32                   addr_num; /*output*/
+       __u8                    addrs[0]; /*output, variable size*/
+};
+
+/* A socket user request obtained via SCTP_GET_ASSOC_STATS that retrieves
+ * association stats. All stats are counts except sas_maxrto and
+ * sas_obs_rto_ipaddr. maxrto is the max observed rto + transport since
+ * the last call. Will return 0 when RTO was not update since last call
+ */
+struct sctp_assoc_stats {
+       sctp_assoc_t    sas_assoc_id;    /* Input */
+                                        /* Transport of observed max RTO */
+       struct sockaddr_storage sas_obs_rto_ipaddr;
+       __u64           sas_maxrto;      /* Maximum Observed RTO for period */
+       __u64           sas_isacks;      /* SACKs received */
+       __u64           sas_osacks;      /* SACKs sent */
+       __u64           sas_opackets;    /* Packets sent */
+       __u64           sas_ipackets;    /* Packets received */
+       __u64           sas_rtxchunks;   /* Retransmitted Chunks */
+       __u64           sas_outofseqtsns;/* TSN received > next expected */
+       __u64           sas_idupchunks;  /* Dups received (ordered+unordered) */
+       __u64           sas_gapcnt;      /* Gap Acknowledgements Received */
+       __u64           sas_ouodchunks;  /* Unordered data chunks sent */
+       __u64           sas_iuodchunks;  /* Unordered data chunks received */
+       __u64           sas_oodchunks;   /* Ordered data chunks sent */
+       __u64           sas_iodchunks;   /* Ordered data chunks received */
+       __u64           sas_octrlchunks; /* Control chunks sent */
+       __u64           sas_ictrlchunks; /* Control chunks received */
+};
+
+/* These are bit fields for msghdr->msg_flags.  See section 5.1.  */
+/* On user space Linux, these live in <bits/socket.h> as an enum.  */
+enum sctp_msg_flags {
+       MSG_NOTIFICATION = 0x8000,
+#define MSG_NOTIFICATION MSG_NOTIFICATION
+};
+
+/*
+ * 8.1 sctp_bindx()
+ *
+ * The flags parameter is formed from the bitwise OR of zero or more of the
+ * following currently defined flags:
+ */
+#define SCTP_BINDX_ADD_ADDR 0x01
+#define SCTP_BINDX_REM_ADDR 0x02
+
+/* This is the structure that is passed as an argument(optval) to
+ * getsockopt(SCTP_SOCKOPT_PEELOFF).
+ */
+typedef struct {
+       sctp_assoc_t associd;
+       int sd;
+} sctp_peeloff_arg_t;
+
+/*
+ *  Peer Address Thresholds socket option
+ */
+struct sctp_paddrthlds {
+       sctp_assoc_t spt_assoc_id;
+       struct sockaddr_storage spt_address;
+       __u16 spt_pathmaxrxt;
+       __u16 spt_pathpfthld;
+};
+
+#endif /* _UAPI_SCTP_H */
index b49eab89c9fd4461c155ec1b60083aeab1234e44..fefdec91c68b5f20a88cce9ee1942dbc722452ee 100644 (file)
@@ -202,6 +202,8 @@ enum
        LINUX_MIB_TCPFORWARDRETRANS,            /* TCPForwardRetrans */
        LINUX_MIB_TCPSLOWSTARTRETRANS,          /* TCPSlowStartRetrans */
        LINUX_MIB_TCPTIMEOUTS,                  /* TCPTimeouts */
+       LINUX_MIB_TCPLOSSPROBES,                /* TCPLossProbes */
+       LINUX_MIB_TCPLOSSPROBERECOVERY,         /* TCPLossProbeRecovery */
        LINUX_MIB_TCPRENORECOVERYFAIL,          /* TCPRenoRecoveryFail */
        LINUX_MIB_TCPSACKRECOVERYFAIL,          /* TCPSackRecoveryFail */
        LINUX_MIB_TCPSCHEDULERFAILED,           /* TCPSchedulerFailed */
@@ -245,6 +247,7 @@ enum
        LINUX_MIB_TCPFASTOPENPASSIVEFAIL,       /* TCPFastOpenPassiveFail */
        LINUX_MIB_TCPFASTOPENLISTENOVERFLOW,    /* TCPFastOpenListenOverflow */
        LINUX_MIB_TCPFASTOPENCOOKIEREQD,        /* TCPFastOpenCookieReqd */
+       LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */
        __LINUX_MIB_MAX
 };
 
index 6b1ead0b0c9d54a041bda86d5cf9b927e9aa3a56..8d776ebc4829df669bbb70c4d6331ba21cc88884 100644 (file)
@@ -102,7 +102,6 @@ enum {
 #define TCP_QUICKACK           12      /* Block/reenable quick acks */
 #define TCP_CONGESTION         13      /* Congestion control algorithm */
 #define TCP_MD5SIG             14      /* TCP MD5 Signature (RFC2385) */
-#define TCP_COOKIE_TRANSACTIONS        15      /* TCP Cookie Transactions */
 #define TCP_THIN_LINEAR_TIMEOUTS 16      /* Use linear timeouts for thin streams*/
 #define TCP_THIN_DUPACK         17      /* Fast retrans. after 1 dupack */
 #define TCP_USER_TIMEOUT       18      /* How long for loss retry before timeout */
@@ -199,29 +198,4 @@ struct tcp_md5sig {
        __u8    tcpm_key[TCP_MD5SIG_MAXKEYLEN];         /* key (binary) */
 };
 
-/* for TCP_COOKIE_TRANSACTIONS (TCPCT) socket option */
-#define TCP_COOKIE_MIN          8              /*  64-bits */
-#define TCP_COOKIE_MAX         16              /* 128-bits */
-#define TCP_COOKIE_PAIR_SIZE   (2*TCP_COOKIE_MAX)
-
-/* Flags for both getsockopt and setsockopt */
-#define TCP_COOKIE_IN_ALWAYS   (1 << 0)        /* Discard SYN without cookie */
-#define TCP_COOKIE_OUT_NEVER   (1 << 1)        /* Prohibit outgoing cookies,
-                                                * supercedes everything. */
-
-/* Flags for getsockopt */
-#define TCP_S_DATA_IN          (1 << 2)        /* Was data received? */
-#define TCP_S_DATA_OUT         (1 << 3)        /* Was data sent? */
-
-/* TCP_COOKIE_TRANSACTIONS data */
-struct tcp_cookie_transactions {
-       __u16   tcpct_flags;                    /* see above */
-       __u8    __tcpct_pad1;                   /* zero */
-       __u8    tcpct_cookie_desired;           /* bytes */
-       __u16   tcpct_s_data_desired;           /* bytes of variable data */
-       __u16   tcpct_used;                     /* bytes in value */
-       __u8    tcpct_value[TCP_MSS_DEFAULT];
-};
-
-
 #endif /* _UAPI_LINUX_TCP_H */
index df91301847ec244463191579649a064aed76c1ce..b4ed5d89569908ed5fb4407a2eb16ccecf43d4d0 100644 (file)
  * more details.
  */
 
-#ifndef _VM_SOCKETS_H_
-#define _VM_SOCKETS_H_
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
 
-#if !defined(__KERNEL__)
-#include <sys/socket.h>
-#endif
+#include <linux/socket.h>
 
 /* Option name for STREAM socket buffer size.  Use as the option name in
  * setsockopt(3) or getsockopt(3) to set or get an unsigned long long that
 #define VM_SOCKETS_VERSION_MINOR(_v) (((_v) & 0x0000FFFF))
 
 /* Address structure for vSockets.   The address family should be set to
- * whatever vmci_sock_get_af_value_fd() returns.  The structure members should
- * all align on their natural boundaries without resorting to compiler packing
- * directives.  The total size of this structure should be exactly the same as
- * that of struct sockaddr.
+ * AF_VSOCK.  The structure members should all align on their natural
+ * boundaries without resorting to compiler packing directives.  The total size
+ * of this structure should be exactly the same as that of struct sockaddr.
  */
 
 struct sockaddr_vm {
-       sa_family_t svm_family;
+       __kernel_sa_family_t svm_family;
        unsigned short svm_reserved1;
        unsigned int svm_port;
        unsigned int svm_cid;
@@ -156,8 +153,4 @@ struct sockaddr_vm {
 
 #define IOCTL_VM_SOCKETS_GET_LOCAL_CID         _IO(7, 0xb9)
 
-#if defined(__KERNEL__)
-int vm_sockets_get_local_cid(void);
-#endif
-
-#endif
+#endif /* _UAPI_VM_SOCKETS_H */
index 28e493b5b94c9ba6d946d728a0908c84e34294db..a8cd6a4a297070052a0e3deddafd9a05bca62c91 100644 (file)
@@ -297,6 +297,7 @@ enum xfrm_attr_type_t {
        XFRMA_MARK,             /* struct xfrm_mark */
        XFRMA_TFCPAD,           /* __u32 */
        XFRMA_REPLAY_ESN_VAL,   /* struct xfrm_replay_esn */
+       XFRMA_SA_EXTRA_FLAGS,   /* __u32 */
        __XFRMA_MAX
 
 #define XFRMA_MAX (__XFRMA_MAX - 1)
@@ -367,6 +368,8 @@ struct xfrm_usersa_info {
 #define XFRM_STATE_ESN         128
 };
 
+#define XFRM_SA_XFLAG_DONT_ENCAP_DSCP  1
+
 struct xfrm_usersa_id {
        xfrm_address_t                  daddr;
        __be32                          spi;
index 9dfc12000980f6e4a036008ca39d7460fa25534f..3ef3fe05ee99c55aa27d7c3eb5f828935b89ed59 100644 (file)
 #include <xen/interface/io/ring.h>
 #include <xen/interface/grant_table.h>
 
+/*
+ * Older implementation of Xen network frontend / backend has an
+ * implicit dependency on the MAX_SKB_FRAGS as the maximum number of
+ * ring slots a skb can use. Netfront / netback may not work as
+ * expected when frontend and backend have different MAX_SKB_FRAGS.
+ *
+ * A better approach is to add mechanism for netfront / netback to
+ * negotiate this value. However we cannot fix all possible
+ * frontends, so we need to define a value which states the minimum
+ * slots backend must support.
+ *
+ * The minimum value derives from older Linux kernel's MAX_SKB_FRAGS
+ * (18), which is proved to work with most frontends. Any new backend
+ * which doesn't negotiate with frontend should expect frontend to
+ * send a valid packet using slots up to this value.
+ */
+#define XEN_NETIF_NR_SLOTS_MIN 18
+
 /*
  * Notifications after enqueuing any type of message should be conditional on
  * the appropriate req_event or rsp_event field in the shared ring.
@@ -47,6 +65,7 @@
 #define _XEN_NETTXF_extra_info         (3)
 #define  XEN_NETTXF_extra_info         (1U<<_XEN_NETTXF_extra_info)
 
+#define XEN_NETIF_MAX_TX_SIZE 0xFFFF
 struct xen_netif_tx_request {
     grant_ref_t gref;      /* Reference to buffer page */
     uint16_t offset;       /* Offset within buffer page */
index d596e5355f153d0cac5da1bc7806239a1ec6b860..488f85f76335cfa609da70ee96e6cc46646ed7fa 100644 (file)
@@ -58,7 +58,7 @@
 #ifdef CONFIG_SECURITY
 #include <linux/security.h>
 #endif
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <linux/freezer.h>
 #include <linux/tty.h>
 #include <linux/pid_namespace.h>
@@ -910,7 +910,7 @@ static void audit_receive_skb(struct sk_buff *skb)
 {
        struct nlmsghdr *nlh;
        /*
-        * len MUST be signed for NLMSG_NEXT to be able to dec it below 0
+        * len MUST be signed for nlmsg_next to be able to dec it below 0
         * if the nlmsg_len was not aligned
         */
        int len;
@@ -919,13 +919,13 @@ static void audit_receive_skb(struct sk_buff *skb)
        nlh = nlmsg_hdr(skb);
        len = skb->len;
 
-       while (NLMSG_OK(nlh, len)) {
+       while (nlmsg_ok(nlh, len)) {
                err = audit_receive_msg(skb, nlh);
                /* if err or if this message says it wants a response */
                if (err || (nlh->nlmsg_flags & NLM_F_ACK))
                        netlink_ack(skb, nlh, err);
 
-               nlh = NLMSG_NEXT(nlh, len);
+               nlh = nlmsg_next(nlh, &len);
        }
 }
 
@@ -1483,7 +1483,7 @@ void audit_log_end(struct audit_buffer *ab)
                audit_log_lost("rate limit exceeded");
        } else {
                struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
-               nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0);
+               nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN;
 
                if (audit_pid) {
                        skb_queue_tail(&audit_skb_queue, ab->skb);
index 598dc06be4214c7a11fbf128baff984fa9c1c4b5..06ff7764ab7c748fd31634521122b1381af17d73 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/user_namespace.h>
 #include <linux/uprobes.h>
 #include <linux/compat.h>
+#include <linux/cn_proc.h>
 #define CREATE_TRACE_POINTS
 #include <trace/events/signal.h>
 
@@ -2350,6 +2351,7 @@ relock:
                if (sig_kernel_coredump(signr)) {
                        if (print_fatal_signals)
                                print_fatal_signal(info->si_signo);
+                       proc_coredump_connector(current);
                        /*
                         * If it was able to dump core, this kills all
                         * other threads in the group and synchronizes with
index 8456f5d98b853b24607d8168aee1b7d9aa9fdac8..5d9630a0eb93b75604f49f3c9de84ad7a417de03 100644 (file)
@@ -609,8 +609,12 @@ void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl
        /* Delete timer and generate a final TRANSMIT_PDU event to flush out
         * all pending messages before the applicant is gone. */
        del_timer_sync(&app->join_timer);
+
+       spin_lock_bh(&app->lock);
        garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
        garp_pdu_queue(app);
+       spin_unlock_bh(&app->lock);
+
        garp_queue_xmit(app);
 
        dev_mc_del(dev, appl->proto.group_address);
index 8f7517df41a5a1d5d101d33b1e81517006019d8d..b85a91fa61f1eb0d24b20387e607596833e54595 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 config VLAN_8021Q
-       tristate "802.1Q VLAN Support"
+       tristate "802.1Q/802.1ad VLAN Support"
        ---help---
          Select this and you will be able to create 802.1Q VLAN interfaces
          on your ethernet interfaces.  802.1Q VLAN supports almost
index 85addcd9372b0d8abfbd277d332408900efdea1d..9424f3718ea703adc1019193a30352ad95981e6a 100644 (file)
@@ -51,14 +51,18 @@ const char vlan_version[] = DRV_VERSION;
 
 /* End of global variables definitions. */
 
-static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id)
+static int vlan_group_prealloc_vid(struct vlan_group *vg,
+                                  __be16 vlan_proto, u16 vlan_id)
 {
        struct net_device **array;
+       unsigned int pidx, vidx;
        unsigned int size;
 
        ASSERT_RTNL();
 
-       array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN];
+       pidx  = vlan_proto_idx(vlan_proto);
+       vidx  = vlan_id / VLAN_GROUP_ARRAY_PART_LEN;
+       array = vg->vlan_devices_arrays[pidx][vidx];
        if (array != NULL)
                return 0;
 
@@ -67,7 +71,7 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id)
        if (array == NULL)
                return -ENOBUFS;
 
-       vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN] = array;
+       vg->vlan_devices_arrays[pidx][vidx] = array;
        return 0;
 }
 
@@ -93,7 +97,7 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
        if (vlan->flags & VLAN_FLAG_GVRP)
                vlan_gvrp_request_leave(dev);
 
-       vlan_group_set_device(grp, vlan_id, NULL);
+       vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL);
        /* Because unregister_netdevice_queue() makes sure at least one rcu
         * grace period is respected before device freeing,
         * we dont need to call synchronize_net() here.
@@ -112,13 +116,14 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
         * VLAN is not 0 (leave it there for 802.1p).
         */
        if (vlan_id)
-               vlan_vid_del(real_dev, vlan_id);
+               vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
 
        /* Get rid of the vlan's reference to real_dev */
        dev_put(real_dev);
 }
 
-int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id)
+int vlan_check_real_dev(struct net_device *real_dev,
+                       __be16 protocol, u16 vlan_id)
 {
        const char *name = real_dev->name;
 
@@ -127,7 +132,7 @@ int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id)
                return -EOPNOTSUPP;
        }
 
-       if (vlan_find_dev(real_dev, vlan_id) != NULL)
+       if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL)
                return -EEXIST;
 
        return 0;
@@ -142,7 +147,7 @@ int register_vlan_dev(struct net_device *dev)
        struct vlan_group *grp;
        int err;
 
-       err = vlan_vid_add(real_dev, vlan_id);
+       err = vlan_vid_add(real_dev, vlan->vlan_proto, vlan_id);
        if (err)
                return err;
 
@@ -160,7 +165,7 @@ int register_vlan_dev(struct net_device *dev)
                        goto out_uninit_gvrp;
        }
 
-       err = vlan_group_prealloc_vid(grp, vlan_id);
+       err = vlan_group_prealloc_vid(grp, vlan->vlan_proto, vlan_id);
        if (err < 0)
                goto out_uninit_mvrp;
 
@@ -181,7 +186,7 @@ int register_vlan_dev(struct net_device *dev)
        /* So, got the sucker initialized, now lets place
         * it into our local structure.
         */
-       vlan_group_set_device(grp, vlan_id, dev);
+       vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev);
        grp->nr_vlan_devs++;
 
        return 0;
@@ -195,7 +200,7 @@ out_uninit_gvrp:
        if (grp->nr_vlan_devs == 0)
                vlan_gvrp_uninit_applicant(real_dev);
 out_vid_del:
-       vlan_vid_del(real_dev, vlan_id);
+       vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
        return err;
 }
 
@@ -213,7 +218,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
        if (vlan_id >= VLAN_VID_MASK)
                return -ERANGE;
 
-       err = vlan_check_real_dev(real_dev, vlan_id);
+       err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id);
        if (err < 0)
                return err;
 
@@ -255,6 +260,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
        new_dev->mtu = real_dev->mtu;
        new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT);
 
+       vlan_dev_priv(new_dev)->vlan_proto = htons(ETH_P_8021Q);
        vlan_dev_priv(new_dev)->vlan_id = vlan_id;
        vlan_dev_priv(new_dev)->real_dev = real_dev;
        vlan_dev_priv(new_dev)->dent = NULL;
@@ -301,7 +307,7 @@ static void vlan_transfer_features(struct net_device *dev,
 {
        vlandev->gso_max_size = dev->gso_max_size;
 
-       if (dev->features & NETIF_F_HW_VLAN_TX)
+       if (dev->features & NETIF_F_HW_VLAN_CTAG_TX)
                vlandev->hard_header_len = dev->hard_header_len;
        else
                vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN;
@@ -341,16 +347,17 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
        int i, flgs;
        struct net_device *vlandev;
        struct vlan_dev_priv *vlan;
+       bool last = false;
        LIST_HEAD(list);
 
        if (is_vlan_dev(dev))
                __vlan_device_event(dev, event);
 
        if ((event == NETDEV_UP) &&
-           (dev->features & NETIF_F_HW_VLAN_FILTER)) {
+           (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
                pr_info("adding VLAN 0 to HW filter on device %s\n",
                        dev->name);
-               vlan_vid_add(dev, 0);
+               vlan_vid_add(dev, htons(ETH_P_8021Q), 0);
        }
 
        vlan_info = rtnl_dereference(dev->vlan_info);
@@ -365,22 +372,13 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
        switch (event) {
        case NETDEV_CHANGE:
                /* Propagate real device state to vlan devices */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev)
                        netif_stacked_transfer_operstate(dev, vlandev);
-               }
                break;
 
        case NETDEV_CHANGEADDR:
                /* Adjust unicast filters on underlying device */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        flgs = vlandev->flags;
                        if (!(flgs & IFF_UP))
                                continue;
@@ -390,11 +388,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
                break;
 
        case NETDEV_CHANGEMTU:
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        if (vlandev->mtu <= dev->mtu)
                                continue;
 
@@ -404,26 +398,16 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
 
        case NETDEV_FEAT_CHANGE:
                /* Propagate device features to underlying device */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev)
                        vlan_transfer_features(dev, vlandev);
-               }
-
                break;
 
        case NETDEV_DOWN:
-               if (dev->features & NETIF_F_HW_VLAN_FILTER)
-                       vlan_vid_del(dev, 0);
+               if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+                       vlan_vid_del(dev, htons(ETH_P_8021Q), 0);
 
                /* Put all VLANs for this dev in the down state too.  */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        flgs = vlandev->flags;
                        if (!(flgs & IFF_UP))
                                continue;
@@ -437,11 +421,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
 
        case NETDEV_UP:
                /* Put all VLANs for this dev in the up state too.  */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        flgs = vlandev->flags;
                        if (flgs & IFF_UP)
                                continue;
@@ -458,17 +438,15 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
                if (dev->reg_state != NETREG_UNREGISTERING)
                        break;
 
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        /* removal of last vid destroys vlan_info, abort
                         * afterwards */
                        if (vlan_info->nr_vids == 1)
-                               i = VLAN_N_VID;
+                               last = true;
 
                        unregister_vlan_dev(vlandev, &list);
+                       if (last)
+                               break;
                }
                unregister_netdevice_many(&list);
                break;
@@ -482,13 +460,8 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
        case NETDEV_NOTIFY_PEERS:
        case NETDEV_BONDING_FAILOVER:
                /* Propagate to vlan devices */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev)
                        call_netdevice_notifiers(event, vlandev);
-               }
                break;
        }
 
index 670f1e8cfc0fd1dec4e0415ef7ab37cf33db7c5f..ba5983f34c42f8076f4416af164f229678ffc4e1 100644 (file)
@@ -49,6 +49,7 @@ struct netpoll;
  *     @ingress_priority_map: ingress priority mappings
  *     @nr_egress_mappings: number of egress priority mappings
  *     @egress_priority_map: hash of egress priority mappings
+ *     @vlan_proto: VLAN encapsulation protocol
  *     @vlan_id: VLAN identifier
  *     @flags: device flags
  *     @real_dev: underlying netdevice
@@ -62,6 +63,7 @@ struct vlan_dev_priv {
        unsigned int                            nr_egress_mappings;
        struct vlan_priority_tci_mapping        *egress_priority_map[16];
 
+       __be16                                  vlan_proto;
        u16                                     vlan_id;
        u16                                     flags;
 
@@ -87,10 +89,17 @@ static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev)
 #define VLAN_GROUP_ARRAY_SPLIT_PARTS  8
 #define VLAN_GROUP_ARRAY_PART_LEN     (VLAN_N_VID/VLAN_GROUP_ARRAY_SPLIT_PARTS)
 
+enum vlan_protos {
+       VLAN_PROTO_8021Q        = 0,
+       VLAN_PROTO_8021AD,
+       VLAN_PROTO_NUM,
+};
+
 struct vlan_group {
        unsigned int            nr_vlan_devs;
        struct hlist_node       hlist;  /* linked list */
-       struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS];
+       struct net_device **vlan_devices_arrays[VLAN_PROTO_NUM]
+                                              [VLAN_GROUP_ARRAY_SPLIT_PARTS];
 };
 
 struct vlan_info {
@@ -103,37 +112,67 @@ struct vlan_info {
        struct rcu_head         rcu;
 };
 
-static inline struct net_device *vlan_group_get_device(struct vlan_group *vg,
-                                                      u16 vlan_id)
+static inline unsigned int vlan_proto_idx(__be16 proto)
+{
+       switch (proto) {
+       case __constant_htons(ETH_P_8021Q):
+               return VLAN_PROTO_8021Q;
+       case __constant_htons(ETH_P_8021AD):
+               return VLAN_PROTO_8021AD;
+       default:
+               BUG();
+               return 0;
+       }
+}
+
+static inline struct net_device *__vlan_group_get_device(struct vlan_group *vg,
+                                                        unsigned int pidx,
+                                                        u16 vlan_id)
 {
        struct net_device **array;
-       array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN];
+
+       array = vg->vlan_devices_arrays[pidx]
+                                      [vlan_id / VLAN_GROUP_ARRAY_PART_LEN];
        return array ? array[vlan_id % VLAN_GROUP_ARRAY_PART_LEN] : NULL;
 }
 
+static inline struct net_device *vlan_group_get_device(struct vlan_group *vg,
+                                                      __be16 vlan_proto,
+                                                      u16 vlan_id)
+{
+       return __vlan_group_get_device(vg, vlan_proto_idx(vlan_proto), vlan_id);
+}
+
 static inline void vlan_group_set_device(struct vlan_group *vg,
-                                        u16 vlan_id,
+                                        __be16 vlan_proto, u16 vlan_id,
                                         struct net_device *dev)
 {
        struct net_device **array;
        if (!vg)
                return;
-       array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN];
+       array = vg->vlan_devices_arrays[vlan_proto_idx(vlan_proto)]
+                                      [vlan_id / VLAN_GROUP_ARRAY_PART_LEN];
        array[vlan_id % VLAN_GROUP_ARRAY_PART_LEN] = dev;
 }
 
 /* Must be invoked with rcu_read_lock or with RTNL. */
 static inline struct net_device *vlan_find_dev(struct net_device *real_dev,
-                                              u16 vlan_id)
+                                              __be16 vlan_proto, u16 vlan_id)
 {
        struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info);
 
        if (vlan_info)
-               return vlan_group_get_device(&vlan_info->grp, vlan_id);
+               return vlan_group_get_device(&vlan_info->grp,
+                                            vlan_proto, vlan_id);
 
        return NULL;
 }
 
+#define vlan_group_for_each_dev(grp, i, dev) \
+       for ((i) = 0; i < VLAN_PROTO_NUM * VLAN_N_VID; i++) \
+               if (((dev) = __vlan_group_get_device((grp), (i) / VLAN_N_VID, \
+                                                           (i) % VLAN_N_VID)))
+
 /* found in vlan_dev.c */
 void vlan_dev_set_ingress_priority(const struct net_device *dev,
                                   u32 skb_prio, u16 vlan_prio);
@@ -142,7 +181,8 @@ int vlan_dev_set_egress_priority(const struct net_device *dev,
 int vlan_dev_change_flags(const struct net_device *dev, u32 flag, u32 mask);
 void vlan_dev_get_realdev_name(const struct net_device *dev, char *result);
 
-int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id);
+int vlan_check_real_dev(struct net_device *real_dev,
+                       __be16 protocol, u16 vlan_id);
 void vlan_setup(struct net_device *dev);
 int register_vlan_dev(struct net_device *dev);
 void unregister_vlan_dev(struct net_device *dev, struct list_head *head);
index f3b6f515eba67bd8e883707521da6ae1e4f64834..8a15eaadc4bd655c9969b0b19197a424d79aaa36 100644 (file)
@@ -8,11 +8,12 @@
 bool vlan_do_receive(struct sk_buff **skbp)
 {
        struct sk_buff *skb = *skbp;
+       __be16 vlan_proto = skb->vlan_proto;
        u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK;
        struct net_device *vlan_dev;
        struct vlan_pcpu_stats *rx_stats;
 
-       vlan_dev = vlan_find_dev(skb->dev, vlan_id);
+       vlan_dev = vlan_find_dev(skb->dev, vlan_proto, vlan_id);
        if (!vlan_dev)
                return false;
 
@@ -38,7 +39,8 @@ bool vlan_do_receive(struct sk_buff **skbp)
                 * original position later
                 */
                skb_push(skb, offset);
-               skb = *skbp = vlan_insert_tag(skb, skb->vlan_tci);
+               skb = *skbp = vlan_insert_tag(skb, skb->vlan_proto,
+                                             skb->vlan_tci);
                if (!skb)
                        return false;
                skb_pull(skb, offset + VLAN_HLEN);
@@ -62,12 +64,13 @@ bool vlan_do_receive(struct sk_buff **skbp)
 
 /* Must be invoked with rcu_read_lock. */
 struct net_device *__vlan_find_dev_deep(struct net_device *dev,
-                                       u16 vlan_id)
+                                       __be16 vlan_proto, u16 vlan_id)
 {
        struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info);
 
        if (vlan_info) {
-               return vlan_group_get_device(&vlan_info->grp, vlan_id);
+               return vlan_group_get_device(&vlan_info->grp,
+                                            vlan_proto, vlan_id);
        } else {
                /*
                 * Lower devices of master uppers (bonding, team) do not have
@@ -78,7 +81,8 @@ struct net_device *__vlan_find_dev_deep(struct net_device *dev,
 
                upper_dev = netdev_master_upper_dev_get_rcu(dev);
                if (upper_dev)
-                       return __vlan_find_dev_deep(upper_dev, vlan_id);
+                       return __vlan_find_dev_deep(upper_dev,
+                                                   vlan_proto, vlan_id);
        }
 
        return NULL;
@@ -125,7 +129,7 @@ struct sk_buff *vlan_untag(struct sk_buff *skb)
 
        vhdr = (struct vlan_hdr *) skb->data;
        vlan_tci = ntohs(vhdr->h_vlan_TCI);
-       __vlan_hwaccel_put_tag(skb, vlan_tci);
+       __vlan_hwaccel_put_tag(skb, skb->protocol, vlan_tci);
 
        skb_pull_rcsum(skb, VLAN_HLEN);
        vlan_set_encap_proto(skb, vhdr);
@@ -153,10 +157,11 @@ EXPORT_SYMBOL(vlan_untag);
 
 static void vlan_group_free(struct vlan_group *grp)
 {
-       int i;
+       int i, j;
 
-       for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++)
-               kfree(grp->vlan_devices_arrays[i]);
+       for (i = 0; i < VLAN_PROTO_NUM; i++)
+               for (j = 0; j < VLAN_GROUP_ARRAY_SPLIT_PARTS; j++)
+                       kfree(grp->vlan_devices_arrays[i][j]);
 }
 
 static void vlan_info_free(struct vlan_info *vlan_info)
@@ -185,35 +190,49 @@ static struct vlan_info *vlan_info_alloc(struct net_device *dev)
 
 struct vlan_vid_info {
        struct list_head list;
-       unsigned short vid;
+       __be16 proto;
+       u16 vid;
        int refcount;
 };
 
+static bool vlan_hw_filter_capable(const struct net_device *dev,
+                                    const struct vlan_vid_info *vid_info)
+{
+       if (vid_info->proto == htons(ETH_P_8021Q) &&
+           dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+               return true;
+       if (vid_info->proto == htons(ETH_P_8021AD) &&
+           dev->features & NETIF_F_HW_VLAN_STAG_FILTER)
+               return true;
+       return false;
+}
+
 static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info,
-                                              unsigned short vid)
+                                              __be16 proto, u16 vid)
 {
        struct vlan_vid_info *vid_info;
 
        list_for_each_entry(vid_info, &vlan_info->vid_list, list) {
-               if (vid_info->vid == vid)
+               if (vid_info->proto == proto && vid_info->vid == vid)
                        return vid_info;
        }
        return NULL;
 }
 
-static struct vlan_vid_info *vlan_vid_info_alloc(unsigned short vid)
+static struct vlan_vid_info *vlan_vid_info_alloc(__be16 proto, u16 vid)
 {
        struct vlan_vid_info *vid_info;
 
        vid_info = kzalloc(sizeof(struct vlan_vid_info), GFP_KERNEL);
        if (!vid_info)
                return NULL;
+       vid_info->proto = proto;
        vid_info->vid = vid;
 
        return vid_info;
 }
 
-static int __vlan_vid_add(struct vlan_info *vlan_info, unsigned short vid,
+static int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid,
                          struct vlan_vid_info **pvid_info)
 {
        struct net_device *dev = vlan_info->real_dev;
@@ -221,12 +240,12 @@ static int __vlan_vid_add(struct vlan_info *vlan_info, unsigned short vid,
        struct vlan_vid_info *vid_info;
        int err;
 
-       vid_info = vlan_vid_info_alloc(vid);
+       vid_info = vlan_vid_info_alloc(proto, vid);
        if (!vid_info)
                return -ENOMEM;
 
-       if (dev->features & NETIF_F_HW_VLAN_FILTER) {
-               err =  ops->ndo_vlan_rx_add_vid(dev, vid);
+       if (vlan_hw_filter_capable(dev, vid_info)) {
+               err =  ops->ndo_vlan_rx_add_vid(dev, proto, vid);
                if (err) {
                        kfree(vid_info);
                        return err;
@@ -238,7 +257,7 @@ static int __vlan_vid_add(struct vlan_info *vlan_info, unsigned short vid,
        return 0;
 }
 
-int vlan_vid_add(struct net_device *dev, unsigned short vid)
+int vlan_vid_add(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct vlan_info *vlan_info;
        struct vlan_vid_info *vid_info;
@@ -254,9 +273,9 @@ int vlan_vid_add(struct net_device *dev, unsigned short vid)
                        return -ENOMEM;
                vlan_info_created = true;
        }
-       vid_info = vlan_vid_info_get(vlan_info, vid);
+       vid_info = vlan_vid_info_get(vlan_info, proto, vid);
        if (!vid_info) {
-               err = __vlan_vid_add(vlan_info, vid, &vid_info);
+               err = __vlan_vid_add(vlan_info, proto, vid, &vid_info);
                if (err)
                        goto out_free_vlan_info;
        }
@@ -279,14 +298,15 @@ static void __vlan_vid_del(struct vlan_info *vlan_info,
 {
        struct net_device *dev = vlan_info->real_dev;
        const struct net_device_ops *ops = dev->netdev_ops;
-       unsigned short vid = vid_info->vid;
+       __be16 proto = vid_info->proto;
+       u16 vid = vid_info->vid;
        int err;
 
-       if (dev->features & NETIF_F_HW_VLAN_FILTER) {
-               err = ops->ndo_vlan_rx_kill_vid(dev, vid);
+       if (vlan_hw_filter_capable(dev, vid_info)) {
+               err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid);
                if (err) {
-                       pr_warn("failed to kill vid %d for device %s\n",
-                               vid, dev->name);
+                       pr_warn("failed to kill vid %04x/%d for device %s\n",
+                               proto, vid, dev->name);
                }
        }
        list_del(&vid_info->list);
@@ -294,7 +314,7 @@ static void __vlan_vid_del(struct vlan_info *vlan_info,
        vlan_info->nr_vids--;
 }
 
-void vlan_vid_del(struct net_device *dev, unsigned short vid)
+void vlan_vid_del(struct net_device *dev, __be16 proto, u16 vid)
 {
        struct vlan_info *vlan_info;
        struct vlan_vid_info *vid_info;
@@ -305,7 +325,7 @@ void vlan_vid_del(struct net_device *dev, unsigned short vid)
        if (!vlan_info)
                return;
 
-       vid_info = vlan_vid_info_get(vlan_info, vid);
+       vid_info = vlan_vid_info_get(vlan_info, proto, vid);
        if (!vid_info)
                return;
        vid_info->refcount--;
@@ -333,7 +353,7 @@ int vlan_vids_add_by_dev(struct net_device *dev,
                return 0;
 
        list_for_each_entry(vid_info, &vlan_info->vid_list, list) {
-               err = vlan_vid_add(dev, vid_info->vid);
+               err = vlan_vid_add(dev, vid_info->proto, vid_info->vid);
                if (err)
                        goto unwind;
        }
@@ -343,7 +363,7 @@ unwind:
        list_for_each_entry_continue_reverse(vid_info,
                                             &vlan_info->vid_list,
                                             list) {
-               vlan_vid_del(dev, vid_info->vid);
+               vlan_vid_del(dev, vid_info->proto, vid_info->vid);
        }
 
        return err;
@@ -363,7 +383,7 @@ void vlan_vids_del_by_dev(struct net_device *dev,
                return;
 
        list_for_each_entry(vid_info, &vlan_info->vid_list, list)
-               vlan_vid_del(dev, vid_info->vid);
+               vlan_vid_del(dev, vid_info->proto, vid_info->vid);
 }
 EXPORT_SYMBOL(vlan_vids_del_by_dev);
 
index 19cf81bf9f692f0ac41f21bdf171f6ea2ab039a0..8af508536d36b539117ffeecfc8e7b47200bd72d 100644 (file)
@@ -99,6 +99,7 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
                                const void *daddr, const void *saddr,
                                unsigned int len)
 {
+       struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
        struct vlan_hdr *vhdr;
        unsigned int vhdrlen = 0;
        u16 vlan_tci = 0;
@@ -120,8 +121,8 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
                else
                        vhdr->h_vlan_encapsulated_proto = htons(len);
 
-               skb->protocol = htons(ETH_P_8021Q);
-               type = ETH_P_8021Q;
+               skb->protocol = vlan->vlan_proto;
+               type = ntohs(vlan->vlan_proto);
                vhdrlen = VLAN_HLEN;
        }
 
@@ -161,12 +162,12 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
         * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING
         * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...
         */
-       if (veth->h_vlan_proto != htons(ETH_P_8021Q) ||
+       if (veth->h_vlan_proto != vlan->vlan_proto ||
            vlan->flags & VLAN_FLAG_REORDER_HDR) {
                u16 vlan_tci;
                vlan_tci = vlan->vlan_id;
                vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
-               skb = __vlan_hwaccel_put_tag(skb, vlan_tci);
+               skb = __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
        }
 
        skb->dev = vlan->real_dev;
@@ -583,7 +584,7 @@ static int vlan_dev_init(struct net_device *dev)
 #endif
 
        dev->needed_headroom = real_dev->needed_headroom;
-       if (real_dev->features & NETIF_F_HW_VLAN_TX) {
+       if (real_dev->features & NETIF_F_HW_VLAN_CTAG_TX) {
                dev->header_ops      = real_dev->header_ops;
                dev->hard_header_len = real_dev->hard_header_len;
        } else {
index 6f9755352760c7494ad91eb46f3f43df3f181bdb..66a80320b03254bb7148e1fc9af827f59bed8eb2 100644 (file)
@@ -32,6 +32,8 @@ int vlan_gvrp_request_join(const struct net_device *dev)
        const struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
        __be16 vlan_id = htons(vlan->vlan_id);
 
+       if (vlan->vlan_proto != htons(ETH_P_8021Q))
+               return 0;
        return garp_request_join(vlan->real_dev, &vlan_gvrp_app,
                                 &vlan_id, sizeof(vlan_id), GVRP_ATTR_VID);
 }
@@ -41,6 +43,8 @@ void vlan_gvrp_request_leave(const struct net_device *dev)
        const struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
        __be16 vlan_id = htons(vlan->vlan_id);
 
+       if (vlan->vlan_proto != htons(ETH_P_8021Q))
+               return;
        garp_request_leave(vlan->real_dev, &vlan_gvrp_app,
                           &vlan_id, sizeof(vlan_id), GVRP_ATTR_VID);
 }
index d9ec1d5964aaad69838c1b8868e93be0390e0e15..e0fe091801b0c38edffb39bd1889a373464f4f02 100644 (file)
@@ -38,6 +38,8 @@ int vlan_mvrp_request_join(const struct net_device *dev)
        const struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
        __be16 vlan_id = htons(vlan->vlan_id);
 
+       if (vlan->vlan_proto != htons(ETH_P_8021Q))
+               return 0;
        return mrp_request_join(vlan->real_dev, &vlan_mrp_app,
                                &vlan_id, sizeof(vlan_id), MVRP_ATTR_VID);
 }
@@ -47,6 +49,8 @@ void vlan_mvrp_request_leave(const struct net_device *dev)
        const struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
        __be16 vlan_id = htons(vlan->vlan_id);
 
+       if (vlan->vlan_proto != htons(ETH_P_8021Q))
+               return;
        mrp_request_leave(vlan->real_dev, &vlan_mrp_app,
                          &vlan_id, sizeof(vlan_id), MVRP_ATTR_VID);
 }
index 1789658b7cd751bb8d9d3fb2db431defc7838321..309129732285fd610159446bade5ede27c835d1c 100644 (file)
@@ -23,6 +23,7 @@ static const struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = {
        [IFLA_VLAN_FLAGS]       = { .len = sizeof(struct ifla_vlan_flags) },
        [IFLA_VLAN_EGRESS_QOS]  = { .type = NLA_NESTED },
        [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
+       [IFLA_VLAN_PROTOCOL]    = { .type = NLA_U16 },
 };
 
 static const struct nla_policy vlan_map_policy[IFLA_VLAN_QOS_MAX + 1] = {
@@ -53,6 +54,16 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[])
        if (!data)
                return -EINVAL;
 
+       if (data[IFLA_VLAN_PROTOCOL]) {
+               switch (nla_get_be16(data[IFLA_VLAN_PROTOCOL])) {
+               case __constant_htons(ETH_P_8021Q):
+               case __constant_htons(ETH_P_8021AD):
+                       break;
+               default:
+                       return -EPROTONOSUPPORT;
+               }
+       }
+
        if (data[IFLA_VLAN_ID]) {
                id = nla_get_u16(data[IFLA_VLAN_ID]);
                if (id >= VLAN_VID_MASK)
@@ -107,6 +118,7 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
 {
        struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
        struct net_device *real_dev;
+       __be16 proto;
        int err;
 
        if (!data[IFLA_VLAN_ID])
@@ -118,11 +130,17 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
        if (!real_dev)
                return -ENODEV;
 
-       vlan->vlan_id  = nla_get_u16(data[IFLA_VLAN_ID]);
-       vlan->real_dev = real_dev;
-       vlan->flags    = VLAN_FLAG_REORDER_HDR;
+       if (data[IFLA_VLAN_PROTOCOL])
+               proto = nla_get_be16(data[IFLA_VLAN_PROTOCOL]);
+       else
+               proto = htons(ETH_P_8021Q);
+
+       vlan->vlan_proto = proto;
+       vlan->vlan_id    = nla_get_u16(data[IFLA_VLAN_ID]);
+       vlan->real_dev   = real_dev;
+       vlan->flags      = VLAN_FLAG_REORDER_HDR;
 
-       err = vlan_check_real_dev(real_dev, vlan->vlan_id);
+       err = vlan_check_real_dev(real_dev, vlan->vlan_proto, vlan->vlan_id);
        if (err < 0)
                return err;
 
@@ -151,7 +169,8 @@ static size_t vlan_get_size(const struct net_device *dev)
 {
        struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
 
-       return nla_total_size(2) +      /* IFLA_VLAN_ID */
+       return nla_total_size(2) +      /* IFLA_VLAN_PROTOCOL */
+              nla_total_size(2) +      /* IFLA_VLAN_ID */
               sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */
               vlan_qos_map_size(vlan->nr_ingress_mappings) +
               vlan_qos_map_size(vlan->nr_egress_mappings);
@@ -166,7 +185,8 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
        struct nlattr *nest;
        unsigned int i;
 
-       if (nla_put_u16(skb, IFLA_VLAN_ID, vlan_dev_priv(dev)->vlan_id))
+       if (nla_put_be16(skb, IFLA_VLAN_PROTOCOL, vlan->vlan_proto) ||
+           nla_put_u16(skb, IFLA_VLAN_ID, vlan->vlan_id))
                goto nla_put_failure;
        if (vlan->flags) {
                f.flags = vlan->flags;
index 6f676ab885be52b442fea9a0d0f0898e67da50a2..1a2221630e6a22a525f4877c0fc4b721b15e3025 100644 (file)
@@ -23,6 +23,15 @@ menuconfig NET
 
 if NET
 
+config NETLINK_MMAP
+       bool "Netlink: mmaped IO"
+       help
+         This option enables support for memory mapped netlink IO. This
+         reduces overhead by avoiding copying data between kernel- and
+         userspace.
+
+         If unsure, say N.
+
 config WANT_COMPAT_NETLINK_MESSAGES
        bool
        help
@@ -217,6 +226,7 @@ source "net/dns_resolver/Kconfig"
 source "net/batman-adv/Kconfig"
 source "net/openvswitch/Kconfig"
 source "net/vmw_vsock/Kconfig"
+source "net/netlink/Kconfig"
 
 config RPS
        boolean
index a86aff9a3c04a760e42d67b7460e28bdb2c23dfb..4149db1b7885127db5a3518bf5c944ab5ba9728f 100644 (file)
@@ -58,7 +58,7 @@ struct lane2_ops {
  *    field in h_type field. Data follows immediately after header.
  * 2. LLC Data frames whose total length, including LLC field and data,
  *    but not padding required to meet the minimum data frame length,
- *    is less than 1536(0x0600) MUST be encoded by placing that length
+ *    is less than ETH_P_802_3_MIN MUST be encoded by placing that length
  *    in the h_type field. The LLC field follows header immediately.
  * 3. LLC data frames longer than this maximum MUST be encoded by placing
  *    the value 0 in the h_type field.
index 8d8afb134b3ac016799be9ab43d6fe45f40962bb..fa780b76630e4def219fc5f464554a4361721229 100644 (file)
@@ -36,6 +36,20 @@ config BATMAN_ADV_DAT
          mesh networks. If you think that your network does not need
          this option you can safely remove it and save some space.
 
+config BATMAN_ADV_NC
+       bool "Network Coding"
+       depends on BATMAN_ADV
+       default n
+       help
+         This option enables network coding, a mechanism that aims to
+         increase the overall network throughput by fusing multiple
+         packets in one transmission.
+         Note that interfaces controlled by batman-adv must be manually
+         configured to have promiscuous mode enabled in order to make
+         network coding work.
+         If you think that your network does not need this feature you
+         can safely disable it and save some space.
+
 config BATMAN_ADV_DEBUG
        bool "B.A.T.M.A.N. debugging"
        depends on BATMAN_ADV
index e45e3b4e32e3f480b2b65d5900ff0838391bc2ec..acbac2a9c62fc6e0c70f893f80da00ea5503cdf9 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
+# Copyright (C) 2007-2013 B.A.T.M.A.N. contributors:
 #
 # Marek Lindner, Simon Wunderlich
 #
@@ -30,6 +30,7 @@ batman-adv-y += hard-interface.o
 batman-adv-y += hash.o
 batman-adv-y += icmp_socket.o
 batman-adv-y += main.o
+batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o
 batman-adv-y += originator.o
 batman-adv-y += ring_buffer.o
 batman-adv-y += routing.o
index a5bb0a769eb927efff0408fa7dd6afbffcdf2ec0..071f288b77a86c8c88b6eadd9634a24cdb209e81 100644 (file)
@@ -27,6 +27,7 @@
 #include "hard-interface.h"
 #include "send.h"
 #include "bat_algo.h"
+#include "network-coding.h"
 
 static struct batadv_neigh_node *
 batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
@@ -1185,6 +1186,10 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
        if (!orig_neigh_node)
                goto out;
 
+       /* Update nc_nodes of the originator */
+       batadv_nc_update_nc_node(bat_priv, orig_node, orig_neigh_node,
+                                batadv_ogm_packet, is_single_hop_neigh);
+
        orig_neigh_router = batadv_orig_node_get_router(orig_neigh_node);
 
        /* drop packet if sender is not a direct neighbor and if we
index 6a4f728680ae5c347f74c7930bf5c0d672c5f6bc..379061c725491ec0801940265aedd21245e4ccf1 100644 (file)
@@ -341,7 +341,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
        }
 
        if (vid != -1)
-               skb = vlan_insert_tag(skb, vid);
+               skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), vid);
 
        skb_reset_mac_header(skb);
        skb->protocol = eth_type_trans(skb, soft_iface);
index 6ae86516db4dcebd6125d8d04976c33a06ff9f5f..f186a55b23c3fbd1eda3a2d5877b5b781fd647eb 100644 (file)
@@ -32,6 +32,7 @@
 #include "icmp_socket.h"
 #include "bridge_loop_avoidance.h"
 #include "distributed-arp-table.h"
+#include "network-coding.h"
 
 static struct dentry *batadv_debugfs;
 
@@ -310,6 +311,14 @@ struct batadv_debuginfo {
        const struct file_operations fops;
 };
 
+#ifdef CONFIG_BATMAN_ADV_NC
+static int batadv_nc_nodes_open(struct inode *inode, struct file *file)
+{
+       struct net_device *net_dev = (struct net_device *)inode->i_private;
+       return single_open(file, batadv_nc_nodes_seq_print_text, net_dev);
+}
+#endif
+
 #define BATADV_DEBUGINFO(_name, _mode, _open)          \
 struct batadv_debuginfo batadv_debuginfo_##_name = {   \
        .attr = { .name = __stringify(_name),           \
@@ -348,6 +357,9 @@ static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open);
 static BATADV_DEBUGINFO(transtable_local, S_IRUGO,
                        batadv_transtable_local_open);
 static BATADV_DEBUGINFO(vis_data, S_IRUGO, batadv_vis_data_open);
+#ifdef CONFIG_BATMAN_ADV_NC
+static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
+#endif
 
 static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
        &batadv_debuginfo_originators,
@@ -362,6 +374,9 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
 #endif
        &batadv_debuginfo_transtable_local,
        &batadv_debuginfo_vis_data,
+#ifdef CONFIG_BATMAN_ADV_NC
+       &batadv_debuginfo_nc_nodes,
+#endif
        NULL,
 };
 
@@ -431,6 +446,9 @@ int batadv_debugfs_add_meshif(struct net_device *dev)
                }
        }
 
+       if (batadv_nc_init_debugfs(bat_priv) < 0)
+               goto rem_attr;
+
        return 0;
 rem_attr:
        debugfs_remove_recursive(bat_priv->debug_dir);
index d54188a112eaee9198c23a60e6ed1ae518fc5b31..8e15d966d9b0a9f30e1010c242c440e206f73ea2 100644 (file)
@@ -816,7 +816,6 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
        bool ret = false;
        struct batadv_dat_entry *dat_entry = NULL;
        struct sk_buff *skb_new;
-       struct batadv_hard_iface *primary_if = NULL;
 
        if (!atomic_read(&bat_priv->distributed_arp_table))
                goto out;
@@ -838,22 +837,18 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
 
        dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
        if (dat_entry) {
-               primary_if = batadv_primary_if_get_selected(bat_priv);
-               if (!primary_if)
-                       goto out;
-
                skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
-                                    primary_if->soft_iface, ip_dst, hw_src,
+                                    bat_priv->soft_iface, ip_dst, hw_src,
                                     dat_entry->mac_addr, hw_src);
                if (!skb_new)
                        goto out;
 
                skb_reset_mac_header(skb_new);
                skb_new->protocol = eth_type_trans(skb_new,
-                                                  primary_if->soft_iface);
+                                                  bat_priv->soft_iface);
                bat_priv->stats.rx_packets++;
                bat_priv->stats.rx_bytes += skb->len + ETH_HLEN;
-               primary_if->soft_iface->last_rx = jiffies;
+               bat_priv->soft_iface->last_rx = jiffies;
 
                netif_rx(skb_new);
                batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n");
@@ -866,8 +861,6 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
 out:
        if (dat_entry)
                batadv_dat_entry_free_ref(dat_entry);
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
        return ret;
 }
 
@@ -887,7 +880,6 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
        __be32 ip_src, ip_dst;
        uint8_t *hw_src;
        struct sk_buff *skb_new;
-       struct batadv_hard_iface *primary_if = NULL;
        struct batadv_dat_entry *dat_entry = NULL;
        bool ret = false;
        int err;
@@ -912,12 +904,8 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
        if (!dat_entry)
                goto out;
 
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
-
        skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
-                            primary_if->soft_iface, ip_dst, hw_src,
+                            bat_priv->soft_iface, ip_dst, hw_src,
                             dat_entry->mac_addr, hw_src);
 
        if (!skb_new)
@@ -941,8 +929,6 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
 out:
        if (dat_entry)
                batadv_dat_entry_free_ref(dat_entry);
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
        if (ret)
                kfree_skb(skb);
        return ret;
index 34f99a46ec1d142b66ec92314d5f2e7fac35a15c..f105219f4a4b120810973e0ae8cb31a8c4e2aca1 100644 (file)
@@ -500,7 +500,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
        rcu_read_unlock();
 
        if (gw_count == 0)
-               seq_printf(seq, "No gateways in range ...\n");
+               seq_puts(seq, "No gateways in range ...\n");
 
 out:
        if (primary_if)
index 368219e026a96fbf2ca50df30cb49bd2c54d5978..522243aff2f3b4a45895efea0752405bbee95fa9 100644 (file)
@@ -307,11 +307,35 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
        batadv_update_min_mtu(hard_iface->soft_iface);
 }
 
+/**
+ * batadv_master_del_slave - remove hard_iface from the current master interface
+ * @slave: the interface enslaved in another master
+ * @master: the master from which slave has to be removed
+ *
+ * Invoke ndo_del_slave on master passing slave as argument. In this way slave
+ * is free'd and master can correctly change its internal state.
+ * Return 0 on success, a negative value representing the error otherwise
+ */
+static int batadv_master_del_slave(struct batadv_hard_iface *slave,
+                                  struct net_device *master)
+{
+       int ret;
+
+       if (!master)
+               return 0;
+
+       ret = -EBUSY;
+       if (master->netdev_ops->ndo_del_slave)
+               ret = master->netdev_ops->ndo_del_slave(master, slave->net_dev);
+
+       return ret;
+}
+
 int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
                                   const char *iface_name)
 {
        struct batadv_priv *bat_priv;
-       struct net_device *soft_iface;
+       struct net_device *soft_iface, *master;
        __be16 ethertype = __constant_htons(ETH_P_BATMAN);
        int ret;
 
@@ -321,11 +345,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
        if (!atomic_inc_not_zero(&hard_iface->refcount))
                goto out;
 
-       /* hard-interface is part of a bridge */
-       if (hard_iface->net_dev->priv_flags & IFF_BRIDGE_PORT)
-               pr_err("You are about to enable batman-adv on '%s' which already is part of a bridge. Unless you know exactly what you are doing this is probably wrong and won't work the way you think it would.\n",
-                      hard_iface->net_dev->name);
-
        soft_iface = dev_get_by_name(&init_net, iface_name);
 
        if (!soft_iface) {
@@ -347,12 +366,24 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
                goto err_dev;
        }
 
+       /* check if the interface is enslaved in another virtual one and
+        * in that case unlink it first
+        */
+       master = netdev_master_upper_dev_get(hard_iface->net_dev);
+       ret = batadv_master_del_slave(hard_iface, master);
+       if (ret)
+               goto err_dev;
+
        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);
+       if (ret)
+               goto err_dev;
+
        ret = bat_priv->bat_algo_ops->bat_iface_enable(hard_iface);
        if (ret < 0)
-               goto err_dev;
+               goto err_upper;
 
        hard_iface->if_num = bat_priv->num_ifaces;
        bat_priv->num_ifaces++;
@@ -362,7 +393,7 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
                bat_priv->bat_algo_ops->bat_iface_disable(hard_iface);
                bat_priv->num_ifaces--;
                hard_iface->if_status = BATADV_IF_NOT_IN_USE;
-               goto err_dev;
+               goto err_upper;
        }
 
        hard_iface->batman_adv_ptype.type = ethertype;
@@ -401,14 +432,18 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
 out:
        return 0;
 
+err_upper:
+       netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface);
 err_dev:
+       hard_iface->soft_iface = NULL;
        dev_put(soft_iface);
 err:
        batadv_hardif_free_ref(hard_iface);
        return ret;
 }
 
-void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
+void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
+                                    enum batadv_hard_if_cleanup autodel)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
        struct batadv_hard_iface *primary_if = NULL;
@@ -446,9 +481,10 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
        dev_put(hard_iface->soft_iface);
 
        /* nobody uses this interface anymore */
-       if (!bat_priv->num_ifaces)
-               batadv_softif_destroy(hard_iface->soft_iface);
+       if (!bat_priv->num_ifaces && autodel == BATADV_IF_CLEANUP_AUTO)
+               batadv_softif_destroy_sysfs(hard_iface->soft_iface);
 
+       netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface);
        hard_iface->soft_iface = NULL;
        batadv_hardif_free_ref(hard_iface);
 
@@ -533,7 +569,8 @@ static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
 
        /* first deactivate interface */
        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
-               batadv_hardif_disable_interface(hard_iface);
+               batadv_hardif_disable_interface(hard_iface,
+                                               BATADV_IF_CLEANUP_AUTO);
 
        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
                return;
@@ -563,6 +600,11 @@ static int batadv_hard_if_event(struct notifier_block *this,
        struct batadv_hard_iface *primary_if = NULL;
        struct batadv_priv *bat_priv;
 
+       if (batadv_softif_is_valid(net_dev) && event == NETDEV_REGISTER) {
+               batadv_sysfs_add_meshif(net_dev);
+               return NOTIFY_DONE;
+       }
+
        hard_iface = batadv_hardif_get_by_netdev(net_dev);
        if (!hard_iface && event == NETDEV_REGISTER)
                hard_iface = batadv_hardif_add_interface(net_dev);
index 308437d52e221f3dfdaf917cfeee8676dd422c20..49892881a7c5bb581fc92837617380ad5b62752f 100644 (file)
@@ -29,13 +29,24 @@ enum batadv_hard_if_state {
        BATADV_IF_I_WANT_YOU,
 };
 
+/**
+ * enum batadv_hard_if_cleanup - Cleanup modi for soft_iface after slave removal
+ * @BATADV_IF_CLEANUP_KEEP: Don't automatically delete soft-interface
+ * @BATADV_IF_CLEANUP_AUTO: Delete soft-interface after last slave was removed
+ */
+enum batadv_hard_if_cleanup {
+       BATADV_IF_CLEANUP_KEEP,
+       BATADV_IF_CLEANUP_AUTO,
+};
+
 extern struct notifier_block batadv_hard_if_notifier;
 
 struct batadv_hard_iface*
 batadv_hardif_get_by_netdev(const struct net_device *net_dev);
 int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
                                   const char *iface_name);
-void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
+void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
+                                    enum batadv_hard_if_cleanup autodel);
 void batadv_hardif_remove_interfaces(void);
 int batadv_hardif_min_mtu(struct net_device *soft_iface);
 void batadv_update_min_mtu(struct net_device *soft_iface);
index fa563e497c4861a3fd1656a91887eed47e81ec81..3e30a0f1b908b2872bcb6a13100306e0deda0c85 100644 (file)
@@ -35,6 +35,7 @@
 #include "vis.h"
 #include "hash.h"
 #include "bat_algo.h"
+#include "network-coding.h"
 
 
 /* List manipulations on hardif_list have to be rtnl_lock()'ed,
@@ -70,6 +71,7 @@ static int __init batadv_init(void)
        batadv_debugfs_init();
 
        register_netdevice_notifier(&batadv_hard_if_notifier);
+       rtnl_link_register(&batadv_link_ops);
 
        pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) loaded\n",
                BATADV_SOURCE_VERSION, BATADV_COMPAT_VERSION);
@@ -80,6 +82,7 @@ static int __init batadv_init(void)
 static void __exit batadv_exit(void)
 {
        batadv_debugfs_destroy();
+       rtnl_link_unregister(&batadv_link_ops);
        unregister_netdevice_notifier(&batadv_hard_if_notifier);
        batadv_hardif_remove_interfaces();
 
@@ -135,6 +138,10 @@ int batadv_mesh_init(struct net_device *soft_iface)
        if (ret < 0)
                goto err;
 
+       ret = batadv_nc_init(bat_priv);
+       if (ret < 0)
+               goto err;
+
        atomic_set(&bat_priv->gw.reselect, 0);
        atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
 
@@ -157,6 +164,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
 
        batadv_gw_node_purge(bat_priv);
        batadv_originator_free(bat_priv);
+       batadv_nc_free(bat_priv);
 
        batadv_tt_free(bat_priv);
 
@@ -169,6 +177,12 @@ void batadv_mesh_free(struct net_device *soft_iface)
        atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE);
 }
 
+/**
+ * batadv_is_my_mac - check if the given mac address belongs to any of the real
+ * interfaces in the current mesh
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the address to check
+ */
 int batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr)
 {
        const struct batadv_hard_iface *hard_iface;
@@ -414,7 +428,7 @@ int batadv_algo_seq_print_text(struct seq_file *seq, void *offset)
 {
        struct batadv_algo_ops *bat_algo_ops;
 
-       seq_printf(seq, "Available routing algorithms:\n");
+       seq_puts(seq, "Available routing algorithms:\n");
 
        hlist_for_each_entry(bat_algo_ops, &batadv_algo_list, list) {
                seq_printf(seq, "%s\n", bat_algo_ops->name);
index d40910dfc8ea5a20fbe2c839f59bd65018bf1c28..59a0d6af15c88aca2f05c0883fc2fdfeadea66f4 100644 (file)
@@ -26,7 +26,7 @@
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2013.1.0"
+#define BATADV_SOURCE_VERSION "2013.2.0"
 #endif
 
 /* B.A.T.M.A.N. parameters */
 #define BATADV_RESET_PROTECTION_MS 30000
 #define BATADV_EXPECTED_SEQNO_RANGE    65536
 
+#define BATADV_NC_NODE_TIMEOUT 10000 /* Milliseconds */
+
 enum batadv_mesh_state {
        BATADV_MESH_INACTIVE,
        BATADV_MESH_ACTIVE,
@@ -150,6 +152,7 @@ enum batadv_uev_type {
 #include <linux/percpu.h>
 #include <linux/slab.h>
 #include <net/sock.h>          /* struct sock */
+#include <net/rtnetlink.h>
 #include <linux/jiffies.h>
 #include <linux/seq_file.h>
 #include "types.h"
@@ -185,6 +188,7 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr);
  * @BATADV_DBG_TT: translation table messages
  * @BATADV_DBG_BLA: bridge loop avoidance messages
  * @BATADV_DBG_DAT: ARP snooping and DAT related messages
+ * @BATADV_DBG_NC: network coding related messages
  * @BATADV_DBG_ALL: the union of all the above log levels
  */
 enum batadv_dbg_level {
@@ -193,7 +197,8 @@ enum batadv_dbg_level {
        BATADV_DBG_TT     = BIT(2),
        BATADV_DBG_BLA    = BIT(3),
        BATADV_DBG_DAT    = BIT(4),
-       BATADV_DBG_ALL    = 31,
+       BATADV_DBG_NC     = BIT(5),
+       BATADV_DBG_ALL    = 63,
 };
 
 #ifdef CONFIG_BATMAN_ADV_DEBUG
@@ -298,4 +303,10 @@ static inline uint64_t batadv_sum_counter(struct batadv_priv *bat_priv,
        return sum;
 }
 
+/* Define a macro to reach the control buffer of the skb. The members of the
+ * control buffer are defined in struct batadv_skb_cb in types.h.
+ * The macro is inspired by the similar macro TCP_SKB_CB() in tcp.h.
+ */
+#define BATADV_SKB_CB(__skb)       ((struct batadv_skb_cb *)&((__skb)->cb[0]))
+
 #endif /* _NET_BATMAN_ADV_MAIN_H_ */
diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c
new file mode 100644 (file)
index 0000000..f7c5430
--- /dev/null
@@ -0,0 +1,1822 @@
+/* Copyright (C) 2012-2013 B.A.T.M.A.N. contributors:
+ *
+ * Martin Hundebøll, Jeppe Ledet-Pedersen
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include <linux/debugfs.h>
+
+#include "main.h"
+#include "hash.h"
+#include "network-coding.h"
+#include "send.h"
+#include "originator.h"
+#include "hard-interface.h"
+#include "routing.h"
+
+static struct lock_class_key batadv_nc_coding_hash_lock_class_key;
+static struct lock_class_key batadv_nc_decoding_hash_lock_class_key;
+
+static void batadv_nc_worker(struct work_struct *work);
+static int batadv_nc_recv_coded_packet(struct sk_buff *skb,
+                                      struct batadv_hard_iface *recv_if);
+
+/**
+ * batadv_nc_start_timer - initialise the nc periodic worker
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
+{
+       queue_delayed_work(batadv_event_workqueue, &bat_priv->nc.work,
+                          msecs_to_jiffies(10));
+}
+
+/**
+ * batadv_nc_init - initialise coding hash table and start house keeping
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+int batadv_nc_init(struct batadv_priv *bat_priv)
+{
+       bat_priv->nc.timestamp_fwd_flush = jiffies;
+       bat_priv->nc.timestamp_sniffed_purge = jiffies;
+
+       if (bat_priv->nc.coding_hash || bat_priv->nc.decoding_hash)
+               return 0;
+
+       bat_priv->nc.coding_hash = batadv_hash_new(128);
+       if (!bat_priv->nc.coding_hash)
+               goto err;
+
+       batadv_hash_set_lock_class(bat_priv->nc.coding_hash,
+                                  &batadv_nc_coding_hash_lock_class_key);
+
+       bat_priv->nc.decoding_hash = batadv_hash_new(128);
+       if (!bat_priv->nc.decoding_hash)
+               goto err;
+
+       batadv_hash_set_lock_class(bat_priv->nc.coding_hash,
+                                  &batadv_nc_decoding_hash_lock_class_key);
+
+       /* Register our packet type */
+       if (batadv_recv_handler_register(BATADV_CODED,
+                                        batadv_nc_recv_coded_packet) < 0)
+               goto err;
+
+       INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
+       batadv_nc_start_timer(bat_priv);
+
+       return 0;
+
+err:
+       return -ENOMEM;
+}
+
+/**
+ * batadv_nc_init_bat_priv - initialise the nc specific bat_priv variables
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv)
+{
+       atomic_set(&bat_priv->network_coding, 1);
+       bat_priv->nc.min_tq = 200;
+       bat_priv->nc.max_fwd_delay = 10;
+       bat_priv->nc.max_buffer_time = 200;
+}
+
+/**
+ * batadv_nc_init_orig - initialise the nc fields of an orig_node
+ * @orig_node: the orig_node which is going to be initialised
+ */
+void batadv_nc_init_orig(struct batadv_orig_node *orig_node)
+{
+       INIT_LIST_HEAD(&orig_node->in_coding_list);
+       INIT_LIST_HEAD(&orig_node->out_coding_list);
+       spin_lock_init(&orig_node->in_coding_list_lock);
+       spin_lock_init(&orig_node->out_coding_list_lock);
+}
+
+/**
+ * batadv_nc_node_free_rcu - rcu callback to free an nc node and remove
+ *  its refcount on the orig_node
+ * @rcu: rcu pointer of the nc node
+ */
+static void batadv_nc_node_free_rcu(struct rcu_head *rcu)
+{
+       struct batadv_nc_node *nc_node;
+
+       nc_node = container_of(rcu, struct batadv_nc_node, rcu);
+       batadv_orig_node_free_ref(nc_node->orig_node);
+       kfree(nc_node);
+}
+
+/**
+ * batadv_nc_node_free_ref - decrements the nc node refcounter and possibly
+ * frees it
+ * @nc_node: the nc node to free
+ */
+static void batadv_nc_node_free_ref(struct batadv_nc_node *nc_node)
+{
+       if (atomic_dec_and_test(&nc_node->refcount))
+               call_rcu(&nc_node->rcu, batadv_nc_node_free_rcu);
+}
+
+/**
+ * batadv_nc_path_free_ref - decrements the nc path refcounter and possibly
+ * frees it
+ * @nc_path: the nc node to free
+ */
+static void batadv_nc_path_free_ref(struct batadv_nc_path *nc_path)
+{
+       if (atomic_dec_and_test(&nc_path->refcount))
+               kfree_rcu(nc_path, rcu);
+}
+
+/**
+ * batadv_nc_packet_free - frees nc packet
+ * @nc_packet: the nc packet to free
+ */
+static void batadv_nc_packet_free(struct batadv_nc_packet *nc_packet)
+{
+       if (nc_packet->skb)
+               kfree_skb(nc_packet->skb);
+
+       batadv_nc_path_free_ref(nc_packet->nc_path);
+       kfree(nc_packet);
+}
+
+/**
+ * batadv_nc_to_purge_nc_node - checks whether an nc node has to be purged
+ * @bat_priv: the bat priv with all the soft interface information
+ * @nc_node: the nc node to check
+ *
+ * Returns true if the entry has to be purged now, false otherwise
+ */
+static bool batadv_nc_to_purge_nc_node(struct batadv_priv *bat_priv,
+                                      struct batadv_nc_node *nc_node)
+{
+       if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
+               return true;
+
+       return batadv_has_timed_out(nc_node->last_seen, BATADV_NC_NODE_TIMEOUT);
+}
+
+/**
+ * batadv_nc_to_purge_nc_path_coding - checks whether an nc path has timed out
+ * @bat_priv: the bat priv with all the soft interface information
+ * @nc_path: the nc path to check
+ *
+ * Returns true if the entry has to be purged now, false otherwise
+ */
+static bool batadv_nc_to_purge_nc_path_coding(struct batadv_priv *bat_priv,
+                                             struct batadv_nc_path *nc_path)
+{
+       if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
+               return true;
+
+       /* purge the path when no packets has been added for 10 times the
+        * max_fwd_delay time
+        */
+       return batadv_has_timed_out(nc_path->last_valid,
+                                   bat_priv->nc.max_fwd_delay * 10);
+}
+
+/**
+ * batadv_nc_to_purge_nc_path_decoding - checks whether an nc path has timed out
+ * @bat_priv: the bat priv with all the soft interface information
+ * @nc_path: the nc path to check
+ *
+ * Returns true if the entry has to be purged now, false otherwise
+ */
+static bool batadv_nc_to_purge_nc_path_decoding(struct batadv_priv *bat_priv,
+                                               struct batadv_nc_path *nc_path)
+{
+       if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
+               return true;
+
+       /* purge the path when no packets has been added for 10 times the
+        * max_buffer time
+        */
+       return batadv_has_timed_out(nc_path->last_valid,
+                                   bat_priv->nc.max_buffer_time*10);
+}
+
+/**
+ * batadv_nc_purge_orig_nc_nodes - go through list of nc nodes and purge stale
+ *  entries
+ * @bat_priv: the bat priv with all the soft interface information
+ * @list: list of nc nodes
+ * @lock: nc node list lock
+ * @to_purge: function in charge to decide whether an entry has to be purged or
+ *           not. This function takes the nc node as argument and has to return
+ *           a boolean value: true if the entry has to be deleted, false
+ *           otherwise
+ */
+static void
+batadv_nc_purge_orig_nc_nodes(struct batadv_priv *bat_priv,
+                             struct list_head *list,
+                             spinlock_t *lock,
+                             bool (*to_purge)(struct batadv_priv *,
+                                              struct batadv_nc_node *))
+{
+       struct batadv_nc_node *nc_node, *nc_node_tmp;
+
+       /* For each nc_node in list */
+       spin_lock_bh(lock);
+       list_for_each_entry_safe(nc_node, nc_node_tmp, list, list) {
+               /* if an helper function has been passed as parameter,
+                * ask it if the entry has to be purged or not
+                */
+               if (to_purge && !to_purge(bat_priv, nc_node))
+                       continue;
+
+               batadv_dbg(BATADV_DBG_NC, bat_priv,
+                          "Removing nc_node %pM -> %pM\n",
+                          nc_node->addr, nc_node->orig_node->orig);
+               list_del_rcu(&nc_node->list);
+               batadv_nc_node_free_ref(nc_node);
+       }
+       spin_unlock_bh(lock);
+}
+
+/**
+ * batadv_nc_purge_orig - purges all nc node data attached of the given
+ *  originator
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: orig_node with the nc node entries to be purged
+ * @to_purge: function in charge to decide whether an entry has to be purged or
+ *           not. This function takes the nc node as argument and has to return
+ *           a boolean value: true is the entry has to be deleted, false
+ *           otherwise
+ */
+void batadv_nc_purge_orig(struct batadv_priv *bat_priv,
+                         struct batadv_orig_node *orig_node,
+                         bool (*to_purge)(struct batadv_priv *,
+                                          struct batadv_nc_node *))
+{
+       /* Check ingoing nc_node's of this orig_node */
+       batadv_nc_purge_orig_nc_nodes(bat_priv, &orig_node->in_coding_list,
+                                     &orig_node->in_coding_list_lock,
+                                     to_purge);
+
+       /* Check outgoing nc_node's of this orig_node */
+       batadv_nc_purge_orig_nc_nodes(bat_priv, &orig_node->out_coding_list,
+                                     &orig_node->out_coding_list_lock,
+                                     to_purge);
+}
+
+/**
+ * batadv_nc_purge_orig_hash - traverse entire originator hash to check if they
+ *  have timed out nc nodes
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_nc_purge_orig_hash(struct batadv_priv *bat_priv)
+{
+       struct batadv_hashtable *hash = bat_priv->orig_hash;
+       struct hlist_head *head;
+       struct batadv_orig_node *orig_node;
+       uint32_t i;
+
+       if (!hash)
+               return;
+
+       /* For each orig_node */
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(orig_node, head, hash_entry)
+                       batadv_nc_purge_orig(bat_priv, orig_node,
+                                            batadv_nc_to_purge_nc_node);
+               rcu_read_unlock();
+       }
+}
+
+/**
+ * batadv_nc_purge_paths - traverse all nc paths part of the hash and remove
+ *  unused ones
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hash: hash table containing the nc paths to check
+ * @to_purge: function in charge to decide whether an entry has to be purged or
+ *           not. This function takes the nc node as argument and has to return
+ *           a boolean value: true is the entry has to be deleted, false
+ *           otherwise
+ */
+static void batadv_nc_purge_paths(struct batadv_priv *bat_priv,
+                                 struct batadv_hashtable *hash,
+                                 bool (*to_purge)(struct batadv_priv *,
+                                                  struct batadv_nc_path *))
+{
+       struct hlist_head *head;
+       struct hlist_node *node_tmp;
+       struct batadv_nc_path *nc_path;
+       spinlock_t *lock; /* Protects lists in hash */
+       uint32_t i;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+               lock = &hash->list_locks[i];
+
+               /* For each nc_path in this bin */
+               spin_lock_bh(lock);
+               hlist_for_each_entry_safe(nc_path, node_tmp, head, hash_entry) {
+                       /* if an helper function has been passed as parameter,
+                        * ask it if the entry has to be purged or not
+                        */
+                       if (to_purge && !to_purge(bat_priv, nc_path))
+                               continue;
+
+                       /* purging an non-empty nc_path should never happen, but
+                        * is observed under high CPU load. Delay the purging
+                        * until next iteration to allow the packet_list to be
+                        * emptied first.
+                        */
+                       if (!unlikely(list_empty(&nc_path->packet_list))) {
+                               net_ratelimited_function(printk,
+                                                        KERN_WARNING
+                                                        "Skipping free of non-empty nc_path (%pM -> %pM)!\n",
+                                                        nc_path->prev_hop,
+                                                        nc_path->next_hop);
+                               continue;
+                       }
+
+                       /* nc_path is unused, so remove it */
+                       batadv_dbg(BATADV_DBG_NC, bat_priv,
+                                  "Remove nc_path %pM -> %pM\n",
+                                  nc_path->prev_hop, nc_path->next_hop);
+                       hlist_del_rcu(&nc_path->hash_entry);
+                       batadv_nc_path_free_ref(nc_path);
+               }
+               spin_unlock_bh(lock);
+       }
+}
+
+/**
+ * batadv_nc_hash_key_gen - computes the nc_path hash key
+ * @key: buffer to hold the final hash key
+ * @src: source ethernet mac address going into the hash key
+ * @dst: destination ethernet mac address going into the hash key
+ */
+static void batadv_nc_hash_key_gen(struct batadv_nc_path *key, const char *src,
+                                  const char *dst)
+{
+       memcpy(key->prev_hop, src, sizeof(key->prev_hop));
+       memcpy(key->next_hop, dst, sizeof(key->next_hop));
+}
+
+/**
+ * batadv_nc_hash_choose - compute the hash value for an nc path
+ * @data: data to hash
+ * @size: size of the hash table
+ *
+ * Returns the selected index in the hash table for the given data.
+ */
+static uint32_t batadv_nc_hash_choose(const void *data, uint32_t size)
+{
+       const struct batadv_nc_path *nc_path = data;
+       uint32_t hash = 0;
+
+       hash = batadv_hash_bytes(hash, &nc_path->prev_hop,
+                                sizeof(nc_path->prev_hop));
+       hash = batadv_hash_bytes(hash, &nc_path->next_hop,
+                                sizeof(nc_path->next_hop));
+
+       hash += (hash << 3);
+       hash ^= (hash >> 11);
+       hash += (hash << 15);
+
+       return hash % size;
+}
+
+/**
+ * batadv_nc_hash_compare - comparing function used in the network coding hash
+ *  tables
+ * @node: node in the local table
+ * @data2: second object to compare the node to
+ *
+ * Returns 1 if the two entry are the same, 0 otherwise
+ */
+static int batadv_nc_hash_compare(const struct hlist_node *node,
+                                 const void *data2)
+{
+       const struct batadv_nc_path *nc_path1, *nc_path2;
+
+       nc_path1 = container_of(node, struct batadv_nc_path, hash_entry);
+       nc_path2 = data2;
+
+       /* Return 1 if the two keys are identical */
+       if (memcmp(nc_path1->prev_hop, nc_path2->prev_hop,
+                  sizeof(nc_path1->prev_hop)) != 0)
+               return 0;
+
+       if (memcmp(nc_path1->next_hop, nc_path2->next_hop,
+                  sizeof(nc_path1->next_hop)) != 0)
+               return 0;
+
+       return 1;
+}
+
+/**
+ * batadv_nc_hash_find - search for an existing nc path and return it
+ * @hash: hash table containing the nc path
+ * @data: search key
+ *
+ * Returns the nc_path if found, NULL otherwise.
+ */
+static struct batadv_nc_path *
+batadv_nc_hash_find(struct batadv_hashtable *hash,
+                   void *data)
+{
+       struct hlist_head *head;
+       struct batadv_nc_path *nc_path, *nc_path_tmp = NULL;
+       int index;
+
+       if (!hash)
+               return NULL;
+
+       index = batadv_nc_hash_choose(data, hash->size);
+       head = &hash->table[index];
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(nc_path, head, hash_entry) {
+               if (!batadv_nc_hash_compare(&nc_path->hash_entry, data))
+                       continue;
+
+               if (!atomic_inc_not_zero(&nc_path->refcount))
+                       continue;
+
+               nc_path_tmp = nc_path;
+               break;
+       }
+       rcu_read_unlock();
+
+       return nc_path_tmp;
+}
+
+/**
+ * batadv_nc_send_packet - send non-coded packet and free nc_packet struct
+ * @nc_packet: the nc packet to send
+ */
+static void batadv_nc_send_packet(struct batadv_nc_packet *nc_packet)
+{
+       batadv_send_skb_packet(nc_packet->skb,
+                              nc_packet->neigh_node->if_incoming,
+                              nc_packet->nc_path->next_hop);
+       nc_packet->skb = NULL;
+       batadv_nc_packet_free(nc_packet);
+}
+
+/**
+ * batadv_nc_sniffed_purge - Checks timestamp of given sniffed nc_packet.
+ * @bat_priv: the bat priv with all the soft interface information
+ * @nc_path: the nc path the packet belongs to
+ * @nc_packet: the nc packet to be checked
+ *
+ * Checks whether the given sniffed (overheard) nc_packet has hit its buffering
+ * timeout. If so, the packet is no longer kept and the entry deleted from the
+ * queue. Has to be called with the appropriate locks.
+ *
+ * Returns false as soon as the entry in the fifo queue has not been timed out
+ * yet and true otherwise.
+ */
+static bool batadv_nc_sniffed_purge(struct batadv_priv *bat_priv,
+                                   struct batadv_nc_path *nc_path,
+                                   struct batadv_nc_packet *nc_packet)
+{
+       unsigned long timeout = bat_priv->nc.max_buffer_time;
+       bool res = false;
+
+       /* Packets are added to tail, so the remaining packets did not time
+        * out and we can stop processing the current queue
+        */
+       if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE &&
+           !batadv_has_timed_out(nc_packet->timestamp, timeout))
+               goto out;
+
+       /* purge nc packet */
+       list_del(&nc_packet->list);
+       batadv_nc_packet_free(nc_packet);
+
+       res = true;
+
+out:
+       return res;
+}
+
+/**
+ * batadv_nc_fwd_flush - Checks the timestamp of the given nc packet.
+ * @bat_priv: the bat priv with all the soft interface information
+ * @nc_path: the nc path the packet belongs to
+ * @nc_packet: the nc packet to be checked
+ *
+ * Checks whether the given nc packet has hit its forward timeout. If so, the
+ * packet is no longer delayed, immediately sent and the entry deleted from the
+ * queue. Has to be called with the appropriate locks.
+ *
+ * Returns false as soon as the entry in the fifo queue has not been timed out
+ * yet and true otherwise.
+ */
+static bool batadv_nc_fwd_flush(struct batadv_priv *bat_priv,
+                               struct batadv_nc_path *nc_path,
+                               struct batadv_nc_packet *nc_packet)
+{
+       unsigned long timeout = bat_priv->nc.max_fwd_delay;
+
+       /* Packets are added to tail, so the remaining packets did not time
+        * out and we can stop processing the current queue
+        */
+       if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE &&
+           !batadv_has_timed_out(nc_packet->timestamp, timeout))
+               return false;
+
+       /* Send packet */
+       batadv_inc_counter(bat_priv, BATADV_CNT_FORWARD);
+       batadv_add_counter(bat_priv, BATADV_CNT_FORWARD_BYTES,
+                          nc_packet->skb->len + ETH_HLEN);
+       list_del(&nc_packet->list);
+       batadv_nc_send_packet(nc_packet);
+
+       return true;
+}
+
+/**
+ * batadv_nc_process_nc_paths - traverse given nc packet pool and free timed out
+ *  nc packets
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hash: to be processed hash table
+ * @process_fn: Function called to process given nc packet. Should return true
+ *             to encourage this function to proceed with the next packet.
+ *             Otherwise the rest of the current queue is skipped.
+ */
+static void
+batadv_nc_process_nc_paths(struct batadv_priv *bat_priv,
+                          struct batadv_hashtable *hash,
+                          bool (*process_fn)(struct batadv_priv *,
+                                             struct batadv_nc_path *,
+                                             struct batadv_nc_packet *))
+{
+       struct hlist_head *head;
+       struct batadv_nc_packet *nc_packet, *nc_packet_tmp;
+       struct batadv_nc_path *nc_path;
+       bool ret;
+       int i;
+
+       if (!hash)
+               return;
+
+       /* Loop hash table bins */
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               /* Loop coding paths */
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(nc_path, head, hash_entry) {
+                       /* Loop packets */
+                       spin_lock_bh(&nc_path->packet_list_lock);
+                       list_for_each_entry_safe(nc_packet, nc_packet_tmp,
+                                                &nc_path->packet_list, list) {
+                               ret = process_fn(bat_priv, nc_path, nc_packet);
+                               if (!ret)
+                                       break;
+                       }
+                       spin_unlock_bh(&nc_path->packet_list_lock);
+               }
+               rcu_read_unlock();
+       }
+}
+
+/**
+ * batadv_nc_worker - periodic task for house keeping related to network coding
+ * @work: kernel work struct
+ */
+static void batadv_nc_worker(struct work_struct *work)
+{
+       struct delayed_work *delayed_work;
+       struct batadv_priv_nc *priv_nc;
+       struct batadv_priv *bat_priv;
+       unsigned long timeout;
+
+       delayed_work = container_of(work, struct delayed_work, work);
+       priv_nc = container_of(delayed_work, struct batadv_priv_nc, work);
+       bat_priv = container_of(priv_nc, struct batadv_priv, nc);
+
+       batadv_nc_purge_orig_hash(bat_priv);
+       batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash,
+                             batadv_nc_to_purge_nc_path_coding);
+       batadv_nc_purge_paths(bat_priv, bat_priv->nc.decoding_hash,
+                             batadv_nc_to_purge_nc_path_decoding);
+
+       timeout = bat_priv->nc.max_fwd_delay;
+
+       if (batadv_has_timed_out(bat_priv->nc.timestamp_fwd_flush, timeout)) {
+               batadv_nc_process_nc_paths(bat_priv, bat_priv->nc.coding_hash,
+                                          batadv_nc_fwd_flush);
+               bat_priv->nc.timestamp_fwd_flush = jiffies;
+       }
+
+       if (batadv_has_timed_out(bat_priv->nc.timestamp_sniffed_purge,
+                                bat_priv->nc.max_buffer_time)) {
+               batadv_nc_process_nc_paths(bat_priv, bat_priv->nc.decoding_hash,
+                                          batadv_nc_sniffed_purge);
+               bat_priv->nc.timestamp_sniffed_purge = jiffies;
+       }
+
+       /* Schedule a new check */
+       batadv_nc_start_timer(bat_priv);
+}
+
+/**
+ * batadv_can_nc_with_orig - checks whether the given orig node is suitable for
+ *  coding or not
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: neighboring orig node which may be used as nc candidate
+ * @ogm_packet: incoming ogm packet also used for the checks
+ *
+ * Returns true if:
+ *  1) The OGM must have the most recent sequence number.
+ *  2) The TTL must be decremented by one and only one.
+ *  3) The OGM must be received from the first hop from orig_node.
+ *  4) The TQ value of the OGM must be above bat_priv->nc.min_tq.
+ */
+static bool batadv_can_nc_with_orig(struct batadv_priv *bat_priv,
+                                   struct batadv_orig_node *orig_node,
+                                   struct batadv_ogm_packet *ogm_packet)
+{
+       if (orig_node->last_real_seqno != ntohl(ogm_packet->seqno))
+               return false;
+       if (orig_node->last_ttl != ogm_packet->header.ttl + 1)
+               return false;
+       if (!batadv_compare_eth(ogm_packet->orig, ogm_packet->prev_sender))
+               return false;
+       if (ogm_packet->tq < bat_priv->nc.min_tq)
+               return false;
+
+       return true;
+}
+
+/**
+ * batadv_nc_find_nc_node - search for an existing nc node and return it
+ * @orig_node: orig node originating the ogm packet
+ * @orig_neigh_node: neighboring orig node from which we received the ogm packet
+ *  (can be equal to orig_node)
+ * @in_coding: traverse incoming or outgoing network coding list
+ *
+ * Returns the nc_node if found, NULL otherwise.
+ */
+static struct batadv_nc_node
+*batadv_nc_find_nc_node(struct batadv_orig_node *orig_node,
+                       struct batadv_orig_node *orig_neigh_node,
+                       bool in_coding)
+{
+       struct batadv_nc_node *nc_node, *nc_node_out = NULL;
+       struct list_head *list;
+
+       if (in_coding)
+               list = &orig_neigh_node->in_coding_list;
+       else
+               list = &orig_neigh_node->out_coding_list;
+
+       /* Traverse list of nc_nodes to orig_node */
+       rcu_read_lock();
+       list_for_each_entry_rcu(nc_node, list, list) {
+               if (!batadv_compare_eth(nc_node->addr, orig_node->orig))
+                       continue;
+
+               if (!atomic_inc_not_zero(&nc_node->refcount))
+                       continue;
+
+               /* Found a match */
+               nc_node_out = nc_node;
+               break;
+       }
+       rcu_read_unlock();
+
+       return nc_node_out;
+}
+
+/**
+ * batadv_nc_get_nc_node - retrieves an nc node or creates the entry if it was
+ *  not found
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: orig node originating the ogm packet
+ * @orig_neigh_node: neighboring orig node from which we received the ogm packet
+ *  (can be equal to orig_node)
+ * @in_coding: traverse incoming or outgoing network coding list
+ *
+ * Returns the nc_node if found or created, NULL in case of an error.
+ */
+static struct batadv_nc_node
+*batadv_nc_get_nc_node(struct batadv_priv *bat_priv,
+                      struct batadv_orig_node *orig_node,
+                      struct batadv_orig_node *orig_neigh_node,
+                      bool in_coding)
+{
+       struct batadv_nc_node *nc_node;
+       spinlock_t *lock; /* Used to lock list selected by "int in_coding" */
+       struct list_head *list;
+
+       /* Check if nc_node is already added */
+       nc_node = batadv_nc_find_nc_node(orig_node, orig_neigh_node, in_coding);
+
+       /* Node found */
+       if (nc_node)
+               return nc_node;
+
+       nc_node = kzalloc(sizeof(*nc_node), GFP_ATOMIC);
+       if (!nc_node)
+               return NULL;
+
+       if (!atomic_inc_not_zero(&orig_neigh_node->refcount))
+               goto free;
+
+       /* Initialize nc_node */
+       INIT_LIST_HEAD(&nc_node->list);
+       memcpy(nc_node->addr, orig_node->orig, ETH_ALEN);
+       nc_node->orig_node = orig_neigh_node;
+       atomic_set(&nc_node->refcount, 2);
+
+       /* Select ingoing or outgoing coding node */
+       if (in_coding) {
+               lock = &orig_neigh_node->in_coding_list_lock;
+               list = &orig_neigh_node->in_coding_list;
+       } else {
+               lock = &orig_neigh_node->out_coding_list_lock;
+               list = &orig_neigh_node->out_coding_list;
+       }
+
+       batadv_dbg(BATADV_DBG_NC, bat_priv, "Adding nc_node %pM -> %pM\n",
+                  nc_node->addr, nc_node->orig_node->orig);
+
+       /* Add nc_node to orig_node */
+       spin_lock_bh(lock);
+       list_add_tail_rcu(&nc_node->list, list);
+       spin_unlock_bh(lock);
+
+       return nc_node;
+
+free:
+       kfree(nc_node);
+       return NULL;
+}
+
+/**
+ * batadv_nc_update_nc_node - updates stored incoming and outgoing nc node structs
+ *  (best called on incoming OGMs)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: orig node originating the ogm packet
+ * @orig_neigh_node: neighboring orig node from which we received the ogm packet
+ *  (can be equal to orig_node)
+ * @ogm_packet: incoming ogm packet
+ * @is_single_hop_neigh: orig_node is a single hop neighbor
+ */
+void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
+                             struct batadv_orig_node *orig_node,
+                             struct batadv_orig_node *orig_neigh_node,
+                             struct batadv_ogm_packet *ogm_packet,
+                             int is_single_hop_neigh)
+{
+       struct batadv_nc_node *in_nc_node = NULL, *out_nc_node = NULL;
+
+       /* Check if network coding is enabled */
+       if (!atomic_read(&bat_priv->network_coding))
+               goto out;
+
+       /* accept ogms from 'good' neighbors and single hop neighbors */
+       if (!batadv_can_nc_with_orig(bat_priv, orig_node, ogm_packet) &&
+           !is_single_hop_neigh)
+               goto out;
+
+       /* Add orig_node as in_nc_node on hop */
+       in_nc_node = batadv_nc_get_nc_node(bat_priv, orig_node,
+                                          orig_neigh_node, true);
+       if (!in_nc_node)
+               goto out;
+
+       in_nc_node->last_seen = jiffies;
+
+       /* Add hop as out_nc_node on orig_node */
+       out_nc_node = batadv_nc_get_nc_node(bat_priv, orig_neigh_node,
+                                           orig_node, false);
+       if (!out_nc_node)
+               goto out;
+
+       out_nc_node->last_seen = jiffies;
+
+out:
+       if (in_nc_node)
+               batadv_nc_node_free_ref(in_nc_node);
+       if (out_nc_node)
+               batadv_nc_node_free_ref(out_nc_node);
+}
+
+/**
+ * batadv_nc_get_path - get existing nc_path or allocate a new one
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hash: hash table containing the nc path
+ * @src: ethernet source address - first half of the nc path search key
+ * @dst: ethernet destination address - second half of the nc path search key
+ *
+ * Returns pointer to nc_path if the path was found or created, returns NULL
+ * on error.
+ */
+static struct batadv_nc_path *batadv_nc_get_path(struct batadv_priv *bat_priv,
+                                                struct batadv_hashtable *hash,
+                                                uint8_t *src,
+                                                uint8_t *dst)
+{
+       int hash_added;
+       struct batadv_nc_path *nc_path, nc_path_key;
+
+       batadv_nc_hash_key_gen(&nc_path_key, src, dst);
+
+       /* Search for existing nc_path */
+       nc_path = batadv_nc_hash_find(hash, (void *)&nc_path_key);
+
+       if (nc_path) {
+               /* Set timestamp to delay removal of nc_path */
+               nc_path->last_valid = jiffies;
+               return nc_path;
+       }
+
+       /* No existing nc_path was found; create a new */
+       nc_path = kzalloc(sizeof(*nc_path), GFP_ATOMIC);
+
+       if (!nc_path)
+               return NULL;
+
+       /* Initialize nc_path */
+       INIT_LIST_HEAD(&nc_path->packet_list);
+       spin_lock_init(&nc_path->packet_list_lock);
+       atomic_set(&nc_path->refcount, 2);
+       nc_path->last_valid = jiffies;
+       memcpy(nc_path->next_hop, dst, ETH_ALEN);
+       memcpy(nc_path->prev_hop, src, ETH_ALEN);
+
+       batadv_dbg(BATADV_DBG_NC, bat_priv, "Adding nc_path %pM -> %pM\n",
+                  nc_path->prev_hop,
+                  nc_path->next_hop);
+
+       /* Add nc_path to hash table */
+       hash_added = batadv_hash_add(hash, batadv_nc_hash_compare,
+                                    batadv_nc_hash_choose, &nc_path_key,
+                                    &nc_path->hash_entry);
+
+       if (hash_added < 0) {
+               kfree(nc_path);
+               return NULL;
+       }
+
+       return nc_path;
+}
+
+/**
+ * batadv_nc_random_weight_tq - scale the receivers TQ-value to avoid unfair
+ *  selection of a receiver with slightly lower TQ than the other
+ * @tq: to be weighted tq value
+ */
+static uint8_t batadv_nc_random_weight_tq(uint8_t tq)
+{
+       uint8_t rand_val, rand_tq;
+
+       get_random_bytes(&rand_val, sizeof(rand_val));
+
+       /* randomize the estimated packet loss (max TQ - estimated TQ) */
+       rand_tq = rand_val * (BATADV_TQ_MAX_VALUE - tq);
+
+       /* normalize the randomized packet loss */
+       rand_tq /= BATADV_TQ_MAX_VALUE;
+
+       /* convert to (randomized) estimated tq again */
+       return BATADV_TQ_MAX_VALUE - rand_tq;
+}
+
+/**
+ * batadv_nc_memxor - XOR destination with source
+ * @dst: byte array to XOR into
+ * @src: byte array to XOR from
+ * @len: length of destination array
+ */
+static void batadv_nc_memxor(char *dst, const char *src, unsigned int len)
+{
+       unsigned int i;
+
+       for (i = 0; i < len; ++i)
+               dst[i] ^= src[i];
+}
+
+/**
+ * batadv_nc_code_packets - code a received unicast_packet with an nc packet
+ *  into a coded_packet and send it
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: data skb to forward
+ * @ethhdr: pointer to the ethernet header inside the skb
+ * @nc_packet: structure containing the packet to the skb can be coded with
+ * @neigh_node: next hop to forward packet to
+ *
+ * Returns true if both packets are consumed, false otherwise.
+ */
+static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb,
+                                  struct ethhdr *ethhdr,
+                                  struct batadv_nc_packet *nc_packet,
+                                  struct batadv_neigh_node *neigh_node)
+{
+       uint8_t tq_weighted_neigh, tq_weighted_coding;
+       struct sk_buff *skb_dest, *skb_src;
+       struct batadv_unicast_packet *packet1;
+       struct batadv_unicast_packet *packet2;
+       struct batadv_coded_packet *coded_packet;
+       struct batadv_neigh_node *neigh_tmp, *router_neigh;
+       struct batadv_neigh_node *router_coding = NULL;
+       uint8_t *first_source, *first_dest, *second_source, *second_dest;
+       __be32 packet_id1, packet_id2;
+       size_t count;
+       bool res = false;
+       int coding_len;
+       int unicast_size = sizeof(*packet1);
+       int coded_size = sizeof(*coded_packet);
+       int header_add = coded_size - unicast_size;
+
+       router_neigh = batadv_orig_node_get_router(neigh_node->orig_node);
+       if (!router_neigh)
+               goto out;
+
+       neigh_tmp = nc_packet->neigh_node;
+       router_coding = batadv_orig_node_get_router(neigh_tmp->orig_node);
+       if (!router_coding)
+               goto out;
+
+       tq_weighted_neigh = batadv_nc_random_weight_tq(router_neigh->tq_avg);
+       tq_weighted_coding = batadv_nc_random_weight_tq(router_coding->tq_avg);
+
+       /* Select one destination for the MAC-header dst-field based on
+        * weighted TQ-values.
+        */
+       if (tq_weighted_neigh >= tq_weighted_coding) {
+               /* Destination from nc_packet is selected for MAC-header */
+               first_dest = nc_packet->nc_path->next_hop;
+               first_source = nc_packet->nc_path->prev_hop;
+               second_dest = neigh_node->addr;
+               second_source = ethhdr->h_source;
+               packet1 = (struct batadv_unicast_packet *)nc_packet->skb->data;
+               packet2 = (struct batadv_unicast_packet *)skb->data;
+               packet_id1 = nc_packet->packet_id;
+               packet_id2 = batadv_skb_crc32(skb,
+                                             skb->data + sizeof(*packet2));
+       } else {
+               /* Destination for skb is selected for MAC-header */
+               first_dest = neigh_node->addr;
+               first_source = ethhdr->h_source;
+               second_dest = nc_packet->nc_path->next_hop;
+               second_source = nc_packet->nc_path->prev_hop;
+               packet1 = (struct batadv_unicast_packet *)skb->data;
+               packet2 = (struct batadv_unicast_packet *)nc_packet->skb->data;
+               packet_id1 = batadv_skb_crc32(skb,
+                                             skb->data + sizeof(*packet1));
+               packet_id2 = nc_packet->packet_id;
+       }
+
+       /* Instead of zero padding the smallest data buffer, we
+        * code into the largest.
+        */
+       if (skb->len <= nc_packet->skb->len) {
+               skb_dest = nc_packet->skb;
+               skb_src = skb;
+       } else {
+               skb_dest = skb;
+               skb_src = nc_packet->skb;
+       }
+
+       /* coding_len is used when decoding the packet shorter packet */
+       coding_len = skb_src->len - unicast_size;
+
+       if (skb_linearize(skb_dest) < 0 || skb_linearize(skb_src) < 0)
+               goto out;
+
+       skb_push(skb_dest, header_add);
+
+       coded_packet = (struct batadv_coded_packet *)skb_dest->data;
+       skb_reset_mac_header(skb_dest);
+
+       coded_packet->header.packet_type = BATADV_CODED;
+       coded_packet->header.version = BATADV_COMPAT_VERSION;
+       coded_packet->header.ttl = packet1->header.ttl;
+
+       /* Info about first unicast packet */
+       memcpy(coded_packet->first_source, first_source, ETH_ALEN);
+       memcpy(coded_packet->first_orig_dest, packet1->dest, ETH_ALEN);
+       coded_packet->first_crc = packet_id1;
+       coded_packet->first_ttvn = packet1->ttvn;
+
+       /* Info about second unicast packet */
+       memcpy(coded_packet->second_dest, second_dest, ETH_ALEN);
+       memcpy(coded_packet->second_source, second_source, ETH_ALEN);
+       memcpy(coded_packet->second_orig_dest, packet2->dest, ETH_ALEN);
+       coded_packet->second_crc = packet_id2;
+       coded_packet->second_ttl = packet2->header.ttl;
+       coded_packet->second_ttvn = packet2->ttvn;
+       coded_packet->coded_len = htons(coding_len);
+
+       /* This is where the magic happens: Code skb_src into skb_dest */
+       batadv_nc_memxor(skb_dest->data + coded_size,
+                        skb_src->data + unicast_size, coding_len);
+
+       /* Update counters accordingly */
+       if (BATADV_SKB_CB(skb_src)->decoded &&
+           BATADV_SKB_CB(skb_dest)->decoded) {
+               /* Both packets are recoded */
+               count = skb_src->len + ETH_HLEN;
+               count += skb_dest->len + ETH_HLEN;
+               batadv_add_counter(bat_priv, BATADV_CNT_NC_RECODE, 2);
+               batadv_add_counter(bat_priv, BATADV_CNT_NC_RECODE_BYTES, count);
+       } else if (!BATADV_SKB_CB(skb_src)->decoded &&
+                  !BATADV_SKB_CB(skb_dest)->decoded) {
+               /* Both packets are newly coded */
+               count = skb_src->len + ETH_HLEN;
+               count += skb_dest->len + ETH_HLEN;
+               batadv_add_counter(bat_priv, BATADV_CNT_NC_CODE, 2);
+               batadv_add_counter(bat_priv, BATADV_CNT_NC_CODE_BYTES, count);
+       } else if (BATADV_SKB_CB(skb_src)->decoded &&
+                  !BATADV_SKB_CB(skb_dest)->decoded) {
+               /* skb_src recoded and skb_dest is newly coded */
+               batadv_inc_counter(bat_priv, BATADV_CNT_NC_RECODE);
+               batadv_add_counter(bat_priv, BATADV_CNT_NC_RECODE_BYTES,
+                                  skb_src->len + ETH_HLEN);
+               batadv_inc_counter(bat_priv, BATADV_CNT_NC_CODE);
+               batadv_add_counter(bat_priv, BATADV_CNT_NC_CODE_BYTES,
+                                  skb_dest->len + ETH_HLEN);
+       } else if (!BATADV_SKB_CB(skb_src)->decoded &&
+                  BATADV_SKB_CB(skb_dest)->decoded) {
+               /* skb_src is newly coded and skb_dest is recoded */
+               batadv_inc_counter(bat_priv, BATADV_CNT_NC_CODE);
+               batadv_add_counter(bat_priv, BATADV_CNT_NC_CODE_BYTES,
+                                  skb_src->len + ETH_HLEN);
+               batadv_inc_counter(bat_priv, BATADV_CNT_NC_RECODE);
+               batadv_add_counter(bat_priv, BATADV_CNT_NC_RECODE_BYTES,
+                                  skb_dest->len + ETH_HLEN);
+       }
+
+       /* skb_src is now coded into skb_dest, so free it */
+       kfree_skb(skb_src);
+
+       /* avoid duplicate free of skb from nc_packet */
+       nc_packet->skb = NULL;
+       batadv_nc_packet_free(nc_packet);
+
+       /* Send the coded packet and return true */
+       batadv_send_skb_packet(skb_dest, neigh_node->if_incoming, first_dest);
+       res = true;
+out:
+       if (router_neigh)
+               batadv_neigh_node_free_ref(router_neigh);
+       if (router_coding)
+               batadv_neigh_node_free_ref(router_coding);
+       return res;
+}
+
+/**
+ * batadv_nc_skb_coding_possible - true if a decoded skb is available at dst.
+ * @skb: data skb to forward
+ * @dst: destination mac address of the other skb to code with
+ * @src: source mac address of skb
+ *
+ * Whenever we network code a packet we have to check whether we received it in
+ * a network coded form. If so, we may not be able to use it for coding because
+ * some neighbors may also have received (overheard) the packet in the network
+ * coded form without being able to decode it. It is hard to know which of the
+ * neighboring nodes was able to decode the packet, therefore we can only
+ * re-code the packet if the source of the previous encoded packet is involved.
+ * Since the source encoded the packet we can be certain it has all necessary
+ * decode information.
+ *
+ * Returns true if coding of a decoded packet is allowed.
+ */
+static bool batadv_nc_skb_coding_possible(struct sk_buff *skb,
+                                         uint8_t *dst, uint8_t *src)
+{
+       if (BATADV_SKB_CB(skb)->decoded && !batadv_compare_eth(dst, src))
+               return false;
+       else
+               return true;
+}
+
+/**
+ * batadv_nc_path_search - Find the coding path matching in_nc_node and
+ *  out_nc_node to retrieve a buffered packet that can be used for coding.
+ * @bat_priv: the bat priv with all the soft interface information
+ * @in_nc_node: pointer to skb next hop's neighbor nc node
+ * @out_nc_node: pointer to skb source's neighbor nc node
+ * @skb: data skb to forward
+ * @eth_dst: next hop mac address of skb
+ *
+ * Returns true if coding of a decoded skb is allowed.
+ */
+static struct batadv_nc_packet *
+batadv_nc_path_search(struct batadv_priv *bat_priv,
+                     struct batadv_nc_node *in_nc_node,
+                     struct batadv_nc_node *out_nc_node,
+                     struct sk_buff *skb,
+                     uint8_t *eth_dst)
+{
+       struct batadv_nc_path *nc_path, nc_path_key;
+       struct batadv_nc_packet *nc_packet_out = NULL;
+       struct batadv_nc_packet *nc_packet, *nc_packet_tmp;
+       struct batadv_hashtable *hash = bat_priv->nc.coding_hash;
+       int idx;
+
+       if (!hash)
+               return NULL;
+
+       /* Create almost path key */
+       batadv_nc_hash_key_gen(&nc_path_key, in_nc_node->addr,
+                              out_nc_node->addr);
+       idx = batadv_nc_hash_choose(&nc_path_key, hash->size);
+
+       /* Check for coding opportunities in this nc_path */
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(nc_path, &hash->table[idx], hash_entry) {
+               if (!batadv_compare_eth(nc_path->prev_hop, in_nc_node->addr))
+                       continue;
+
+               if (!batadv_compare_eth(nc_path->next_hop, out_nc_node->addr))
+                       continue;
+
+               spin_lock_bh(&nc_path->packet_list_lock);
+               if (list_empty(&nc_path->packet_list)) {
+                       spin_unlock_bh(&nc_path->packet_list_lock);
+                       continue;
+               }
+
+               list_for_each_entry_safe(nc_packet, nc_packet_tmp,
+                                        &nc_path->packet_list, list) {
+                       if (!batadv_nc_skb_coding_possible(nc_packet->skb,
+                                                          eth_dst,
+                                                          in_nc_node->addr))
+                               continue;
+
+                       /* Coding opportunity is found! */
+                       list_del(&nc_packet->list);
+                       nc_packet_out = nc_packet;
+                       break;
+               }
+
+               spin_unlock_bh(&nc_path->packet_list_lock);
+               break;
+       }
+       rcu_read_unlock();
+
+       return nc_packet_out;
+}
+
+/**
+ * batadv_nc_skb_src_search - Loops through the list of neighoring nodes of the
+ *  skb's sender (may be equal to the originator).
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: data skb to forward
+ * @eth_dst: next hop mac address of skb
+ * @eth_src: source mac address of skb
+ * @in_nc_node: pointer to skb next hop's neighbor nc node
+ *
+ * Returns an nc packet if a suitable coding packet was found, NULL otherwise.
+ */
+static struct batadv_nc_packet *
+batadv_nc_skb_src_search(struct batadv_priv *bat_priv,
+                        struct sk_buff *skb,
+                        uint8_t *eth_dst,
+                        uint8_t *eth_src,
+                        struct batadv_nc_node *in_nc_node)
+{
+       struct batadv_orig_node *orig_node;
+       struct batadv_nc_node *out_nc_node;
+       struct batadv_nc_packet *nc_packet = NULL;
+
+       orig_node = batadv_orig_hash_find(bat_priv, eth_src);
+       if (!orig_node)
+               return NULL;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(out_nc_node,
+                               &orig_node->out_coding_list, list) {
+               /* Check if the skb is decoded and if recoding is possible */
+               if (!batadv_nc_skb_coding_possible(skb,
+                                                  out_nc_node->addr, eth_src))
+                       continue;
+
+               /* Search for an opportunity in this nc_path */
+               nc_packet = batadv_nc_path_search(bat_priv, in_nc_node,
+                                                 out_nc_node, skb, eth_dst);
+               if (nc_packet)
+                       break;
+       }
+       rcu_read_unlock();
+
+       batadv_orig_node_free_ref(orig_node);
+       return nc_packet;
+}
+
+/**
+ * batadv_nc_skb_store_before_coding - set the ethernet src and dst of the
+ *  unicast skb before it is stored for use in later decoding
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: data skb to store
+ * @eth_dst_new: new destination mac address of skb
+ */
+static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv,
+                                             struct sk_buff *skb,
+                                             uint8_t *eth_dst_new)
+{
+       struct ethhdr *ethhdr;
+
+       /* Copy skb header to change the mac header */
+       skb = pskb_copy(skb, GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       /* Set the mac header as if we actually sent the packet uncoded */
+       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       memcpy(ethhdr->h_source, ethhdr->h_dest, ETH_ALEN);
+       memcpy(ethhdr->h_dest, eth_dst_new, ETH_ALEN);
+
+       /* Set data pointer to MAC header to mimic packets from our tx path */
+       skb_push(skb, ETH_HLEN);
+
+       /* Add the packet to the decoding packet pool */
+       batadv_nc_skb_store_for_decoding(bat_priv, skb);
+
+       /* batadv_nc_skb_store_for_decoding() clones the skb, so we must free
+        * our ref
+        */
+       kfree_skb(skb);
+}
+
+/**
+ * batadv_nc_skb_dst_search - Loops through list of neighboring nodes to dst.
+ * @skb: data skb to forward
+ * @neigh_node: next hop to forward packet to
+ * @ethhdr: pointer to the ethernet header inside the skb
+ *
+ * Loops through list of neighboring nodes the next hop has a good connection to
+ * (receives OGMs with a sufficient quality). We need to find a neighbor of our
+ * next hop that potentially sent a packet which our next hop also received
+ * (overheard) and has stored for later decoding.
+ *
+ * Returns true if the skb was consumed (encoded packet sent) or false otherwise
+ */
+static bool batadv_nc_skb_dst_search(struct sk_buff *skb,
+                                    struct batadv_neigh_node *neigh_node,
+                                    struct ethhdr *ethhdr)
+{
+       struct net_device *netdev = neigh_node->if_incoming->soft_iface;
+       struct batadv_priv *bat_priv = netdev_priv(netdev);
+       struct batadv_orig_node *orig_node = neigh_node->orig_node;
+       struct batadv_nc_node *nc_node;
+       struct batadv_nc_packet *nc_packet = NULL;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(nc_node, &orig_node->in_coding_list, list) {
+               /* Search for coding opportunity with this in_nc_node */
+               nc_packet = batadv_nc_skb_src_search(bat_priv, skb,
+                                                    neigh_node->addr,
+                                                    ethhdr->h_source, nc_node);
+
+               /* Opportunity was found, so stop searching */
+               if (nc_packet)
+                       break;
+       }
+       rcu_read_unlock();
+
+       if (!nc_packet)
+               return false;
+
+       /* Save packets for later decoding */
+       batadv_nc_skb_store_before_coding(bat_priv, skb,
+                                         neigh_node->addr);
+       batadv_nc_skb_store_before_coding(bat_priv, nc_packet->skb,
+                                         nc_packet->neigh_node->addr);
+
+       /* Code and send packets */
+       if (batadv_nc_code_packets(bat_priv, skb, ethhdr, nc_packet,
+                                  neigh_node))
+               return true;
+
+       /* out of mem ? Coding failed - we have to free the buffered packet
+        * to avoid memleaks. The skb passed as argument will be dealt with
+        * by the calling function.
+        */
+       batadv_nc_send_packet(nc_packet);
+       return false;
+}
+
+/**
+ * batadv_nc_skb_add_to_path - buffer skb for later encoding / decoding
+ * @skb: skb to add to path
+ * @nc_path: path to add skb to
+ * @neigh_node: next hop to forward packet to
+ * @packet_id: checksum to identify packet
+ *
+ * Returns true if the packet was buffered or false in case of an error.
+ */
+static bool batadv_nc_skb_add_to_path(struct sk_buff *skb,
+                                     struct batadv_nc_path *nc_path,
+                                     struct batadv_neigh_node *neigh_node,
+                                     __be32 packet_id)
+{
+       struct batadv_nc_packet *nc_packet;
+
+       nc_packet = kzalloc(sizeof(*nc_packet), GFP_ATOMIC);
+       if (!nc_packet)
+               return false;
+
+       /* Initialize nc_packet */
+       nc_packet->timestamp = jiffies;
+       nc_packet->packet_id = packet_id;
+       nc_packet->skb = skb;
+       nc_packet->neigh_node = neigh_node;
+       nc_packet->nc_path = nc_path;
+
+       /* Add coding packet to list */
+       spin_lock_bh(&nc_path->packet_list_lock);
+       list_add_tail(&nc_packet->list, &nc_path->packet_list);
+       spin_unlock_bh(&nc_path->packet_list_lock);
+
+       return true;
+}
+
+/**
+ * batadv_nc_skb_forward - try to code a packet or add it to the coding packet
+ *  buffer
+ * @skb: data skb to forward
+ * @neigh_node: next hop to forward packet to
+ * @ethhdr: pointer to the ethernet header inside the skb
+ *
+ * Returns true if the skb was consumed (encoded packet sent) or false otherwise
+ */
+bool batadv_nc_skb_forward(struct sk_buff *skb,
+                          struct batadv_neigh_node *neigh_node,
+                          struct ethhdr *ethhdr)
+{
+       const struct net_device *netdev = neigh_node->if_incoming->soft_iface;
+       struct batadv_priv *bat_priv = netdev_priv(netdev);
+       struct batadv_unicast_packet *packet;
+       struct batadv_nc_path *nc_path;
+       __be32 packet_id;
+       u8 *payload;
+
+       /* Check if network coding is enabled */
+       if (!atomic_read(&bat_priv->network_coding))
+               goto out;
+
+       /* We only handle unicast packets */
+       payload = skb_network_header(skb);
+       packet = (struct batadv_unicast_packet *)payload;
+       if (packet->header.packet_type != BATADV_UNICAST)
+               goto out;
+
+       /* Try to find a coding opportunity and send the skb if one is found */
+       if (batadv_nc_skb_dst_search(skb, neigh_node, ethhdr))
+               return true;
+
+       /* Find or create a nc_path for this src-dst pair */
+       nc_path = batadv_nc_get_path(bat_priv,
+                                    bat_priv->nc.coding_hash,
+                                    ethhdr->h_source,
+                                    neigh_node->addr);
+
+       if (!nc_path)
+               goto out;
+
+       /* Add skb to nc_path */
+       packet_id = batadv_skb_crc32(skb, payload + sizeof(*packet));
+       if (!batadv_nc_skb_add_to_path(skb, nc_path, neigh_node, packet_id))
+               goto free_nc_path;
+
+       /* Packet is consumed */
+       return true;
+
+free_nc_path:
+       batadv_nc_path_free_ref(nc_path);
+out:
+       /* Packet is not consumed */
+       return false;
+}
+
+/**
+ * batadv_nc_skb_store_for_decoding - save a clone of the skb which can be used
+ *  when decoding coded packets
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: data skb to store
+ */
+void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
+                                     struct sk_buff *skb)
+{
+       struct batadv_unicast_packet *packet;
+       struct batadv_nc_path *nc_path;
+       struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       __be32 packet_id;
+       u8 *payload;
+
+       /* Check if network coding is enabled */
+       if (!atomic_read(&bat_priv->network_coding))
+               goto out;
+
+       /* Check for supported packet type */
+       payload = skb_network_header(skb);
+       packet = (struct batadv_unicast_packet *)payload;
+       if (packet->header.packet_type != BATADV_UNICAST)
+               goto out;
+
+       /* Find existing nc_path or create a new */
+       nc_path = batadv_nc_get_path(bat_priv,
+                                    bat_priv->nc.decoding_hash,
+                                    ethhdr->h_source,
+                                    ethhdr->h_dest);
+
+       if (!nc_path)
+               goto out;
+
+       /* Clone skb and adjust skb->data to point at batman header */
+       skb = skb_clone(skb, GFP_ATOMIC);
+       if (unlikely(!skb))
+               goto free_nc_path;
+
+       if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))
+               goto free_skb;
+
+       if (unlikely(!skb_pull_rcsum(skb, ETH_HLEN)))
+               goto free_skb;
+
+       /* Add skb to nc_path */
+       packet_id = batadv_skb_crc32(skb, payload + sizeof(*packet));
+       if (!batadv_nc_skb_add_to_path(skb, nc_path, NULL, packet_id))
+               goto free_skb;
+
+       batadv_inc_counter(bat_priv, BATADV_CNT_NC_BUFFER);
+       return;
+
+free_skb:
+       kfree_skb(skb);
+free_nc_path:
+       batadv_nc_path_free_ref(nc_path);
+out:
+       return;
+}
+
+/**
+ * batadv_nc_skb_store_sniffed_unicast - check if a received unicast packet
+ *  should be saved in the decoding buffer and, if so, store it there
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: unicast skb to store
+ */
+void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
+                                        struct sk_buff *skb)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
+
+       if (batadv_is_my_mac(bat_priv, ethhdr->h_dest))
+               return;
+
+       /* Set data pointer to MAC header to mimic packets from our tx path */
+       skb_push(skb, ETH_HLEN);
+
+       batadv_nc_skb_store_for_decoding(bat_priv, skb);
+}
+
+/**
+ * batadv_nc_skb_decode_packet - decode given skb using the decode data stored
+ *  in nc_packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: unicast skb to decode
+ * @nc_packet: decode data needed to decode the skb
+ *
+ * Returns pointer to decoded unicast packet if the packet was decoded or NULL
+ * in case of an error.
+ */
+static struct batadv_unicast_packet *
+batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                           struct batadv_nc_packet *nc_packet)
+{
+       const int h_size = sizeof(struct batadv_unicast_packet);
+       const int h_diff = sizeof(struct batadv_coded_packet) - h_size;
+       struct batadv_unicast_packet *unicast_packet;
+       struct batadv_coded_packet coded_packet_tmp;
+       struct ethhdr *ethhdr, ethhdr_tmp;
+       uint8_t *orig_dest, ttl, ttvn;
+       unsigned int coding_len;
+
+       /* Save headers temporarily */
+       memcpy(&coded_packet_tmp, skb->data, sizeof(coded_packet_tmp));
+       memcpy(&ethhdr_tmp, skb_mac_header(skb), sizeof(ethhdr_tmp));
+
+       if (skb_cow(skb, 0) < 0)
+               return NULL;
+
+       if (unlikely(!skb_pull_rcsum(skb, h_diff)))
+               return NULL;
+
+       /* Data points to batman header, so set mac header 14 bytes before
+        * and network to data
+        */
+       skb_set_mac_header(skb, -ETH_HLEN);
+       skb_reset_network_header(skb);
+
+       /* Reconstruct original mac header */
+       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       memcpy(ethhdr, &ethhdr_tmp, sizeof(*ethhdr));
+
+       /* Select the correct unicast header information based on the location
+        * of our mac address in the coded_packet header
+        */
+       if (batadv_is_my_mac(bat_priv, coded_packet_tmp.second_dest)) {
+               /* If we are the second destination the packet was overheard,
+                * so the Ethernet address must be copied to h_dest and
+                * pkt_type changed from PACKET_OTHERHOST to PACKET_HOST
+                */
+               memcpy(ethhdr->h_dest, coded_packet_tmp.second_dest, ETH_ALEN);
+               skb->pkt_type = PACKET_HOST;
+
+               orig_dest = coded_packet_tmp.second_orig_dest;
+               ttl = coded_packet_tmp.second_ttl;
+               ttvn = coded_packet_tmp.second_ttvn;
+       } else {
+               orig_dest = coded_packet_tmp.first_orig_dest;
+               ttl = coded_packet_tmp.header.ttl;
+               ttvn = coded_packet_tmp.first_ttvn;
+       }
+
+       coding_len = ntohs(coded_packet_tmp.coded_len);
+
+       if (coding_len > skb->len)
+               return NULL;
+
+       /* Here the magic is reversed:
+        *   extract the missing packet from the received coded packet
+        */
+       batadv_nc_memxor(skb->data + h_size,
+                        nc_packet->skb->data + h_size,
+                        coding_len);
+
+       /* Resize decoded skb if decoded with larger packet */
+       if (nc_packet->skb->len > coding_len + h_size)
+               pskb_trim_rcsum(skb, coding_len + h_size);
+
+       /* Create decoded unicast packet */
+       unicast_packet = (struct batadv_unicast_packet *)skb->data;
+       unicast_packet->header.packet_type = BATADV_UNICAST;
+       unicast_packet->header.version = BATADV_COMPAT_VERSION;
+       unicast_packet->header.ttl = ttl;
+       memcpy(unicast_packet->dest, orig_dest, ETH_ALEN);
+       unicast_packet->ttvn = ttvn;
+
+       batadv_nc_packet_free(nc_packet);
+       return unicast_packet;
+}
+
+/**
+ * batadv_nc_find_decoding_packet - search through buffered decoding data to
+ *  find the data needed to decode the coded packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ethhdr: pointer to the ethernet header inside the coded packet
+ * @coded: coded packet we try to find decode data for
+ *
+ * Returns pointer to nc packet if the needed data was found or NULL otherwise.
+ */
+static struct batadv_nc_packet *
+batadv_nc_find_decoding_packet(struct batadv_priv *bat_priv,
+                              struct ethhdr *ethhdr,
+                              struct batadv_coded_packet *coded)
+{
+       struct batadv_hashtable *hash = bat_priv->nc.decoding_hash;
+       struct batadv_nc_packet *tmp_nc_packet, *nc_packet = NULL;
+       struct batadv_nc_path *nc_path, nc_path_key;
+       uint8_t *dest, *source;
+       __be32 packet_id;
+       int index;
+
+       if (!hash)
+               return NULL;
+
+       /* Select the correct packet id based on the location of our mac-addr */
+       dest = ethhdr->h_source;
+       if (!batadv_is_my_mac(bat_priv, coded->second_dest)) {
+               source = coded->second_source;
+               packet_id = coded->second_crc;
+       } else {
+               source = coded->first_source;
+               packet_id = coded->first_crc;
+       }
+
+       batadv_nc_hash_key_gen(&nc_path_key, source, dest);
+       index = batadv_nc_hash_choose(&nc_path_key, hash->size);
+
+       /* Search for matching coding path */
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(nc_path, &hash->table[index], hash_entry) {
+               /* Find matching nc_packet */
+               spin_lock_bh(&nc_path->packet_list_lock);
+               list_for_each_entry(tmp_nc_packet,
+                                   &nc_path->packet_list, list) {
+                       if (packet_id == tmp_nc_packet->packet_id) {
+                               list_del(&tmp_nc_packet->list);
+
+                               nc_packet = tmp_nc_packet;
+                               break;
+                       }
+               }
+               spin_unlock_bh(&nc_path->packet_list_lock);
+
+               if (nc_packet)
+                       break;
+       }
+       rcu_read_unlock();
+
+       if (!nc_packet)
+               batadv_dbg(BATADV_DBG_NC, bat_priv,
+                          "No decoding packet found for %u\n", packet_id);
+
+       return nc_packet;
+}
+
+/**
+ * batadv_nc_recv_coded_packet - try to decode coded packet and enqueue the
+ *  resulting unicast packet
+ * @skb: incoming coded packet
+ * @recv_if: pointer to interface this packet was received on
+ */
+static int batadv_nc_recv_coded_packet(struct sk_buff *skb,
+                                      struct batadv_hard_iface *recv_if)
+{
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       struct batadv_unicast_packet *unicast_packet;
+       struct batadv_coded_packet *coded_packet;
+       struct batadv_nc_packet *nc_packet;
+       struct ethhdr *ethhdr;
+       int hdr_size = sizeof(*coded_packet);
+
+       /* Check if network coding is enabled */
+       if (!atomic_read(&bat_priv->network_coding))
+               return NET_RX_DROP;
+
+       /* Make sure we can access (and remove) header */
+       if (unlikely(!pskb_may_pull(skb, hdr_size)))
+               return NET_RX_DROP;
+
+       coded_packet = (struct batadv_coded_packet *)skb->data;
+       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+
+       /* Verify frame is destined for us */
+       if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest) &&
+           !batadv_is_my_mac(bat_priv, coded_packet->second_dest))
+               return NET_RX_DROP;
+
+       /* Update stat counter */
+       if (batadv_is_my_mac(bat_priv, coded_packet->second_dest))
+               batadv_inc_counter(bat_priv, BATADV_CNT_NC_SNIFFED);
+
+       nc_packet = batadv_nc_find_decoding_packet(bat_priv, ethhdr,
+                                                  coded_packet);
+       if (!nc_packet) {
+               batadv_inc_counter(bat_priv, BATADV_CNT_NC_DECODE_FAILED);
+               return NET_RX_DROP;
+       }
+
+       /* Make skb's linear, because decoding accesses the entire buffer */
+       if (skb_linearize(skb) < 0)
+               goto free_nc_packet;
+
+       if (skb_linearize(nc_packet->skb) < 0)
+               goto free_nc_packet;
+
+       /* Decode the packet */
+       unicast_packet = batadv_nc_skb_decode_packet(bat_priv, skb, nc_packet);
+       if (!unicast_packet) {
+               batadv_inc_counter(bat_priv, BATADV_CNT_NC_DECODE_FAILED);
+               goto free_nc_packet;
+       }
+
+       /* Mark packet as decoded to do correct recoding when forwarding */
+       BATADV_SKB_CB(skb)->decoded = true;
+       batadv_inc_counter(bat_priv, BATADV_CNT_NC_DECODE);
+       batadv_add_counter(bat_priv, BATADV_CNT_NC_DECODE_BYTES,
+                          skb->len + ETH_HLEN);
+       return batadv_recv_unicast_packet(skb, recv_if);
+
+free_nc_packet:
+       batadv_nc_packet_free(nc_packet);
+       return NET_RX_DROP;
+}
+
+/**
+ * batadv_nc_free - clean up network coding memory
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_nc_free(struct batadv_priv *bat_priv)
+{
+       batadv_recv_handler_unregister(BATADV_CODED);
+       cancel_delayed_work_sync(&bat_priv->nc.work);
+
+       batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL);
+       batadv_hash_destroy(bat_priv->nc.coding_hash);
+       batadv_nc_purge_paths(bat_priv, bat_priv->nc.decoding_hash, NULL);
+       batadv_hash_destroy(bat_priv->nc.decoding_hash);
+}
+
+/**
+ * batadv_nc_nodes_seq_print_text - print the nc node information
+ * @seq: seq file to print on
+ * @offset: not used
+ */
+int batadv_nc_nodes_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_hashtable *hash = bat_priv->orig_hash;
+       struct batadv_hard_iface *primary_if;
+       struct hlist_head *head;
+       struct batadv_orig_node *orig_node;
+       struct batadv_nc_node *nc_node;
+       int i;
+
+       primary_if = batadv_seq_print_text_primary_if_get(seq);
+       if (!primary_if)
+               goto out;
+
+       /* Traverse list of originators */
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               /* For each orig_node in this bin */
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
+                       seq_printf(seq, "Node:      %pM\n", orig_node->orig);
+
+                       seq_puts(seq, " Ingoing:  ");
+                       /* For each in_nc_node to this orig_node */
+                       list_for_each_entry_rcu(nc_node,
+                                               &orig_node->in_coding_list,
+                                               list)
+                               seq_printf(seq, "%pM ",
+                                          nc_node->addr);
+                       seq_puts(seq, "\n");
+
+                       seq_puts(seq, " Outgoing: ");
+                       /* For out_nc_node to this orig_node */
+                       list_for_each_entry_rcu(nc_node,
+                                               &orig_node->out_coding_list,
+                                               list)
+                               seq_printf(seq, "%pM ",
+                                          nc_node->addr);
+                       seq_puts(seq, "\n\n");
+               }
+               rcu_read_unlock();
+       }
+
+out:
+       if (primary_if)
+               batadv_hardif_free_ref(primary_if);
+       return 0;
+}
+
+/**
+ * batadv_nc_init_debugfs - create nc folder and related files in debugfs
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+int batadv_nc_init_debugfs(struct batadv_priv *bat_priv)
+{
+       struct dentry *nc_dir, *file;
+
+       nc_dir = debugfs_create_dir("nc", bat_priv->debug_dir);
+       if (!nc_dir)
+               goto out;
+
+       file = debugfs_create_u8("min_tq", S_IRUGO | S_IWUSR, nc_dir,
+                                &bat_priv->nc.min_tq);
+       if (!file)
+               goto out;
+
+       file = debugfs_create_u32("max_fwd_delay", S_IRUGO | S_IWUSR, nc_dir,
+                                 &bat_priv->nc.max_fwd_delay);
+       if (!file)
+               goto out;
+
+       file = debugfs_create_u32("max_buffer_time", S_IRUGO | S_IWUSR, nc_dir,
+                                 &bat_priv->nc.max_buffer_time);
+       if (!file)
+               goto out;
+
+       return 0;
+
+out:
+       return -ENOMEM;
+}
diff --git a/net/batman-adv/network-coding.h b/net/batman-adv/network-coding.h
new file mode 100644 (file)
index 0000000..4fa6d0c
--- /dev/null
@@ -0,0 +1,123 @@
+/* Copyright (C) 2012-2013 B.A.T.M.A.N. contributors:
+ *
+ * Martin Hundebøll, Jeppe Ledet-Pedersen
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _NET_BATMAN_ADV_NETWORK_CODING_H_
+#define _NET_BATMAN_ADV_NETWORK_CODING_H_
+
+#ifdef CONFIG_BATMAN_ADV_NC
+
+int batadv_nc_init(struct batadv_priv *bat_priv);
+void batadv_nc_free(struct batadv_priv *bat_priv);
+void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
+                             struct batadv_orig_node *orig_node,
+                             struct batadv_orig_node *orig_neigh_node,
+                             struct batadv_ogm_packet *ogm_packet,
+                             int is_single_hop_neigh);
+void batadv_nc_purge_orig(struct batadv_priv *bat_priv,
+                         struct batadv_orig_node *orig_node,
+                         bool (*to_purge)(struct batadv_priv *,
+                                          struct batadv_nc_node *));
+void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv);
+void batadv_nc_init_orig(struct batadv_orig_node *orig_node);
+bool batadv_nc_skb_forward(struct sk_buff *skb,
+                          struct batadv_neigh_node *neigh_node,
+                          struct ethhdr *ethhdr);
+void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
+                                     struct sk_buff *skb);
+void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
+                                        struct sk_buff *skb);
+int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset);
+int batadv_nc_init_debugfs(struct batadv_priv *bat_priv);
+
+#else /* ifdef CONFIG_BATMAN_ADV_NC */
+
+static inline int batadv_nc_init(struct batadv_priv *bat_priv)
+{
+       return 0;
+}
+
+static inline void batadv_nc_free(struct batadv_priv *bat_priv)
+{
+       return;
+}
+
+static inline void
+batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
+                        struct batadv_orig_node *orig_node,
+                        struct batadv_orig_node *orig_neigh_node,
+                        struct batadv_ogm_packet *ogm_packet,
+                        int is_single_hop_neigh)
+{
+       return;
+}
+
+static inline void
+batadv_nc_purge_orig(struct batadv_priv *bat_priv,
+                    struct batadv_orig_node *orig_node,
+                    bool (*to_purge)(struct batadv_priv *,
+                                     struct batadv_nc_node *))
+{
+       return;
+}
+
+static inline void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv)
+{
+       return;
+}
+
+static inline void batadv_nc_init_orig(struct batadv_orig_node *orig_node)
+{
+       return;
+}
+
+static inline bool batadv_nc_skb_forward(struct sk_buff *skb,
+                                        struct batadv_neigh_node *neigh_node,
+                                        struct ethhdr *ethhdr)
+{
+       return false;
+}
+
+static inline void
+batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
+                                struct sk_buff *skb)
+{
+       return;
+}
+
+static inline void
+batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
+                                   struct sk_buff *skb)
+{
+       return;
+}
+
+static inline int batadv_nc_nodes_seq_print_text(struct seq_file *seq,
+                                                void *offset)
+{
+       return 0;
+}
+
+static inline int batadv_nc_init_debugfs(struct batadv_priv *bat_priv)
+{
+       return 0;
+}
+
+#endif /* ifdef CONFIG_BATMAN_ADV_NC */
+
+#endif /* _NET_BATMAN_ADV_NETWORK_CODING_H_ */
index 96fb80b724dc28ec01701edcb2e85a21a2d3d5b5..2f3452546636ce71747ef9a8b1a949caae8d8f04 100644 (file)
@@ -28,6 +28,7 @@
 #include "unicast.h"
 #include "soft-interface.h"
 #include "bridge_loop_avoidance.h"
+#include "network-coding.h"
 
 /* hash class keys */
 static struct lock_class_key batadv_orig_hash_lock_class_key;
@@ -142,6 +143,9 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
 
        spin_unlock_bh(&orig_node->neigh_list_lock);
 
+       /* Free nc_nodes */
+       batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
+
        batadv_frag_list_free(&orig_node->frag_list);
        batadv_tt_global_del_orig(orig_node->bat_priv, orig_node,
                                  "originator timed out");
@@ -219,6 +223,8 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
        spin_lock_init(&orig_node->neigh_list_lock);
        spin_lock_init(&orig_node->tt_buff_lock);
 
+       batadv_nc_init_orig(orig_node);
+
        /* extra reference for return */
        atomic_set(&orig_node->refcount, 2);
 
@@ -459,7 +465,7 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
                                           neigh_node_tmp->tq_avg);
                        }
 
-                       seq_printf(seq, "\n");
+                       seq_puts(seq, "\n");
                        batman_count++;
 
 next:
@@ -469,7 +475,7 @@ next:
        }
 
        if (batman_count == 0)
-               seq_printf(seq, "No batman nodes in range ...\n");
+               seq_puts(seq, "No batman nodes in range ...\n");
 
 out:
        if (primary_if)
index ed0aa89bbf8b7bae0fdf70c5421598c1a04df2d2..a51ccfc39da4144fa2ccee27b0e0afca741034dd 100644 (file)
@@ -30,6 +30,7 @@ enum batadv_packettype {
        BATADV_TT_QUERY         = 0x07,
        BATADV_ROAM_ADV         = 0x08,
        BATADV_UNICAST_4ADDR    = 0x09,
+       BATADV_CODED            = 0x0a,
 };
 
 /**
@@ -278,4 +279,36 @@ struct batadv_tt_change {
        uint8_t addr[ETH_ALEN];
 } __packed;
 
+/**
+ * struct batadv_coded_packet - network coded packet
+ * @header: common batman packet header and ttl of first included packet
+ * @reserved: Align following fields to 2-byte boundaries
+ * @first_source: original source of first included packet
+ * @first_orig_dest: original destinal of first included packet
+ * @first_crc: checksum of first included packet
+ * @first_ttvn: tt-version number of first included packet
+ * @second_ttl: ttl of second packet
+ * @second_dest: second receiver of this coded packet
+ * @second_source: original source of second included packet
+ * @second_orig_dest: original destination of second included packet
+ * @second_crc: checksum of second included packet
+ * @second_ttvn: tt version number of second included packet
+ * @coded_len: length of network coded part of the payload
+ */
+struct batadv_coded_packet {
+       struct batadv_header header;
+       uint8_t  first_ttvn;
+       /* uint8_t  first_dest[ETH_ALEN]; - saved in mac header destination */
+       uint8_t  first_source[ETH_ALEN];
+       uint8_t  first_orig_dest[ETH_ALEN];
+       __be32   first_crc;
+       uint8_t  second_ttl;
+       uint8_t  second_ttvn;
+       uint8_t  second_dest[ETH_ALEN];
+       uint8_t  second_source[ETH_ALEN];
+       uint8_t  second_orig_dest[ETH_ALEN];
+       __be32   second_crc;
+       __be16   coded_len;
+};
+
 #endif /* _NET_BATMAN_ADV_PACKET_H_ */
index 319f2906c71a2146328c1e6ad1c4e9cbedee1e53..2f1f88923df8ca0208222b559bcffbf23870603d 100644 (file)
@@ -29,6 +29,7 @@
 #include "unicast.h"
 #include "bridge_loop_avoidance.h"
 #include "distributed-arp-table.h"
+#include "network-coding.h"
 
 static int batadv_route_unicast_packet(struct sk_buff *skb,
                                       struct batadv_hard_iface *recv_if);
@@ -548,6 +549,17 @@ batadv_find_ifalter_router(struct batadv_orig_node *primary_orig,
        return router;
 }
 
+/**
+ * batadv_check_unicast_packet - Check for malformed unicast packets
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: packet to check
+ * @hdr_size: size of header to pull
+ *
+ * Check for short header and bad addresses in given packet. Returns negative
+ * value when check fails and 0 otherwise. The negative value depends on the
+ * reason: -ENODATA for bad header, -EBADR for broadcast destination or source,
+ * and -EREMOTE for non-local (other host) destination.
+ */
 static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
                                       struct sk_buff *skb, int hdr_size)
 {
@@ -555,21 +567,21 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
 
        /* drop packet if it has not necessary minimum size */
        if (unlikely(!pskb_may_pull(skb, hdr_size)))
-               return -1;
+               return -ENODATA;
 
        ethhdr = (struct ethhdr *)skb_mac_header(skb);
 
        /* packet with unicast indication but broadcast recipient */
        if (is_broadcast_ether_addr(ethhdr->h_dest))
-               return -1;
+               return -EBADR;
 
        /* packet with broadcast sender address */
        if (is_broadcast_ether_addr(ethhdr->h_source))
-               return -1;
+               return -EBADR;
 
        /* not for me */
        if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest))
-               return -1;
+               return -EREMOTE;
 
        return 0;
 }
@@ -852,15 +864,18 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
        /* decrement ttl */
        unicast_packet->header.ttl--;
 
-       /* Update stats counter */
-       batadv_inc_counter(bat_priv, BATADV_CNT_FORWARD);
-       batadv_add_counter(bat_priv, BATADV_CNT_FORWARD_BYTES,
-                          skb->len + ETH_HLEN);
-
-       /* route it */
-       if (batadv_send_skb_to_orig(skb, orig_node, recv_if))
+       /* network code packet if possible */
+       if (batadv_nc_skb_forward(skb, neigh_node, ethhdr)) {
+               ret = NET_RX_SUCCESS;
+       } else if (batadv_send_skb_to_orig(skb, orig_node, recv_if)) {
                ret = NET_RX_SUCCESS;
 
+               /* Update stats counter */
+               batadv_inc_counter(bat_priv, BATADV_CNT_FORWARD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FORWARD_BYTES,
+                                  skb->len + ETH_HLEN);
+       }
+
 out:
        if (neigh_node)
                batadv_neigh_node_free_ref(neigh_node);
@@ -1035,7 +1050,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
        struct batadv_unicast_4addr_packet *unicast_4addr_packet;
        uint8_t *orig_addr;
        struct batadv_orig_node *orig_node = NULL;
-       int hdr_size = sizeof(*unicast_packet);
+       int check, hdr_size = sizeof(*unicast_packet);
        bool is4addr;
 
        unicast_packet = (struct batadv_unicast_packet *)skb->data;
@@ -1046,7 +1061,16 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
        if (is4addr)
                hdr_size = sizeof(*unicast_4addr_packet);
 
-       if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
+       /* function returns -EREMOTE for promiscuous packets */
+       check = batadv_check_unicast_packet(bat_priv, skb, hdr_size);
+
+       /* Even though the packet is not for us, we might save it to use for
+        * decoding a later received coded packet
+        */
+       if (check == -EREMOTE)
+               batadv_nc_skb_store_sniffed_unicast(bat_priv, skb);
+
+       if (check < 0)
                return NET_RX_DROP;
 
        if (!batadv_check_unicast_ttvn(bat_priv, skb))
index a67cffde37ae62c4bcc5ae2833bd0634e4daa3f0..263cfd1ccee78dfdf66c0f102d66301b8700a46d 100644 (file)
@@ -27,6 +27,7 @@
 #include "vis.h"
 #include "gateway_common.h"
 #include "originator.h"
+#include "network-coding.h"
 
 #include <linux/if_ether.h>
 
@@ -39,6 +40,7 @@ int batadv_send_skb_packet(struct sk_buff *skb,
                           struct batadv_hard_iface *hard_iface,
                           const uint8_t *dst_addr)
 {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
        struct ethhdr *ethhdr;
 
        if (hard_iface->if_status != BATADV_IF_ACTIVE)
@@ -70,6 +72,9 @@ int batadv_send_skb_packet(struct sk_buff *skb,
 
        skb->dev = hard_iface->net_dev;
 
+       /* Save a clone of the skb to use when decoding coded packets */
+       batadv_nc_skb_store_for_decoding(bat_priv, skb);
+
        /* dev_queue_xmit() returns a negative result on error.  However on
         * congestion and traffic shaping, it drops and returns NET_XMIT_DROP
         * (which is > 0). This will not be treated as an error.
index 2711e870f557d43ac6bbd4180a7f239a27fb850a..403b8c46085e40a667da281c8352781197f6539b 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/if_ether.h>
 #include "unicast.h"
 #include "bridge_loop_avoidance.h"
+#include "network-coding.h"
 
 
 static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
@@ -400,55 +401,6 @@ static void batadv_set_lockdep_class(struct net_device *dev)
        netdev_for_each_tx_queue(dev, batadv_set_lockdep_class_one, NULL);
 }
 
-/**
- * batadv_softif_init - Late stage initialization of soft interface
- * @dev: registered network device to modify
- *
- * Returns error code on failures
- */
-static int batadv_softif_init(struct net_device *dev)
-{
-       batadv_set_lockdep_class(dev);
-
-       return 0;
-}
-
-static const struct net_device_ops batadv_netdev_ops = {
-       .ndo_init = batadv_softif_init,
-       .ndo_open = batadv_interface_open,
-       .ndo_stop = batadv_interface_release,
-       .ndo_get_stats = batadv_interface_stats,
-       .ndo_set_mac_address = batadv_interface_set_mac_addr,
-       .ndo_change_mtu = batadv_interface_change_mtu,
-       .ndo_start_xmit = batadv_interface_tx,
-       .ndo_validate_addr = eth_validate_addr
-};
-
-static void batadv_interface_setup(struct net_device *dev)
-{
-       struct batadv_priv *priv = netdev_priv(dev);
-
-       ether_setup(dev);
-
-       dev->netdev_ops = &batadv_netdev_ops;
-       dev->destructor = free_netdev;
-       dev->tx_queue_len = 0;
-
-       /* can't call min_mtu, because the needed variables
-        * have not been initialized yet
-        */
-       dev->mtu = ETH_DATA_LEN;
-       /* reserve more space in the skbuff for our header */
-       dev->hard_header_len = BATADV_HEADER_LEN;
-
-       /* generate random address */
-       eth_hw_addr_random(dev);
-
-       SET_ETHTOOL_OPS(dev, &batadv_ethtool_ops);
-
-       memset(priv, 0, sizeof(*priv));
-}
-
 /**
  * batadv_softif_destroy_finish - cleans up the remains of a softif
  * @work: work queue item
@@ -465,7 +417,6 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
                                cleanup_work);
        soft_iface = bat_priv->soft_iface;
 
-       batadv_debugfs_del_meshif(soft_iface);
        batadv_sysfs_del_meshif(soft_iface);
 
        rtnl_lock();
@@ -473,21 +424,22 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
        rtnl_unlock();
 }
 
-struct net_device *batadv_softif_create(const char *name)
+/**
+ * batadv_softif_init_late - late stage initialization of soft interface
+ * @dev: registered network device to modify
+ *
+ * Returns error code on failures
+ */
+static int batadv_softif_init_late(struct net_device *dev)
 {
-       struct net_device *soft_iface;
        struct batadv_priv *bat_priv;
        int ret;
        size_t cnt_len = sizeof(uint64_t) * BATADV_CNT_NUM;
 
-       soft_iface = alloc_netdev(sizeof(*bat_priv), name,
-                                 batadv_interface_setup);
-
-       if (!soft_iface)
-               goto out;
+       batadv_set_lockdep_class(dev);
 
-       bat_priv = netdev_priv(soft_iface);
-       bat_priv->soft_iface = soft_iface;
+       bat_priv = netdev_priv(dev);
+       bat_priv->soft_iface = dev;
        INIT_WORK(&bat_priv->cleanup_work, batadv_softif_destroy_finish);
 
        /* batadv_interface_stats() needs to be available as soon as
@@ -495,14 +447,7 @@ struct net_device *batadv_softif_create(const char *name)
         */
        bat_priv->bat_counters = __alloc_percpu(cnt_len, __alignof__(uint64_t));
        if (!bat_priv->bat_counters)
-               goto free_soft_iface;
-
-       ret = register_netdevice(soft_iface);
-       if (ret < 0) {
-               pr_err("Unable to register the batman interface '%s': %i\n",
-                      name, ret);
-               goto free_bat_counters;
-       }
+               return -ENOMEM;
 
        atomic_set(&bat_priv->aggregated_ogms, 1);
        atomic_set(&bat_priv->bonding, 0);
@@ -540,49 +485,189 @@ struct net_device *batadv_softif_create(const char *name)
        bat_priv->primary_if = NULL;
        bat_priv->num_ifaces = 0;
 
-       ret = batadv_algo_select(bat_priv, batadv_routing_algo);
-       if (ret < 0)
-               goto unreg_soft_iface;
+       batadv_nc_init_bat_priv(bat_priv);
 
-       ret = batadv_sysfs_add_meshif(soft_iface);
+       ret = batadv_algo_select(bat_priv, batadv_routing_algo);
        if (ret < 0)
-               goto unreg_soft_iface;
+               goto free_bat_counters;
 
-       ret = batadv_debugfs_add_meshif(soft_iface);
+       ret = batadv_debugfs_add_meshif(dev);
        if (ret < 0)
-               goto unreg_sysfs;
+               goto free_bat_counters;
 
-       ret = batadv_mesh_init(soft_iface);
+       ret = batadv_mesh_init(dev);
        if (ret < 0)
                goto unreg_debugfs;
 
-       return soft_iface;
+       return 0;
 
 unreg_debugfs:
-       batadv_debugfs_del_meshif(soft_iface);
-unreg_sysfs:
-       batadv_sysfs_del_meshif(soft_iface);
-unreg_soft_iface:
-       free_percpu(bat_priv->bat_counters);
-       unregister_netdevice(soft_iface);
-       return NULL;
-
+       batadv_debugfs_del_meshif(dev);
 free_bat_counters:
        free_percpu(bat_priv->bat_counters);
-free_soft_iface:
-       free_netdev(soft_iface);
+
+       return ret;
+}
+
+/**
+ * batadv_softif_slave_add - Add a slave interface to a batadv_soft_interface
+ * @dev: batadv_soft_interface used as master interface
+ * @slave_dev: net_device which should become the slave interface
+ *
+ * Return 0 if successful or error otherwise.
+ */
+static int batadv_softif_slave_add(struct net_device *dev,
+                                  struct net_device *slave_dev)
+{
+       struct batadv_hard_iface *hard_iface;
+       int ret = -EINVAL;
+
+       hard_iface = batadv_hardif_get_by_netdev(slave_dev);
+       if (!hard_iface || hard_iface->soft_iface != NULL)
+               goto out;
+
+       ret = batadv_hardif_enable_interface(hard_iface, dev->name);
+
 out:
-       return NULL;
+       if (hard_iface)
+               batadv_hardif_free_ref(hard_iface);
+       return ret;
 }
 
-void batadv_softif_destroy(struct net_device *soft_iface)
+/**
+ * batadv_softif_slave_del - Delete a slave iface from a batadv_soft_interface
+ * @dev: batadv_soft_interface used as master interface
+ * @slave_dev: net_device which should be removed from the master interface
+ *
+ * Return 0 if successful or error otherwise.
+ */
+static int batadv_softif_slave_del(struct net_device *dev,
+                                  struct net_device *slave_dev)
+{
+       struct batadv_hard_iface *hard_iface;
+       int ret = -EINVAL;
+
+       hard_iface = batadv_hardif_get_by_netdev(slave_dev);
+
+       if (!hard_iface || hard_iface->soft_iface != dev)
+               goto out;
+
+       batadv_hardif_disable_interface(hard_iface, BATADV_IF_CLEANUP_KEEP);
+       ret = 0;
+
+out:
+       if (hard_iface)
+               batadv_hardif_free_ref(hard_iface);
+       return ret;
+}
+
+static const struct net_device_ops batadv_netdev_ops = {
+       .ndo_init = batadv_softif_init_late,
+       .ndo_open = batadv_interface_open,
+       .ndo_stop = batadv_interface_release,
+       .ndo_get_stats = batadv_interface_stats,
+       .ndo_set_mac_address = batadv_interface_set_mac_addr,
+       .ndo_change_mtu = batadv_interface_change_mtu,
+       .ndo_start_xmit = batadv_interface_tx,
+       .ndo_validate_addr = eth_validate_addr,
+       .ndo_add_slave = batadv_softif_slave_add,
+       .ndo_del_slave = batadv_softif_slave_del,
+};
+
+/**
+ * batadv_softif_free - Deconstructor of batadv_soft_interface
+ * @dev: Device to cleanup and remove
+ */
+static void batadv_softif_free(struct net_device *dev)
+{
+       batadv_debugfs_del_meshif(dev);
+       batadv_mesh_free(dev);
+       free_netdev(dev);
+}
+
+/**
+ * batadv_softif_init_early - early stage initialization of soft interface
+ * @dev: registered network device to modify
+ */
+static void batadv_softif_init_early(struct net_device *dev)
+{
+       struct batadv_priv *priv = netdev_priv(dev);
+
+       ether_setup(dev);
+
+       dev->netdev_ops = &batadv_netdev_ops;
+       dev->destructor = batadv_softif_free;
+       dev->tx_queue_len = 0;
+
+       /* can't call min_mtu, because the needed variables
+        * have not been initialized yet
+        */
+       dev->mtu = ETH_DATA_LEN;
+       /* reserve more space in the skbuff for our header */
+       dev->hard_header_len = BATADV_HEADER_LEN;
+
+       /* generate random address */
+       eth_hw_addr_random(dev);
+
+       SET_ETHTOOL_OPS(dev, &batadv_ethtool_ops);
+
+       memset(priv, 0, sizeof(*priv));
+}
+
+struct net_device *batadv_softif_create(const char *name)
+{
+       struct net_device *soft_iface;
+       int ret;
+
+       soft_iface = alloc_netdev(sizeof(struct batadv_priv), name,
+                                 batadv_softif_init_early);
+       if (!soft_iface)
+               return NULL;
+
+       soft_iface->rtnl_link_ops = &batadv_link_ops;
+
+       ret = register_netdevice(soft_iface);
+       if (ret < 0) {
+               pr_err("Unable to register the batman interface '%s': %i\n",
+                      name, ret);
+               free_netdev(soft_iface);
+               return NULL;
+       }
+
+       return soft_iface;
+}
+
+/**
+ * batadv_softif_destroy_sysfs - deletion of batadv_soft_interface via sysfs
+ * @soft_iface: the to-be-removed batman-adv interface
+ */
+void batadv_softif_destroy_sysfs(struct net_device *soft_iface)
 {
        struct batadv_priv *bat_priv = netdev_priv(soft_iface);
 
-       batadv_mesh_free(soft_iface);
        queue_work(batadv_event_workqueue, &bat_priv->cleanup_work);
 }
 
+/**
+ * batadv_softif_destroy_netlink - deletion of batadv_soft_interface via netlink
+ * @soft_iface: the to-be-removed batman-adv interface
+ * @head: list pointer
+ */
+static void batadv_softif_destroy_netlink(struct net_device *soft_iface,
+                                         struct list_head *head)
+{
+       struct batadv_hard_iface *hard_iface;
+
+       list_for_each_entry(hard_iface, &batadv_hardif_list, list) {
+               if (hard_iface->soft_iface == soft_iface)
+                       batadv_hardif_disable_interface(hard_iface,
+                                                       BATADV_IF_CLEANUP_KEEP);
+       }
+
+       batadv_sysfs_del_meshif(soft_iface);
+       unregister_netdevice_queue(soft_iface, head);
+}
+
 int batadv_softif_is_valid(const struct net_device *net_dev)
 {
        if (net_dev->netdev_ops->ndo_start_xmit == batadv_interface_tx)
@@ -591,6 +676,13 @@ int batadv_softif_is_valid(const struct net_device *net_dev)
        return 0;
 }
 
+struct rtnl_link_ops batadv_link_ops __read_mostly = {
+       .kind           = "batadv",
+       .priv_size      = sizeof(struct batadv_priv),
+       .setup          = batadv_softif_init_early,
+       .dellink        = batadv_softif_destroy_netlink,
+};
+
 /* ethtool */
 static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
@@ -662,6 +754,17 @@ static const struct {
        { "dat_put_rx" },
        { "dat_cached_reply_tx" },
 #endif
+#ifdef CONFIG_BATMAN_ADV_NC
+       { "nc_code" },
+       { "nc_code_bytes" },
+       { "nc_recode" },
+       { "nc_recode_bytes" },
+       { "nc_buffer" },
+       { "nc_decode" },
+       { "nc_decode_bytes" },
+       { "nc_decode_failed" },
+       { "nc_sniffed" },
+#endif
 };
 
 static void batadv_get_strings(struct net_device *dev, uint32_t stringset,
index 43182e5e603aafd4dead1fa516472c4bc4ee5dc4..2f2472c2ea0d91aae4b50314ee1171928c5024ee 100644 (file)
@@ -25,7 +25,8 @@ void batadv_interface_rx(struct net_device *soft_iface,
                         struct sk_buff *skb, struct batadv_hard_iface *recv_if,
                         int hdr_size, struct batadv_orig_node *orig_node);
 struct net_device *batadv_softif_create(const char *name);
-void batadv_softif_destroy(struct net_device *soft_iface);
+void batadv_softif_destroy_sysfs(struct net_device *soft_iface);
 int batadv_softif_is_valid(const struct net_device *net_dev);
+extern struct rtnl_link_ops batadv_link_ops;
 
 #endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */
index afbba319d73af8fb96fb80f14da86416a95a86ef..15a22efa9a679e016e8a435b8564c5460327f116 100644 (file)
@@ -442,6 +442,9 @@ static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
 #ifdef CONFIG_BATMAN_ADV_DEBUG
 BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL);
 #endif
+#ifdef CONFIG_BATMAN_ADV_NC
+BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, NULL);
+#endif
 
 static struct batadv_attribute *batadv_mesh_attrs[] = {
        &batadv_attr_aggregated_ogms,
@@ -463,6 +466,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
        &batadv_attr_gw_bandwidth,
 #ifdef CONFIG_BATMAN_ADV_DEBUG
        &batadv_attr_log_level,
+#endif
+#ifdef CONFIG_BATMAN_ADV_NC
+       &batadv_attr_network_coding,
 #endif
        NULL,
 };
@@ -582,13 +588,15 @@ static ssize_t batadv_store_mesh_iface(struct kobject *kobj,
        }
 
        if (status_tmp == BATADV_IF_NOT_IN_USE) {
-               batadv_hardif_disable_interface(hard_iface);
+               batadv_hardif_disable_interface(hard_iface,
+                                               BATADV_IF_CLEANUP_AUTO);
                goto unlock;
        }
 
        /* if the interface already is in use */
        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
-               batadv_hardif_disable_interface(hard_iface);
+               batadv_hardif_disable_interface(hard_iface,
+                                               BATADV_IF_CLEANUP_AUTO);
 
        ret = batadv_hardif_enable_interface(hard_iface, buff);
 
@@ -688,15 +696,10 @@ int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
                        enum batadv_uev_action action, const char *data)
 {
        int ret = -ENOMEM;
-       struct batadv_hard_iface *primary_if;
        struct kobject *bat_kobj;
        char *uevent_env[4] = { NULL, NULL, NULL, NULL };
 
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
-
-       bat_kobj = &primary_if->soft_iface->dev.kobj;
+       bat_kobj = &bat_priv->soft_iface->dev.kobj;
 
        uevent_env[0] = kmalloc(strlen(BATADV_UEV_TYPE_VAR) +
                                strlen(batadv_uev_type_str[type]) + 1,
@@ -732,9 +735,6 @@ out:
        kfree(uevent_env[1]);
        kfree(uevent_env[2]);
 
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
-
        if (ret)
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                           "Impossible to send uevent for (%s,%s,%s) event (err: %d)\n",
index 7abee19567e9e0f84ecb224e666f2e6c1094333c..5e89deeb9542979c72ba635fb1c18d301a9c1621 100644 (file)
@@ -385,25 +385,19 @@ static void batadv_tt_prepare_packet_buff(struct batadv_priv *bat_priv,
                                          int *packet_buff_len,
                                          int min_packet_len)
 {
-       struct batadv_hard_iface *primary_if;
        int req_len;
 
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-
        req_len = min_packet_len;
        req_len += batadv_tt_len(atomic_read(&bat_priv->tt.local_changes));
 
        /* if we have too many changes for one packet don't send any
         * and wait for the tt table request which will be fragmented
         */
-       if ((!primary_if) || (req_len > primary_if->soft_iface->mtu))
+       if (req_len > bat_priv->soft_iface->mtu)
                req_len = min_packet_len;
 
        batadv_tt_realloc_packet_buff(packet_buff, packet_buff_len,
                                      min_packet_len, req_len);
-
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
 }
 
 static int batadv_tt_changes_fill_buff(struct batadv_priv *bat_priv,
@@ -908,7 +902,7 @@ out_remove:
        /* remove address from local hash if present */
        local_flags = batadv_tt_local_remove(bat_priv, tt_addr,
                                             "global tt received",
-                                            !!(flags & BATADV_TT_CLIENT_ROAM));
+                                            flags & BATADV_TT_CLIENT_ROAM);
        tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI;
 
        if (!(flags & BATADV_TT_CLIENT_ROAM))
@@ -1580,7 +1574,7 @@ static int batadv_tt_global_valid(const void *entry_ptr,
 static struct sk_buff *
 batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
                              struct batadv_hashtable *hash,
-                             struct batadv_hard_iface *primary_if,
+                             struct batadv_priv *bat_priv,
                              int (*valid_cb)(const void *, const void *),
                              void *cb_data)
 {
@@ -1594,8 +1588,8 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
        uint32_t i;
        size_t len;
 
-       if (tt_query_size + tt_len > primary_if->soft_iface->mtu) {
-               tt_len = primary_if->soft_iface->mtu - tt_query_size;
+       if (tt_query_size + tt_len > bat_priv->soft_iface->mtu) {
+               tt_len = bat_priv->soft_iface->mtu - tt_query_size;
                tt_len -= tt_len % sizeof(struct batadv_tt_change);
        }
        tt_tot = tt_len / sizeof(struct batadv_tt_change);
@@ -1715,7 +1709,6 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
 {
        struct batadv_orig_node *req_dst_orig_node;
        struct batadv_orig_node *res_dst_orig_node = NULL;
-       struct batadv_hard_iface *primary_if = NULL;
        uint8_t orig_ttvn, req_ttvn, ttvn;
        int ret = false;
        unsigned char *tt_buff;
@@ -1740,10 +1733,6 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
        if (!res_dst_orig_node)
                goto out;
 
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
-
        orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
        req_ttvn = tt_request->ttvn;
 
@@ -1791,7 +1780,7 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
 
                skb = batadv_tt_response_fill_table(tt_len, ttvn,
                                                    bat_priv->tt.global_hash,
-                                                   primary_if,
+                                                   bat_priv,
                                                    batadv_tt_global_valid,
                                                    req_dst_orig_node);
                if (!skb)
@@ -1828,8 +1817,6 @@ out:
                batadv_orig_node_free_ref(res_dst_orig_node);
        if (req_dst_orig_node)
                batadv_orig_node_free_ref(req_dst_orig_node);
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
        if (!ret)
                kfree_skb(skb);
        return ret;
@@ -1907,7 +1894,7 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv,
 
                skb = batadv_tt_response_fill_table(tt_len, ttvn,
                                                    bat_priv->tt.local_hash,
-                                                   primary_if,
+                                                   bat_priv,
                                                    batadv_tt_local_valid_entry,
                                                    NULL);
                if (!skb)
@@ -2528,7 +2515,7 @@ bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
        if (!tt_global_entry)
                goto out;
 
-       ret = !!(tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM);
+       ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM;
        batadv_tt_global_entry_free_ref(tt_global_entry);
 out:
        return ret;
index 4cd87a0b5b8037d12c9af6ed1d78d515b598af0f..aba8364c368991935ab5f9899266a6662f925618 100644 (file)
@@ -128,6 +128,10 @@ struct batadv_hard_iface {
  * @bond_list: list of bonding candidates
  * @refcount: number of contexts the object is used
  * @rcu: struct used for freeing in an RCU-safe manner
+ * @in_coding_list: list of nodes this orig can hear
+ * @out_coding_list: list of nodes that can hear this orig
+ * @in_coding_list_lock: protects in_coding_list
+ * @out_coding_list_lock: protects out_coding_list
  */
 struct batadv_orig_node {
        uint8_t orig[ETH_ALEN];
@@ -171,6 +175,12 @@ struct batadv_orig_node {
        struct list_head bond_list;
        atomic_t refcount;
        struct rcu_head rcu;
+#ifdef CONFIG_BATMAN_ADV_NC
+       struct list_head in_coding_list;
+       struct list_head out_coding_list;
+       spinlock_t in_coding_list_lock; /* Protects in_coding_list */
+       spinlock_t out_coding_list_lock; /* Protects out_coding_list */
+#endif
 };
 
 /**
@@ -265,6 +275,17 @@ struct batadv_bcast_duplist_entry {
  * @BATADV_CNT_DAT_PUT_RX: received dht PUT traffic packet counter
  * @BATADV_CNT_DAT_CACHED_REPLY_TX: transmitted dat cache reply traffic packet
  *  counter
+ * @BATADV_CNT_NC_CODE: transmitted nc-combined traffic packet counter
+ * @BATADV_CNT_NC_CODE_BYTES: transmitted nc-combined traffic bytes counter
+ * @BATADV_CNT_NC_RECODE: transmitted nc-recombined traffic packet counter
+ * @BATADV_CNT_NC_RECODE_BYTES: transmitted nc-recombined traffic bytes counter
+ * @BATADV_CNT_NC_BUFFER: counter for packets buffered for later nc decoding
+ * @BATADV_CNT_NC_DECODE: received and nc-decoded traffic packet counter
+ * @BATADV_CNT_NC_DECODE_BYTES: received and nc-decoded traffic bytes counter
+ * @BATADV_CNT_NC_DECODE_FAILED: received and decode-failed traffic packet
+ *  counter
+ * @BATADV_CNT_NC_SNIFFED: counter for nc-decoded packets received in promisc
+ *  mode.
  * @BATADV_CNT_NUM: number of traffic counters
  */
 enum batadv_counters {
@@ -291,6 +312,17 @@ enum batadv_counters {
        BATADV_CNT_DAT_PUT_TX,
        BATADV_CNT_DAT_PUT_RX,
        BATADV_CNT_DAT_CACHED_REPLY_TX,
+#endif
+#ifdef CONFIG_BATMAN_ADV_NC
+       BATADV_CNT_NC_CODE,
+       BATADV_CNT_NC_CODE_BYTES,
+       BATADV_CNT_NC_RECODE,
+       BATADV_CNT_NC_RECODE_BYTES,
+       BATADV_CNT_NC_BUFFER,
+       BATADV_CNT_NC_DECODE,
+       BATADV_CNT_NC_DECODE_BYTES,
+       BATADV_CNT_NC_DECODE_FAILED,
+       BATADV_CNT_NC_SNIFFED,
 #endif
        BATADV_CNT_NUM,
 };
@@ -427,6 +459,35 @@ struct batadv_priv_dat {
 };
 #endif
 
+/**
+ * struct batadv_priv_nc - per mesh interface network coding private data
+ * @work: work queue callback item for cleanup
+ * @debug_dir: dentry for nc subdir in batman-adv directory in debugfs
+ * @min_tq: only consider neighbors for encoding if neigh_tq > min_tq
+ * @max_fwd_delay: maximum packet forward delay to allow coding of packets
+ * @max_buffer_time: buffer time for sniffed packets used to decoding
+ * @timestamp_fwd_flush: timestamp of last forward packet queue flush
+ * @timestamp_sniffed_purge: timestamp of last sniffed packet queue purge
+ * @coding_hash: Hash table used to buffer skbs while waiting for another
+ *  incoming skb to code it with. Skbs are added to the buffer just before being
+ *  forwarded in routing.c
+ * @decoding_hash: Hash table used to buffer skbs that might be needed to decode
+ *  a received coded skb. The buffer is used for 1) skbs arriving on the
+ *  soft-interface; 2) skbs overheard on the hard-interface; and 3) skbs
+ *  forwarded by batman-adv.
+ */
+struct batadv_priv_nc {
+       struct delayed_work work;
+       struct dentry *debug_dir;
+       u8 min_tq;
+       u32 max_fwd_delay;
+       u32 max_buffer_time;
+       unsigned long timestamp_fwd_flush;
+       unsigned long timestamp_sniffed_purge;
+       struct batadv_hashtable *coding_hash;
+       struct batadv_hashtable *decoding_hash;
+};
+
 /**
  * struct batadv_priv - per mesh interface data
  * @mesh_state: current status of the mesh (inactive/active/deactivating)
@@ -470,6 +531,8 @@ struct batadv_priv_dat {
  * @tt: translation table data
  * @vis: vis data
  * @dat: distributed arp table data
+ * @network_coding: bool indicating whether network coding is enabled
+ * @batadv_priv_nc: network coding data
  */
 struct batadv_priv {
        atomic_t mesh_state;
@@ -522,6 +585,10 @@ struct batadv_priv {
 #ifdef CONFIG_BATMAN_ADV_DAT
        struct batadv_priv_dat dat;
 #endif
+#ifdef CONFIG_BATMAN_ADV_NC
+       atomic_t network_coding;
+       struct batadv_priv_nc nc;
+#endif /* CONFIG_BATMAN_ADV_NC */
 };
 
 /**
@@ -701,6 +768,75 @@ struct batadv_tt_roam_node {
        struct list_head list;
 };
 
+/**
+ * struct batadv_nc_node - network coding node
+ * @list: next and prev pointer for the list handling
+ * @addr: the node's mac address
+ * @refcount: number of contexts the object is used by
+ * @rcu: struct used for freeing in an RCU-safe manner
+ * @orig_node: pointer to corresponding orig node struct
+ * @last_seen: timestamp of last ogm received from this node
+ */
+struct batadv_nc_node {
+       struct list_head list;
+       uint8_t addr[ETH_ALEN];
+       atomic_t refcount;
+       struct rcu_head rcu;
+       struct batadv_orig_node *orig_node;
+       unsigned long last_seen;
+};
+
+/**
+ * struct batadv_nc_path - network coding path
+ * @hash_entry: next and prev pointer for the list handling
+ * @rcu: struct used for freeing in an RCU-safe manner
+ * @refcount: number of contexts the object is used by
+ * @packet_list: list of buffered packets for this path
+ * @packet_list_lock: access lock for packet list
+ * @next_hop: next hop (destination) of path
+ * @prev_hop: previous hop (source) of path
+ * @last_valid: timestamp for last validation of path
+ */
+struct batadv_nc_path {
+       struct hlist_node hash_entry;
+       struct rcu_head rcu;
+       atomic_t refcount;
+       struct list_head packet_list;
+       spinlock_t packet_list_lock; /* Protects packet_list */
+       uint8_t next_hop[ETH_ALEN];
+       uint8_t prev_hop[ETH_ALEN];
+       unsigned long last_valid;
+};
+
+/**
+ * struct batadv_nc_packet - network coding packet used when coding and
+ *  decoding packets
+ * @list: next and prev pointer for the list handling
+ * @packet_id: crc32 checksum of skb data
+ * @timestamp: field containing the info when the packet was added to path
+ * @neigh_node: pointer to original next hop neighbor of skb
+ * @skb: skb which can be encoded or used for decoding
+ * @nc_path: pointer to path this nc packet is attached to
+ */
+struct batadv_nc_packet {
+       struct list_head list;
+       __be32 packet_id;
+       unsigned long timestamp;
+       struct batadv_neigh_node *neigh_node;
+       struct sk_buff *skb;
+       struct batadv_nc_path *nc_path;
+};
+
+/**
+ * batadv_skb_cb - control buffer structure used to store private data relevant
+ *  to batman-adv in the skb->cb buffer in skbs.
+ * @decoded: Marks a skb as decoded, which is checked when searching for coding
+ *  opportunities in network-coding.c
+ */
+struct batadv_skb_cb {
+       bool decoded;
+};
+
 /**
  * struct batadv_forw_packet - structure for bcast packets to be sent/forwarded
  * @list: list node for batadv_socket_client::queue_list
index 50e079f00be62aeb7530a88d70edc42215dff743..0bb3b5982f94c9480741ad224594e7b0a9143e0f 100644 (file)
@@ -122,7 +122,7 @@ batadv_frag_search_packet(struct list_head *head,
 {
        struct batadv_frag_packet_list_entry *tfp;
        struct batadv_unicast_frag_packet *tmp_up = NULL;
-       int is_head_tmp, is_head;
+       bool is_head_tmp, is_head;
        uint16_t search_seqno;
 
        if (up->flags & BATADV_UNI_FRAG_HEAD)
@@ -130,7 +130,7 @@ batadv_frag_search_packet(struct list_head *head,
        else
                search_seqno = ntohs(up->seqno)-1;
 
-       is_head = !!(up->flags & BATADV_UNI_FRAG_HEAD);
+       is_head = up->flags & BATADV_UNI_FRAG_HEAD;
 
        list_for_each_entry(tfp, head, list) {
                if (!tfp->skb)
@@ -142,7 +142,7 @@ batadv_frag_search_packet(struct list_head *head,
                tmp_up = (struct batadv_unicast_frag_packet *)tfp->skb->data;
 
                if (tfp->seqno == search_seqno) {
-                       is_head_tmp = !!(tmp_up->flags & BATADV_UNI_FRAG_HEAD);
+                       is_head_tmp = tmp_up->flags & BATADV_UNI_FRAG_HEAD;
                        if (is_head_tmp != is_head)
                                return tfp;
                        else
index 6a1e646be96d45b21e21127a056a9aad3af91bb1..1625e5793a895d02aec97966f901ccfb8a29e4ab 100644 (file)
@@ -149,7 +149,7 @@ static void batadv_vis_data_read_prim_sec(struct seq_file *seq,
 
        hlist_for_each_entry(entry, if_list, list) {
                if (entry->primary)
-                       seq_printf(seq, "PRIMARY, ");
+                       seq_puts(seq, "PRIMARY, ");
                else
                        seq_printf(seq,  "SEC %pM, ", entry->addr);
        }
@@ -207,7 +207,7 @@ static void batadv_vis_data_read_entries(struct seq_file *seq,
                if (batadv_compare_eth(entry->addr, packet->vis_orig))
                        batadv_vis_data_read_prim_sec(seq, list);
 
-               seq_printf(seq, "\n");
+               seq_puts(seq, "\n");
        }
 }
 
index eb0f4b16ff099611fb6ea0aa43add29bd1c1173f..17f33a62f6db559824d9dc1b3973acb779fa9162 100644 (file)
@@ -397,13 +397,12 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
        if (ctrl) {
                u8 *assoc;
 
-               assoc = kzalloc(assoc_len, GFP_KERNEL);
+               assoc = kmemdup(rsp->amp_assoc, assoc_len, GFP_KERNEL);
                if (!assoc) {
                        amp_ctrl_put(ctrl);
                        return -ENOMEM;
                }
 
-               memcpy(assoc, rsp->amp_assoc, assoc_len);
                ctrl->assoc = assoc;
                ctrl->assoc_len = assoc_len;
                ctrl->assoc_rem_len = assoc_len;
@@ -472,13 +471,12 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
                size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req);
                u8 *assoc;
 
-               assoc = kzalloc(assoc_len, GFP_KERNEL);
+               assoc = kmemdup(req->amp_assoc, assoc_len, GFP_KERNEL);
                if (!assoc) {
                        amp_ctrl_put(ctrl);
                        return -ENOMEM;
                }
 
-               memcpy(assoc, req->amp_assoc, assoc_len);
                ctrl->assoc = assoc;
                ctrl->assoc_len = assoc_len;
                ctrl->assoc_rem_len = assoc_len;
index 0d1b08cc76e1d73acba966e4c22f64c0285cce05..e5338f787d68e18c6b0db191094eeed61425e21d 100644 (file)
@@ -92,23 +92,14 @@ int bt_sock_register(int proto, const struct net_proto_family *ops)
 }
 EXPORT_SYMBOL(bt_sock_register);
 
-int bt_sock_unregister(int proto)
+void bt_sock_unregister(int proto)
 {
-       int err = 0;
-
        if (proto < 0 || proto >= BT_MAX_PROTO)
-               return -EINVAL;
+               return;
 
        write_lock(&bt_proto_lock);
-
-       if (!bt_proto[proto])
-               err = -ENOENT;
-       else
-               bt_proto[proto] = NULL;
-
+       bt_proto[proto] = NULL;
        write_unlock(&bt_proto_lock);
-
-       return err;
 }
 EXPORT_SYMBOL(bt_sock_unregister);
 
@@ -422,7 +413,8 @@ unsigned int bt_sock_poll(struct file *file, struct socket *sock,
                return bt_accept_poll(sk);
 
        if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
-               mask |= POLLERR;
+               mask |= POLLERR |
+                       (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 
        if (sk->sk_shutdown & RCV_SHUTDOWN)
                mask |= POLLRDHUP | POLLIN | POLLRDNORM;
index e58c8b32589c44811c042a623c48e1ca1d8550a1..4b488ec261054830c6f45bfe3a6a3a9cfd65f316 100644 (file)
@@ -136,7 +136,7 @@ static u16 bnep_net_eth_proto(struct sk_buff *skb)
        struct ethhdr *eh = (void *) skb->data;
        u16 proto = ntohs(eh->h_proto);
 
-       if (proto >= 1536)
+       if (proto >= ETH_P_802_3_MIN)
                return proto;
 
        if (get_unaligned((__be16 *) skb->data) == htons(0xFFFF))
index e7154a58465f6b9136f527ab868024464d961564..5b1c04e2882188c73031415144a8130995b27a3d 100644 (file)
@@ -253,8 +253,6 @@ error:
 void __exit bnep_sock_cleanup(void)
 {
        bt_procfs_cleanup(&init_net, "bnep");
-       if (bt_sock_unregister(BTPROTO_BNEP) < 0)
-               BT_ERR("Can't unregister BNEP socket");
-
+       bt_sock_unregister(BTPROTO_BNEP);
        proto_unregister(&bnep_proto);
 }
index 1c57482112b6e5c1eebfa30ce1ac84375db737dd..58d9edebab4b02d6cfa242d5f450fc6a2a4060cb 100644 (file)
@@ -264,8 +264,6 @@ error:
 void cmtp_cleanup_sockets(void)
 {
        bt_procfs_cleanup(&init_net, "cmtp");
-       if (bt_sock_unregister(BTPROTO_CMTP) < 0)
-               BT_ERR("Can't unregister CMTP socket");
-
+       bt_sock_unregister(BTPROTO_CMTP);
        proto_unregister(&cmtp_proto);
 }
index 4925a02ae7e4db7df4d4cdfc972e68375743861c..b9f90169940be5b32200c25598aaafabd83a184f 100644 (file)
@@ -117,7 +117,7 @@ static void hci_acl_create_connection_cancel(struct hci_conn *conn)
        hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp);
 }
 
-void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
+void hci_disconnect(struct hci_conn *conn, __u8 reason)
 {
        struct hci_cp_disconnect cp;
 
@@ -253,7 +253,7 @@ static void hci_conn_disconnect(struct hci_conn *conn)
                hci_amp_disconn(conn, reason);
                break;
        default:
-               hci_acl_disconn(conn, reason);
+               hci_disconnect(conn, reason);
                break;
        }
 }
index 60793e7b768bb702321c2a2dbdefab089bfd555b..cfcad5423f1cdba4009fde9050f38cc67ee90774 100644 (file)
@@ -57,36 +57,9 @@ static void hci_notify(struct hci_dev *hdev, int event)
 
 /* ---- HCI requests ---- */
 
-void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result)
+static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
 {
-       BT_DBG("%s command 0x%4.4x result 0x%2.2x", hdev->name, cmd, result);
-
-       /* If this is the init phase check if the completed command matches
-        * the last init command, and if not just return.
-        */
-       if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) {
-               struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data;
-               u16 opcode = __le16_to_cpu(sent->opcode);
-               struct sk_buff *skb;
-
-               /* Some CSR based controllers generate a spontaneous
-                * reset complete event during init and any pending
-                * command will never be completed. In such a case we
-                * need to resend whatever was the last sent
-                * command.
-                */
-
-               if (cmd != HCI_OP_RESET || opcode == HCI_OP_RESET)
-                       return;
-
-               skb = skb_clone(hdev->sent_cmd, GFP_ATOMIC);
-               if (skb) {
-                       skb_queue_head(&hdev->cmd_q, skb);
-                       queue_work(hdev->workqueue, &hdev->cmd_work);
-               }
-
-               return;
-       }
+       BT_DBG("%s result 0x%2.2x", hdev->name, result);
 
        if (hdev->req_status == HCI_REQ_PEND) {
                hdev->req_result = result;
@@ -107,21 +80,41 @@ static void hci_req_cancel(struct hci_dev *hdev, int err)
 }
 
 /* Execute request and wait for completion. */
-static int __hci_request(struct hci_dev *hdev,
-                        void (*req)(struct hci_dev *hdev, unsigned long opt),
-                        unsigned long opt, __u32 timeout)
+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);
+
+       err = hci_req_run(&req, hci_req_sync_complete);
+       if (err < 0) {
+               hdev->req_status = 0;
+
+               /* 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;
+       }
+
        add_wait_queue(&hdev->req_wait_q, &wait);
        set_current_state(TASK_INTERRUPTIBLE);
 
-       req(hdev, opt);
        schedule_timeout(timeout);
 
        remove_wait_queue(&hdev->req_wait_q, &wait);
@@ -150,9 +143,10 @@ static int __hci_request(struct hci_dev *hdev,
        return err;
 }
 
-static int hci_request(struct hci_dev *hdev,
-                      void (*req)(struct hci_dev *hdev, unsigned long opt),
-                      unsigned long opt, __u32 timeout)
+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;
 
@@ -161,75 +155,86 @@ static int hci_request(struct hci_dev *hdev,
 
        /* Serialize all requests */
        hci_req_lock(hdev);
-       ret = __hci_request(hdev, req, opt, timeout);
+       ret = __hci_req_sync(hdev, req, opt, timeout);
        hci_req_unlock(hdev);
 
        return ret;
 }
 
-static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
+static void hci_reset_req(struct hci_request *req, unsigned long opt)
 {
-       BT_DBG("%s %ld", hdev->name, opt);
+       BT_DBG("%s %ld", req->hdev->name, opt);
 
        /* Reset device */
-       set_bit(HCI_RESET, &hdev->flags);
-       hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
+       set_bit(HCI_RESET, &req->hdev->flags);
+       hci_req_add(req, HCI_OP_RESET, 0, NULL);
 }
 
-static void bredr_init(struct hci_dev *hdev)
+static void bredr_init(struct hci_request *req)
 {
-       hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED;
+       req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED;
 
        /* Read Local Supported Features */
-       hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+       hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
 
        /* Read Local Version */
-       hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
+       hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
+
+       /* Read BD Address */
+       hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL);
 }
 
-static void amp_init(struct hci_dev *hdev)
+static void amp_init(struct hci_request *req)
 {
-       hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED;
+       req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED;
 
        /* Read Local Version */
-       hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
+       hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
 
        /* Read Local AMP Info */
-       hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
+       hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
 
        /* Read Data Blk size */
-       hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
+       hci_req_add(req, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
 }
 
-static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
+static void hci_init1_req(struct hci_request *req, unsigned long opt)
 {
+       struct hci_dev *hdev = req->hdev;
+       struct hci_request init_req;
        struct sk_buff *skb;
 
        BT_DBG("%s %ld", hdev->name, opt);
 
        /* Driver initialization */
 
+       hci_req_init(&init_req, hdev);
+
        /* Special commands */
        while ((skb = skb_dequeue(&hdev->driver_init))) {
                bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
                skb->dev = (void *) hdev;
 
-               skb_queue_tail(&hdev->cmd_q, skb);
-               queue_work(hdev->workqueue, &hdev->cmd_work);
+               if (skb_queue_empty(&init_req.cmd_q))
+                       bt_cb(skb)->req.start = true;
+
+               skb_queue_tail(&init_req.cmd_q, skb);
        }
        skb_queue_purge(&hdev->driver_init);
 
+       hci_req_run(&init_req, NULL);
+
        /* Reset */
        if (!test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks))
-               hci_reset_req(hdev, 0);
+               hci_reset_req(req, 0);
 
        switch (hdev->dev_type) {
        case HCI_BREDR:
-               bredr_init(hdev);
+               bredr_init(req);
                break;
 
        case HCI_AMP:
-               amp_init(hdev);
+               amp_init(req);
                break;
 
        default:
@@ -238,44 +243,327 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
        }
 }
 
-static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
+static void bredr_setup(struct hci_request *req)
+{
+       struct hci_cp_delete_stored_link_key cp;
+       __le16 param;
+       __u8 flt_type;
+
+       /* Read Buffer Size (ACL mtu, max pkt, etc.) */
+       hci_req_add(req, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
+
+       /* Read Class of Device */
+       hci_req_add(req, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
+
+       /* Read Local Name */
+       hci_req_add(req, HCI_OP_READ_LOCAL_NAME, 0, NULL);
+
+       /* Read Voice Setting */
+       hci_req_add(req, HCI_OP_READ_VOICE_SETTING, 0, NULL);
+
+       /* Clear Event Filters */
+       flt_type = HCI_FLT_CLEAR_ALL;
+       hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
+
+       /* Connection accept timeout ~20 secs */
+       param = __constant_cpu_to_le16(0x7d00);
+       hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
+
+       bacpy(&cp.bdaddr, BDADDR_ANY);
+       cp.delete_all = 0x01;
+       hci_req_add(req, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
+
+       /* Read page scan parameters */
+       if (req->hdev->hci_ver > BLUETOOTH_VER_1_1) {
+               hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL);
+               hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL);
+       }
+}
+
+static void le_setup(struct hci_request *req)
+{
+       /* Read LE Buffer Size */
+       hci_req_add(req, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
+
+       /* Read LE Local Supported Features */
+       hci_req_add(req, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL);
+
+       /* Read LE Advertising Channel TX Power */
+       hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
+
+       /* Read LE White List Size */
+       hci_req_add(req, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL);
+
+       /* Read LE Supported States */
+       hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
+}
+
+static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
+{
+       if (lmp_ext_inq_capable(hdev))
+               return 0x02;
+
+       if (lmp_inq_rssi_capable(hdev))
+               return 0x01;
+
+       if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 &&
+           hdev->lmp_subver == 0x0757)
+               return 0x01;
+
+       if (hdev->manufacturer == 15) {
+               if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963)
+                       return 0x01;
+               if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963)
+                       return 0x01;
+               if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965)
+                       return 0x01;
+       }
+
+       if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 &&
+           hdev->lmp_subver == 0x1805)
+               return 0x01;
+
+       return 0x00;
+}
+
+static void hci_setup_inquiry_mode(struct hci_request *req)
+{
+       u8 mode;
+
+       mode = hci_get_inquiry_mode(req->hdev);
+
+       hci_req_add(req, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode);
+}
+
+static void hci_setup_event_mask(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+
+       /* The second byte is 0xff instead of 0x9f (two reserved bits
+        * disabled) since a Broadcom 1.2 dongle doesn't respond to the
+        * command otherwise.
+        */
+       u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 };
+
+       /* CSR 1.1 dongles does not accept any bitfield so don't try to set
+        * any event mask for pre 1.2 devices.
+        */
+       if (hdev->hci_ver < BLUETOOTH_VER_1_2)
+               return;
+
+       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 */
+       }
+
+       if (lmp_inq_rssi_capable(hdev))
+               events[4] |= 0x02; /* Inquiry Result with RSSI */
+
+       if (lmp_sniffsubr_capable(hdev))
+               events[5] |= 0x20; /* Sniff Subrating */
+
+       if (lmp_pause_enc_capable(hdev))
+               events[5] |= 0x80; /* Encryption Key Refresh Complete */
+
+       if (lmp_ext_inq_capable(hdev))
+               events[5] |= 0x40; /* Extended Inquiry Result */
+
+       if (lmp_no_flush_capable(hdev))
+               events[7] |= 0x01; /* Enhanced Flush Complete */
+
+       if (lmp_lsto_capable(hdev))
+               events[6] |= 0x80; /* Link Supervision Timeout Changed */
+
+       if (lmp_ssp_capable(hdev)) {
+               events[6] |= 0x01;      /* IO Capability Request */
+               events[6] |= 0x02;      /* IO Capability Response */
+               events[6] |= 0x04;      /* User Confirmation Request */
+               events[6] |= 0x08;      /* User Passkey Request */
+               events[6] |= 0x10;      /* Remote OOB Data Request */
+               events[6] |= 0x20;      /* Simple Pairing Complete */
+               events[7] |= 0x04;      /* User Passkey Notification */
+               events[7] |= 0x08;      /* Keypress Notification */
+               events[7] |= 0x10;      /* Remote Host Supported
+                                        * Features Notification
+                                        */
+       }
+
+       if (lmp_le_capable(hdev))
+               events[7] |= 0x20;      /* LE Meta-Event */
+
+       hci_req_add(req, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
+
+       if (lmp_le_capable(hdev)) {
+               memset(events, 0, sizeof(events));
+               events[0] = 0x1f;
+               hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK,
+                           sizeof(events), events);
+       }
+}
+
+static void hci_init2_req(struct hci_request *req, unsigned long opt)
+{
+       struct hci_dev *hdev = req->hdev;
+
+       if (lmp_bredr_capable(hdev))
+               bredr_setup(req);
+
+       if (lmp_le_capable(hdev))
+               le_setup(req);
+
+       hci_setup_event_mask(req);
+
+       if (hdev->hci_ver > BLUETOOTH_VER_1_1)
+               hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
+
+       if (lmp_ssp_capable(hdev)) {
+               if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+                       u8 mode = 0x01;
+                       hci_req_add(req, HCI_OP_WRITE_SSP_MODE,
+                                   sizeof(mode), &mode);
+               } else {
+                       struct hci_cp_write_eir cp;
+
+                       memset(hdev->eir, 0, sizeof(hdev->eir));
+                       memset(&cp, 0, sizeof(cp));
+
+                       hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+               }
+       }
+
+       if (lmp_inq_rssi_capable(hdev))
+               hci_setup_inquiry_mode(req);
+
+       if (lmp_inq_tx_pwr_capable(hdev))
+               hci_req_add(req, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
+
+       if (lmp_ext_feat_capable(hdev)) {
+               struct hci_cp_read_local_ext_features cp;
+
+               cp.page = 0x01;
+               hci_req_add(req, HCI_OP_READ_LOCAL_EXT_FEATURES,
+                           sizeof(cp), &cp);
+       }
+
+       if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) {
+               u8 enable = 1;
+               hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable),
+                           &enable);
+       }
+}
+
+static void hci_setup_link_policy(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_write_def_link_policy cp;
+       u16 link_policy = 0;
+
+       if (lmp_rswitch_capable(hdev))
+               link_policy |= HCI_LP_RSWITCH;
+       if (lmp_hold_capable(hdev))
+               link_policy |= HCI_LP_HOLD;
+       if (lmp_sniff_capable(hdev))
+               link_policy |= HCI_LP_SNIFF;
+       if (lmp_park_capable(hdev))
+               link_policy |= HCI_LP_PARK;
+
+       cp.policy = cpu_to_le16(link_policy);
+       hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp);
+}
+
+static void hci_set_le_support(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_write_le_host_supported cp;
+
+       memset(&cp, 0, sizeof(cp));
+
+       if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+               cp.le = 0x01;
+               cp.simul = lmp_le_br_capable(hdev);
+       }
+
+       if (cp.le != lmp_host_le_capable(hdev))
+               hci_req_add(req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp),
+                           &cp);
+}
+
+static void hci_init3_req(struct hci_request *req, unsigned long opt)
+{
+       struct hci_dev *hdev = req->hdev;
+
+       if (hdev->commands[5] & 0x10)
+               hci_setup_link_policy(req);
+
+       if (lmp_le_capable(hdev)) {
+               hci_set_le_support(req);
+               hci_update_ad(req);
+       }
+}
+
+static int __hci_init(struct hci_dev *hdev)
+{
+       int err;
+
+       err = __hci_req_sync(hdev, hci_init1_req, 0, HCI_INIT_TIMEOUT);
+       if (err < 0)
+               return err;
+
+       /* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
+        * BR/EDR/LE type controllers. AMP controllers only need the
+        * first stage init.
+        */
+       if (hdev->dev_type != HCI_BREDR)
+               return 0;
+
+       err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
+       if (err < 0)
+               return err;
+
+       return __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
+}
+
+static void hci_scan_req(struct hci_request *req, unsigned long opt)
 {
        __u8 scan = opt;
 
-       BT_DBG("%s %x", hdev->name, scan);
+       BT_DBG("%s %x", req->hdev->name, scan);
 
        /* Inquiry and Page scans */
-       hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
 }
 
-static void hci_auth_req(struct hci_dev *hdev, unsigned long opt)
+static void hci_auth_req(struct hci_request *req, unsigned long opt)
 {
        __u8 auth = opt;
 
-       BT_DBG("%s %x", hdev->name, auth);
+       BT_DBG("%s %x", req->hdev->name, auth);
 
        /* Authentication */
-       hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth);
+       hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth);
 }
 
-static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt)
+static void hci_encrypt_req(struct hci_request *req, unsigned long opt)
 {
        __u8 encrypt = opt;
 
-       BT_DBG("%s %x", hdev->name, encrypt);
+       BT_DBG("%s %x", req->hdev->name, encrypt);
 
        /* Encryption */
-       hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt);
+       hci_req_add(req, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt);
 }
 
-static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
+static void hci_linkpol_req(struct hci_request *req, unsigned long opt)
 {
        __le16 policy = cpu_to_le16(opt);
 
-       BT_DBG("%s %x", hdev->name, policy);
+       BT_DBG("%s %x", req->hdev->name, policy);
 
        /* Default link policy */
-       hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
+       hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
 }
 
 /* Get HCI device by index.
@@ -512,9 +800,10 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
        return copied;
 }
 
-static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
+static void 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;
        struct hci_cp_inquiry cp;
 
        BT_DBG("%s", hdev->name);
@@ -526,7 +815,7 @@ static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
        memcpy(&cp.lap, &ir->lap, 3);
        cp.length  = ir->length;
        cp.num_rsp = ir->num_rsp;
-       hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+       hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
 }
 
 int hci_inquiry(void __user *arg)
@@ -556,7 +845,8 @@ int hci_inquiry(void __user *arg)
        timeo = ir.length * msecs_to_jiffies(2000);
 
        if (do_inquiry) {
-               err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo);
+               err = hci_req_sync(hdev, hci_inq_req, (unsigned long) &ir,
+                                  timeo);
                if (err < 0)
                        goto done;
        }
@@ -654,39 +944,29 @@ static u8 create_ad(struct hci_dev *hdev, u8 *ptr)
        return ad_len;
 }
 
-int hci_update_ad(struct hci_dev *hdev)
+void hci_update_ad(struct hci_request *req)
 {
+       struct hci_dev *hdev = req->hdev;
        struct hci_cp_le_set_adv_data cp;
        u8 len;
-       int err;
 
-       hci_dev_lock(hdev);
-
-       if (!lmp_le_capable(hdev)) {
-               err = -EINVAL;
-               goto unlock;
-       }
+       if (!lmp_le_capable(hdev))
+               return;
 
        memset(&cp, 0, sizeof(cp));
 
        len = create_ad(hdev, cp.data);
 
        if (hdev->adv_data_len == len &&
-           memcmp(cp.data, hdev->adv_data, len) == 0) {
-               err = 0;
-               goto unlock;
-       }
+           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;
-       err = hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
 
-unlock:
-       hci_dev_unlock(hdev);
-
-       return err;
+       hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
 }
 
 /* ---- HCI ioctl helpers ---- */
@@ -735,10 +1015,7 @@ int hci_dev_open(__u16 dev)
        if (!test_bit(HCI_RAW, &hdev->flags)) {
                atomic_set(&hdev->cmd_cnt, 1);
                set_bit(HCI_INIT, &hdev->flags);
-               hdev->init_last_cmd = 0;
-
-               ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
-
+               ret = __hci_init(hdev);
                clear_bit(HCI_INIT, &hdev->flags);
        }
 
@@ -746,7 +1023,6 @@ int hci_dev_open(__u16 dev)
                hci_dev_hold(hdev);
                set_bit(HCI_UP, &hdev->flags);
                hci_notify(hdev, HCI_DEV_UP);
-               hci_update_ad(hdev);
                if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
                    mgmt_valid_hdev(hdev)) {
                        hci_dev_lock(hdev);
@@ -828,7 +1104,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        if (!test_bit(HCI_RAW, &hdev->flags) &&
            test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) {
                set_bit(HCI_INIT, &hdev->flags);
-               __hci_request(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
+               __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
                clear_bit(HCI_INIT, &hdev->flags);
        }
 
@@ -851,6 +1127,10 @@ static int hci_dev_do_close(struct hci_dev *hdev)
         * and no tasks are scheduled. */
        hdev->close(hdev);
 
+       /* Clear flags */
+       hdev->flags = 0;
+       hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
+
        if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
            mgmt_valid_hdev(hdev)) {
                hci_dev_lock(hdev);
@@ -858,9 +1138,6 @@ static int hci_dev_do_close(struct hci_dev *hdev)
                hci_dev_unlock(hdev);
        }
 
-       /* Clear flags */
-       hdev->flags = 0;
-
        /* Controller radio is available but is currently powered down */
        hdev->amp_status = 0;
 
@@ -921,7 +1198,7 @@ int hci_dev_reset(__u16 dev)
        hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
 
        if (!test_bit(HCI_RAW, &hdev->flags))
-               ret = __hci_request(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
+               ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
 
 done:
        hci_req_unlock(hdev);
@@ -960,8 +1237,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
 
        switch (cmd) {
        case HCISETAUTH:
-               err = hci_request(hdev, hci_auth_req, dr.dev_opt,
-                                 HCI_INIT_TIMEOUT);
+               err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
+                                  HCI_INIT_TIMEOUT);
                break;
 
        case HCISETENCRYPT:
@@ -972,24 +1249,24 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
 
                if (!test_bit(HCI_AUTH, &hdev->flags)) {
                        /* Auth must be enabled first */
-                       err = hci_request(hdev, hci_auth_req, dr.dev_opt,
-                                         HCI_INIT_TIMEOUT);
+                       err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
+                                          HCI_INIT_TIMEOUT);
                        if (err)
                                break;
                }
 
-               err = hci_request(hdev, hci_encrypt_req, dr.dev_opt,
-                                 HCI_INIT_TIMEOUT);
+               err = hci_req_sync(hdev, hci_encrypt_req, dr.dev_opt,
+                                  HCI_INIT_TIMEOUT);
                break;
 
        case HCISETSCAN:
-               err = hci_request(hdev, hci_scan_req, dr.dev_opt,
-                                 HCI_INIT_TIMEOUT);
+               err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt,
+                                  HCI_INIT_TIMEOUT);
                break;
 
        case HCISETLINKPOL:
-               err = hci_request(hdev, hci_linkpol_req, dr.dev_opt,
-                                 HCI_INIT_TIMEOUT);
+               err = hci_req_sync(hdev, hci_linkpol_req, dr.dev_opt,
+                                  HCI_INIT_TIMEOUT);
                break;
 
        case HCISETLINKMODE:
@@ -1566,7 +1843,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
        return mgmt_device_unblocked(hdev, bdaddr, type);
 }
 
-static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt)
+static void le_scan_param_req(struct hci_request *req, unsigned long opt)
 {
        struct le_scan_params *param =  (struct le_scan_params *) opt;
        struct hci_cp_le_set_scan_param cp;
@@ -1576,10 +1853,10 @@ static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt)
        cp.interval = cpu_to_le16(param->interval);
        cp.window = cpu_to_le16(param->window);
 
-       hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp);
+       hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp);
 }
 
-static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt)
+static void le_scan_enable_req(struct hci_request *req, unsigned long opt)
 {
        struct hci_cp_le_set_scan_enable cp;
 
@@ -1587,7 +1864,7 @@ static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt)
        cp.enable = 1;
        cp.filter_dup = 1;
 
-       hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+       hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
 }
 
 static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
@@ -1608,10 +1885,10 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
 
        hci_req_lock(hdev);
 
-       err = __hci_request(hdev, le_scan_param_req, (unsigned long) &param,
-                           timeo);
+       err = __hci_req_sync(hdev, le_scan_param_req, (unsigned long) &param,
+                            timeo);
        if (!err)
-               err = __hci_request(hdev, le_scan_enable_req, 0, timeo);
+               err = __hci_req_sync(hdev, le_scan_enable_req, 0, timeo);
 
        hci_req_unlock(hdev);
 
@@ -2160,20 +2437,55 @@ static int hci_send_frame(struct sk_buff *skb)
        return hdev->send(skb);
 }
 
-/* Send HCI command */
-int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
+void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
+{
+       skb_queue_head_init(&req->cmd_q);
+       req->hdev = hdev;
+       req->err = 0;
+}
+
+int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       BT_DBG("length %u", skb_queue_len(&req->cmd_q));
+
+       /* If an error occured during request building, remove all HCI
+        * commands queued on the HCI request queue.
+        */
+       if (req->err) {
+               skb_queue_purge(&req->cmd_q);
+               return req->err;
+       }
+
+       /* Do not allow empty requests */
+       if (skb_queue_empty(&req->cmd_q))
+               return -ENODATA;
+
+       skb = skb_peek_tail(&req->cmd_q);
+       bt_cb(skb)->req.complete = complete;
+
+       spin_lock_irqsave(&hdev->cmd_q.lock, flags);
+       skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
+       spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
+
+       queue_work(hdev->workqueue, &hdev->cmd_work);
+
+       return 0;
+}
+
+static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
+                                      u32 plen, void *param)
 {
        int len = HCI_COMMAND_HDR_SIZE + plen;
        struct hci_command_hdr *hdr;
        struct sk_buff *skb;
 
-       BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
-
        skb = bt_skb_alloc(len, GFP_ATOMIC);
-       if (!skb) {
-               BT_ERR("%s no memory for command", hdev->name);
-               return -ENOMEM;
-       }
+       if (!skb)
+               return NULL;
 
        hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE);
        hdr->opcode = cpu_to_le16(opcode);
@@ -2187,8 +2499,26 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
        bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
        skb->dev = (void *) hdev;
 
-       if (test_bit(HCI_INIT, &hdev->flags))
-               hdev->init_last_cmd = opcode;
+       return skb;
+}
+
+/* Send HCI command */
+int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
+{
+       struct sk_buff *skb;
+
+       BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
+
+       skb = hci_prepare_cmd(hdev, opcode, plen, param);
+       if (!skb) {
+               BT_ERR("%s no memory for command", hdev->name);
+               return -ENOMEM;
+       }
+
+       /* Stand-alone HCI commands must be flaged as
+        * single-command requests.
+        */
+       bt_cb(skb)->req.start = true;
 
        skb_queue_tail(&hdev->cmd_q, skb);
        queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -2196,6 +2526,34 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
        return 0;
 }
 
+/* Queue a command to an asynchronous HCI request */
+void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct sk_buff *skb;
+
+       BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
+
+       /* If an error occured during request building, there is no point in
+        * queueing the HCI command. We can simply return.
+        */
+       if (req->err)
+               return;
+
+       skb = hci_prepare_cmd(hdev, opcode, plen, param);
+       if (!skb) {
+               BT_ERR("%s no memory for command (opcode 0x%4.4x)",
+                      hdev->name, opcode);
+               req->err = -ENOMEM;
+               return;
+       }
+
+       if (skb_queue_empty(&req->cmd_q))
+               bt_cb(skb)->req.start = true;
+
+       skb_queue_tail(&req->cmd_q, skb);
+}
+
 /* Get data from the previously sent command */
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
 {
@@ -2398,7 +2756,7 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
                if (c->type == type && c->sent) {
                        BT_ERR("%s killing stalled connection %pMR",
                               hdev->name, &c->dst);
-                       hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM);
+                       hci_disconnect(c, HCI_ERROR_REMOTE_USER_TERM);
                }
        }
 
@@ -2860,6 +3218,123 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
        kfree_skb(skb);
 }
 
+static bool hci_req_is_complete(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       skb = skb_peek(&hdev->cmd_q);
+       if (!skb)
+               return true;
+
+       return bt_cb(skb)->req.start;
+}
+
+static void hci_resend_last(struct hci_dev *hdev)
+{
+       struct hci_command_hdr *sent;
+       struct sk_buff *skb;
+       u16 opcode;
+
+       if (!hdev->sent_cmd)
+               return;
+
+       sent = (void *) hdev->sent_cmd->data;
+       opcode = __le16_to_cpu(sent->opcode);
+       if (opcode == HCI_OP_RESET)
+               return;
+
+       skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
+       if (!skb)
+               return;
+
+       skb_queue_head(&hdev->cmd_q, skb);
+       queue_work(hdev->workqueue, &hdev->cmd_work);
+}
+
+void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status)
+{
+       hci_req_complete_t req_complete = NULL;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       BT_DBG("opcode 0x%04x status 0x%02x", opcode, status);
+
+       /* If the completed command doesn't match the last one that was
+        * sent we need to do special handling of it.
+        */
+       if (!hci_sent_cmd_data(hdev, opcode)) {
+               /* Some CSR based controllers generate a spontaneous
+                * reset complete event during init and any pending
+                * command will never be completed. In such a case we
+                * need to resend whatever was the last sent
+                * command.
+                */
+               if (test_bit(HCI_INIT, &hdev->flags) && opcode == HCI_OP_RESET)
+                       hci_resend_last(hdev);
+
+               return;
+       }
+
+       /* If the command succeeded and there's still more commands in
+        * this request the request is not yet complete.
+        */
+       if (!status && !hci_req_is_complete(hdev))
+               return;
+
+       /* If this was the last command in a request the complete
+        * callback would be found in hdev->sent_cmd instead of the
+        * command queue (hdev->cmd_q).
+        */
+       if (hdev->sent_cmd) {
+               req_complete = bt_cb(hdev->sent_cmd)->req.complete;
+               if (req_complete)
+                       goto call_complete;
+       }
+
+       /* 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)->req.start) {
+                       __skb_queue_head(&hdev->cmd_q, skb);
+                       break;
+               }
+
+               req_complete = bt_cb(skb)->req.complete;
+               kfree_skb(skb);
+       }
+       spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
+
+call_complete:
+       if (req_complete)
+               req_complete(hdev, status);
+}
+
+void hci_req_cmd_status(struct hci_dev *hdev, u16 opcode, u8 status)
+{
+       hci_req_complete_t req_complete = NULL;
+
+       BT_DBG("opcode 0x%04x status 0x%02x", opcode, status);
+
+       if (status) {
+               hci_req_cmd_complete(hdev, opcode, status);
+               return;
+       }
+
+       /* No need to handle success status if there are more commands */
+       if (!hci_req_is_complete(hdev))
+               return;
+
+       if (hdev->sent_cmd)
+               req_complete = bt_cb(hdev->sent_cmd)->req.complete;
+
+       /* If the request doesn't have a complete callback or there
+        * are other commands/requests in the hdev queue we consider
+        * this request as completed.
+        */
+       if (!req_complete || !skb_queue_empty(&hdev->cmd_q))
+               hci_req_cmd_complete(hdev, opcode, status);
+}
+
 static void hci_rx_work(struct work_struct *work)
 {
        struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work);
index 477726a63512e0160fb550f7c83861266ad585b7..138580745c2ce2679e04475b86131c3f34effca9 100644 (file)
@@ -53,7 +53,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
        hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
        hci_dev_unlock(hdev);
 
-       hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
+       hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status);
 
        hci_conn_check_pending(hdev);
 }
@@ -183,8 +183,6 @@ static void hci_cc_write_def_link_policy(struct hci_dev *hdev,
 
        if (!status)
                hdev->link_policy = get_unaligned_le16(sent);
-
-       hci_req_complete(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, status);
 }
 
 static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
@@ -195,11 +193,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
 
        clear_bit(HCI_RESET, &hdev->flags);
 
-       hci_req_complete(hdev, HCI_OP_RESET, status);
-
        /* Reset all non-persistent flags */
-       hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS) |
-                            BIT(HCI_PERIODIC_INQ));
+       hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
 
        hdev->discovery.state = DISCOVERY_STOPPED;
        hdev->inq_tx_power = HCI_TX_POWER_INVALID;
@@ -228,11 +223,6 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
                memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
 
        hci_dev_unlock(hdev);
-
-       if (!status && !test_bit(HCI_INIT, &hdev->flags))
-               hci_update_ad(hdev);
-
-       hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status);
 }
 
 static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -270,8 +260,6 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
 
        if (test_bit(HCI_MGMT, &hdev->dev_flags))
                mgmt_auth_enable_complete(hdev, status);
-
-       hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status);
 }
 
 static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
@@ -293,8 +281,6 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
                else
                        clear_bit(HCI_ENCRYPT, &hdev->flags);
        }
-
-       hci_req_complete(hdev, HCI_OP_WRITE_ENCRYPT_MODE, status);
 }
 
 static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
@@ -343,7 +329,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
 
 done:
        hci_dev_unlock(hdev);
-       hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status);
 }
 
 static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb)
@@ -435,15 +420,6 @@ static void hci_cc_write_voice_setting(struct hci_dev *hdev,
                hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
 }
 
-static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       __u8 status = *((__u8 *) skb->data);
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status);
-}
-
 static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
@@ -472,211 +448,6 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
        }
 }
 
-static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
-{
-       if (lmp_ext_inq_capable(hdev))
-               return 2;
-
-       if (lmp_inq_rssi_capable(hdev))
-               return 1;
-
-       if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 &&
-           hdev->lmp_subver == 0x0757)
-               return 1;
-
-       if (hdev->manufacturer == 15) {
-               if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963)
-                       return 1;
-               if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963)
-                       return 1;
-               if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965)
-                       return 1;
-       }
-
-       if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 &&
-           hdev->lmp_subver == 0x1805)
-               return 1;
-
-       return 0;
-}
-
-static void hci_setup_inquiry_mode(struct hci_dev *hdev)
-{
-       u8 mode;
-
-       mode = hci_get_inquiry_mode(hdev);
-
-       hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode);
-}
-
-static void hci_setup_event_mask(struct hci_dev *hdev)
-{
-       /* The second byte is 0xff instead of 0x9f (two reserved bits
-        * disabled) since a Broadcom 1.2 dongle doesn't respond to the
-        * command otherwise */
-       u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 };
-
-       /* CSR 1.1 dongles does not accept any bitfield so don't try to set
-        * any event mask for pre 1.2 devices */
-       if (hdev->hci_ver < BLUETOOTH_VER_1_2)
-               return;
-
-       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 */
-       }
-
-       if (lmp_inq_rssi_capable(hdev))
-               events[4] |= 0x02; /* Inquiry Result with RSSI */
-
-       if (lmp_sniffsubr_capable(hdev))
-               events[5] |= 0x20; /* Sniff Subrating */
-
-       if (lmp_pause_enc_capable(hdev))
-               events[5] |= 0x80; /* Encryption Key Refresh Complete */
-
-       if (lmp_ext_inq_capable(hdev))
-               events[5] |= 0x40; /* Extended Inquiry Result */
-
-       if (lmp_no_flush_capable(hdev))
-               events[7] |= 0x01; /* Enhanced Flush Complete */
-
-       if (lmp_lsto_capable(hdev))
-               events[6] |= 0x80; /* Link Supervision Timeout Changed */
-
-       if (lmp_ssp_capable(hdev)) {
-               events[6] |= 0x01;      /* IO Capability Request */
-               events[6] |= 0x02;      /* IO Capability Response */
-               events[6] |= 0x04;      /* User Confirmation Request */
-               events[6] |= 0x08;      /* User Passkey Request */
-               events[6] |= 0x10;      /* Remote OOB Data Request */
-               events[6] |= 0x20;      /* Simple Pairing Complete */
-               events[7] |= 0x04;      /* User Passkey Notification */
-               events[7] |= 0x08;      /* Keypress Notification */
-               events[7] |= 0x10;      /* Remote Host Supported
-                                        * Features Notification */
-       }
-
-       if (lmp_le_capable(hdev))
-               events[7] |= 0x20;      /* LE Meta-Event */
-
-       hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
-
-       if (lmp_le_capable(hdev)) {
-               memset(events, 0, sizeof(events));
-               events[0] = 0x1f;
-               hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK,
-                            sizeof(events), events);
-       }
-}
-
-static void bredr_setup(struct hci_dev *hdev)
-{
-       struct hci_cp_delete_stored_link_key cp;
-       __le16 param;
-       __u8 flt_type;
-
-       /* Read Buffer Size (ACL mtu, max pkt, etc.) */
-       hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
-
-       /* Read Class of Device */
-       hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
-
-       /* Read Local Name */
-       hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);
-
-       /* Read Voice Setting */
-       hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
-
-       /* Clear Event Filters */
-       flt_type = HCI_FLT_CLEAR_ALL;
-       hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
-
-       /* Connection accept timeout ~20 secs */
-       param = __constant_cpu_to_le16(0x7d00);
-       hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
-
-       bacpy(&cp.bdaddr, BDADDR_ANY);
-       cp.delete_all = 1;
-       hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
-}
-
-static void le_setup(struct hci_dev *hdev)
-{
-       /* Read LE Buffer Size */
-       hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
-
-       /* Read LE Local Supported Features */
-       hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL);
-
-       /* Read LE Advertising Channel TX Power */
-       hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
-
-       /* Read LE White List Size */
-       hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL);
-
-       /* Read LE Supported States */
-       hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
-}
-
-static void hci_setup(struct hci_dev *hdev)
-{
-       if (hdev->dev_type != HCI_BREDR)
-               return;
-
-       /* Read BD Address */
-       hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
-
-       if (lmp_bredr_capable(hdev))
-               bredr_setup(hdev);
-
-       if (lmp_le_capable(hdev))
-               le_setup(hdev);
-
-       hci_setup_event_mask(hdev);
-
-       if (hdev->hci_ver > BLUETOOTH_VER_1_1)
-               hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
-
-       if (lmp_ssp_capable(hdev)) {
-               if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
-                       u8 mode = 0x01;
-                       hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE,
-                                    sizeof(mode), &mode);
-               } else {
-                       struct hci_cp_write_eir cp;
-
-                       memset(hdev->eir, 0, sizeof(hdev->eir));
-                       memset(&cp, 0, sizeof(cp));
-
-                       hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
-               }
-       }
-
-       if (lmp_inq_rssi_capable(hdev))
-               hci_setup_inquiry_mode(hdev);
-
-       if (lmp_inq_tx_pwr_capable(hdev))
-               hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
-
-       if (lmp_ext_feat_capable(hdev)) {
-               struct hci_cp_read_local_ext_features cp;
-
-               cp.page = 0x01;
-               hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp),
-                            &cp);
-       }
-
-       if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) {
-               u8 enable = 1;
-               hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable),
-                            &enable);
-       }
-}
-
 static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_rp_read_local_version *rp = (void *) skb->data;
@@ -684,7 +455,7 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
        if (rp->status)
-               goto done;
+               return;
 
        hdev->hci_ver = rp->hci_ver;
        hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
@@ -694,30 +465,6 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name,
               hdev->manufacturer, hdev->hci_ver, hdev->hci_rev);
-
-       if (test_bit(HCI_INIT, &hdev->flags))
-               hci_setup(hdev);
-
-done:
-       hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status);
-}
-
-static void hci_setup_link_policy(struct hci_dev *hdev)
-{
-       struct hci_cp_write_def_link_policy cp;
-       u16 link_policy = 0;
-
-       if (lmp_rswitch_capable(hdev))
-               link_policy |= HCI_LP_RSWITCH;
-       if (lmp_hold_capable(hdev))
-               link_policy |= HCI_LP_HOLD;
-       if (lmp_sniff_capable(hdev))
-               link_policy |= HCI_LP_SNIFF;
-       if (lmp_park_capable(hdev))
-               link_policy |= HCI_LP_PARK;
-
-       cp.policy = cpu_to_le16(link_policy);
-       hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp);
 }
 
 static void hci_cc_read_local_commands(struct hci_dev *hdev,
@@ -727,16 +474,8 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (rp->status)
-               goto done;
-
-       memcpy(hdev->commands, rp->commands, sizeof(hdev->commands));
-
-       if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10))
-               hci_setup_link_policy(hdev);
-
-done:
-       hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status);
+       if (!rp->status)
+               memcpy(hdev->commands, rp->commands, sizeof(hdev->commands));
 }
 
 static void hci_cc_read_local_features(struct hci_dev *hdev,
@@ -795,22 +534,6 @@ static void hci_cc_read_local_features(struct hci_dev *hdev,
               hdev->features[6], hdev->features[7]);
 }
 
-static void hci_set_le_support(struct hci_dev *hdev)
-{
-       struct hci_cp_write_le_host_supported cp;
-
-       memset(&cp, 0, sizeof(cp));
-
-       if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
-               cp.le = 1;
-               cp.simul = lmp_le_br_capable(hdev);
-       }
-
-       if (cp.le != lmp_host_le_capable(hdev))
-               hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp),
-                            &cp);
-}
-
 static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
                                           struct sk_buff *skb)
 {
@@ -819,7 +542,7 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
        if (rp->status)
-               goto done;
+               return;
 
        switch (rp->page) {
        case 0:
@@ -829,12 +552,6 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
                memcpy(hdev->host_features, rp->features, 8);
                break;
        }
-
-       if (test_bit(HCI_INIT, &hdev->flags) && lmp_le_capable(hdev))
-               hci_set_le_support(hdev);
-
-done:
-       hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status);
 }
 
 static void hci_cc_read_flow_control_mode(struct hci_dev *hdev,
@@ -844,12 +561,8 @@ static void hci_cc_read_flow_control_mode(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (rp->status)
-               return;
-
-       hdev->flow_ctl_mode = rp->mode;
-
-       hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status);
+       if (!rp->status)
+               hdev->flow_ctl_mode = rp->mode;
 }
 
 static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
@@ -886,8 +599,65 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
 
        if (!rp->status)
                bacpy(&hdev->bdaddr, &rp->bdaddr);
+}
+
+static void hci_cc_read_page_scan_activity(struct hci_dev *hdev,
+                                          struct sk_buff *skb)
+{
+       struct hci_rp_read_page_scan_activity *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (test_bit(HCI_INIT, &hdev->flags) && !rp->status) {
+               hdev->page_scan_interval = __le16_to_cpu(rp->interval);
+               hdev->page_scan_window = __le16_to_cpu(rp->window);
+       }
+}
+
+static void hci_cc_write_page_scan_activity(struct hci_dev *hdev,
+                                           struct sk_buff *skb)
+{
+       u8 status = *((u8 *) skb->data);
+       struct hci_cp_write_page_scan_activity *sent;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       if (status)
+               return;
+
+       sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY);
+       if (!sent)
+               return;
+
+       hdev->page_scan_interval = __le16_to_cpu(sent->interval);
+       hdev->page_scan_window = __le16_to_cpu(sent->window);
+}
+
+static void hci_cc_read_page_scan_type(struct hci_dev *hdev,
+                                          struct sk_buff *skb)
+{
+       struct hci_rp_read_page_scan_type *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (test_bit(HCI_INIT, &hdev->flags) && !rp->status)
+               hdev->page_scan_type = rp->type;
+}
+
+static void hci_cc_write_page_scan_type(struct hci_dev *hdev,
+                                       struct sk_buff *skb)
+{
+       u8 status = *((u8 *) skb->data);
+       u8 *type;
 
-       hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status);
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       if (status)
+               return;
+
+       type = hci_sent_cmd_data(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE);
+       if (type)
+               hdev->page_scan_type = *type;
 }
 
 static void hci_cc_read_data_block_size(struct hci_dev *hdev,
@@ -908,17 +678,6 @@ static void hci_cc_read_data_block_size(struct hci_dev *hdev,
 
        BT_DBG("%s blk mtu %d cnt %d len %d", hdev->name, hdev->block_mtu,
               hdev->block_cnt, hdev->block_len);
-
-       hci_req_complete(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, rp->status);
-}
-
-static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       __u8 status = *((__u8 *) skb->data);
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status);
 }
 
 static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
@@ -942,8 +701,6 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
        hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to);
        hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to);
 
-       hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status);
-
 a2mp_rsp:
        a2mp_send_getinfo_rsp(hdev);
 }
@@ -985,35 +742,6 @@ a2mp_rsp:
        a2mp_send_create_phy_link_req(hdev, rp->status);
 }
 
-static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
-                                         struct sk_buff *skb)
-{
-       __u8 status = *((__u8 *) skb->data);
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status);
-}
-
-static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       __u8 status = *((__u8 *) skb->data);
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status);
-}
-
-static void hci_cc_write_inquiry_mode(struct hci_dev *hdev,
-                                     struct sk_buff *skb)
-{
-       __u8 status = *((__u8 *) skb->data);
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status);
-}
-
 static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev,
                                         struct sk_buff *skb)
 {
@@ -1023,17 +751,6 @@ static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev,
 
        if (!rp->status)
                hdev->inq_tx_power = rp->tx_power;
-
-       hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, rp->status);
-}
-
-static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       __u8 status = *((__u8 *) skb->data);
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status);
 }
 
 static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1095,8 +812,6 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
        hdev->le_cnt = hdev->le_pkts;
 
        BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts);
-
-       hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
 }
 
 static void hci_cc_le_read_local_features(struct hci_dev *hdev,
@@ -1108,8 +823,6 @@ static void hci_cc_le_read_local_features(struct hci_dev *hdev,
 
        if (!rp->status)
                memcpy(hdev->le_features, rp->features, 8);
-
-       hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status);
 }
 
 static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
@@ -1119,22 +832,8 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (!rp->status) {
+       if (!rp->status)
                hdev->adv_tx_power = rp->tx_power;
-               if (!test_bit(HCI_INIT, &hdev->flags))
-                       hci_update_ad(hdev);
-       }
-
-       hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status);
-}
-
-static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       __u8 status = *((__u8 *) skb->data);
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       hci_req_complete(hdev, HCI_OP_LE_SET_EVENT_MASK, status);
 }
 
 static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1231,12 +930,15 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
                        clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
        }
 
-       hci_dev_unlock(hdev);
+       if (!test_bit(HCI_INIT, &hdev->flags)) {
+               struct hci_request req;
 
-       if (!test_bit(HCI_INIT, &hdev->flags))
-               hci_update_ad(hdev);
+               hci_req_init(&req, hdev);
+               hci_update_ad(&req);
+               hci_req_run(&req, NULL);
+       }
 
-       hci_req_complete(hdev, HCI_OP_LE_SET_ADV_ENABLE, status);
+       hci_dev_unlock(hdev);
 }
 
 static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1245,8 +947,6 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
-       hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status);
-
        if (status) {
                hci_dev_lock(hdev);
                mgmt_start_discovery_failed(hdev, status);
@@ -1269,8 +969,6 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
 
        switch (cp->enable) {
        case LE_SCANNING_ENABLED:
-               hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status);
-
                if (status) {
                        hci_dev_lock(hdev);
                        mgmt_start_discovery_failed(hdev, status);
@@ -1321,32 +1019,6 @@ static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
 
        if (!rp->status)
                hdev->le_white_list_size = rp->size;
-
-       hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status);
-}
-
-static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
-
-       if (rp->status)
-               return;
-
-       hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status);
-}
-
-static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data;
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
-
-       if (rp->status)
-               return;
-
-       hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
 }
 
 static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
@@ -1358,8 +1030,6 @@ static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
 
        if (!rp->status)
                memcpy(hdev->le_states, rp->le_states, 8);
-
-       hci_req_complete(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, rp->status);
 }
 
 static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
@@ -1389,8 +1059,6 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
        if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
            !test_bit(HCI_INIT, &hdev->flags))
                mgmt_le_enable_complete(hdev, sent->le, status);
-
-       hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status);
 }
 
 static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
@@ -1412,7 +1080,6 @@ static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
        if (status) {
-               hci_req_complete(hdev, HCI_OP_INQUIRY, status);
                hci_conn_check_pending(hdev);
                hci_dev_lock(hdev);
                if (test_bit(HCI_MGMT, &hdev->dev_flags))
@@ -1884,11 +1551,6 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
        }
 }
 
-static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
-{
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-}
-
 static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status)
 {
        struct hci_cp_create_phy_link *cp;
@@ -1930,11 +1592,6 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
        amp_write_remote_assoc(hdev, cp->phy_handle);
 }
 
-static void hci_cs_create_logical_link(struct hci_dev *hdev, u8 status)
-{
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-}
-
 static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
@@ -1943,7 +1600,7 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
-       hci_req_complete(hdev, HCI_OP_INQUIRY, status);
+       hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status);
 
        hci_conn_check_pending(hdev);
 
@@ -2399,7 +2056,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
                clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
 
                if (ev->status && conn->state == BT_CONNECTED) {
-                       hci_acl_disconn(conn, HCI_ERROR_AUTH_FAILURE);
+                       hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
                        hci_conn_put(conn);
                        goto unlock;
                }
@@ -2491,20 +2148,10 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
-static void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       BT_DBG("%s", hdev->name);
-}
-
-static void hci_qos_setup_complete_evt(struct hci_dev *hdev,
-                                      struct sk_buff *skb)
-{
-       BT_DBG("%s", hdev->name);
-}
-
 static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_cmd_complete *ev = (void *) skb->data;
+       u8 status = skb->data[sizeof(*ev)];
        __u16 opcode;
 
        skb_pull(skb, sizeof(*ev));
@@ -2588,10 +2235,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_write_voice_setting(hdev, skb);
                break;
 
-       case HCI_OP_HOST_BUFFER_SIZE:
-               hci_cc_host_buffer_size(hdev, skb);
-               break;
-
        case HCI_OP_WRITE_SSP_MODE:
                hci_cc_write_ssp_mode(hdev, skb);
                break;
@@ -2620,46 +2263,42 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_read_bd_addr(hdev, skb);
                break;
 
-       case HCI_OP_READ_DATA_BLOCK_SIZE:
-               hci_cc_read_data_block_size(hdev, skb);
+       case HCI_OP_READ_PAGE_SCAN_ACTIVITY:
+               hci_cc_read_page_scan_activity(hdev, skb);
                break;
 
-       case HCI_OP_WRITE_CA_TIMEOUT:
-               hci_cc_write_ca_timeout(hdev, skb);
+       case HCI_OP_WRITE_PAGE_SCAN_ACTIVITY:
+               hci_cc_write_page_scan_activity(hdev, skb);
                break;
 
-       case HCI_OP_READ_FLOW_CONTROL_MODE:
-               hci_cc_read_flow_control_mode(hdev, skb);
+       case HCI_OP_READ_PAGE_SCAN_TYPE:
+               hci_cc_read_page_scan_type(hdev, skb);
                break;
 
-       case HCI_OP_READ_LOCAL_AMP_INFO:
-               hci_cc_read_local_amp_info(hdev, skb);
+       case HCI_OP_WRITE_PAGE_SCAN_TYPE:
+               hci_cc_write_page_scan_type(hdev, skb);
                break;
 
-       case HCI_OP_READ_LOCAL_AMP_ASSOC:
-               hci_cc_read_local_amp_assoc(hdev, skb);
+       case HCI_OP_READ_DATA_BLOCK_SIZE:
+               hci_cc_read_data_block_size(hdev, skb);
                break;
 
-       case HCI_OP_DELETE_STORED_LINK_KEY:
-               hci_cc_delete_stored_link_key(hdev, skb);
+       case HCI_OP_READ_FLOW_CONTROL_MODE:
+               hci_cc_read_flow_control_mode(hdev, skb);
                break;
 
-       case HCI_OP_SET_EVENT_MASK:
-               hci_cc_set_event_mask(hdev, skb);
+       case HCI_OP_READ_LOCAL_AMP_INFO:
+               hci_cc_read_local_amp_info(hdev, skb);
                break;
 
-       case HCI_OP_WRITE_INQUIRY_MODE:
-               hci_cc_write_inquiry_mode(hdev, skb);
+       case HCI_OP_READ_LOCAL_AMP_ASSOC:
+               hci_cc_read_local_amp_assoc(hdev, skb);
                break;
 
        case HCI_OP_READ_INQ_RSP_TX_POWER:
                hci_cc_read_inq_rsp_tx_power(hdev, skb);
                break;
 
-       case HCI_OP_SET_EVENT_FLT:
-               hci_cc_set_event_flt(hdev, skb);
-               break;
-
        case HCI_OP_PIN_CODE_REPLY:
                hci_cc_pin_code_reply(hdev, skb);
                break;
@@ -2684,10 +2323,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_le_read_adv_tx_power(hdev, skb);
                break;
 
-       case HCI_OP_LE_SET_EVENT_MASK:
-               hci_cc_le_set_event_mask(hdev, skb);
-               break;
-
        case HCI_OP_USER_CONFIRM_REPLY:
                hci_cc_user_confirm_reply(hdev, skb);
                break;
@@ -2720,14 +2355,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_le_read_white_list_size(hdev, skb);
                break;
 
-       case HCI_OP_LE_LTK_REPLY:
-               hci_cc_le_ltk_reply(hdev, skb);
-               break;
-
-       case HCI_OP_LE_LTK_NEG_REPLY:
-               hci_cc_le_ltk_neg_reply(hdev, skb);
-               break;
-
        case HCI_OP_LE_READ_SUPPORTED_STATES:
                hci_cc_le_read_supported_states(hdev, skb);
                break;
@@ -2745,9 +2372,11 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                break;
        }
 
-       if (ev->opcode != HCI_OP_NOP)
+       if (opcode != HCI_OP_NOP)
                del_timer(&hdev->cmd_timer);
 
+       hci_req_cmd_complete(hdev, opcode, status);
+
        if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
                atomic_set(&hdev->cmd_cnt, 1);
                if (!skb_queue_empty(&hdev->cmd_q))
@@ -2817,10 +2446,6 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_le_create_conn(hdev, ev->status);
                break;
 
-       case HCI_OP_LE_START_ENC:
-               hci_cs_le_start_enc(hdev, ev->status);
-               break;
-
        case HCI_OP_CREATE_PHY_LINK:
                hci_cs_create_phylink(hdev, ev->status);
                break;
@@ -2829,18 +2454,16 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_accept_phylink(hdev, ev->status);
                break;
 
-       case HCI_OP_CREATE_LOGICAL_LINK:
-               hci_cs_create_logical_link(hdev, ev->status);
-               break;
-
        default:
                BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
                break;
        }
 
-       if (ev->opcode != HCI_OP_NOP)
+       if (opcode != HCI_OP_NOP)
                del_timer(&hdev->cmd_timer);
 
+       hci_req_cmd_status(hdev, opcode, ev->status);
+
        if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
                atomic_set(&hdev->cmd_cnt, 1);
                if (!skb_queue_empty(&hdev->cmd_q))
@@ -3391,18 +3014,6 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
-static void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       BT_DBG("%s", hdev->name);
-}
-
-static void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       struct hci_ev_sniff_subrate *ev = (void *) skb->data;
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
-}
-
 static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
                                            struct sk_buff *skb)
 {
@@ -3472,7 +3083,7 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev,
        clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
 
        if (ev->status && conn->state == BT_CONNECTED) {
-               hci_acl_disconn(conn, HCI_ERROR_AUTH_FAILURE);
+               hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
                hci_conn_put(conn);
                goto unlock;
        }
@@ -4130,14 +3741,6 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_remote_features_evt(hdev, skb);
                break;
 
-       case HCI_EV_REMOTE_VERSION:
-               hci_remote_version_evt(hdev, skb);
-               break;
-
-       case HCI_EV_QOS_SETUP_COMPLETE:
-               hci_qos_setup_complete_evt(hdev, skb);
-               break;
-
        case HCI_EV_CMD_COMPLETE:
                hci_cmd_complete_evt(hdev, skb);
                break;
@@ -4194,14 +3797,6 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_sync_conn_complete_evt(hdev, skb);
                break;
 
-       case HCI_EV_SYNC_CONN_CHANGED:
-               hci_sync_conn_changed_evt(hdev, skb);
-               break;
-
-       case HCI_EV_SNIFF_SUBRATE:
-               hci_sniff_subrate_evt(hdev, skb);
-               break;
-
        case HCI_EV_EXTENDED_INQUIRY_RESULT:
                hci_extended_inquiry_result_evt(hdev, skb);
                break;
index 6a93614f2c49c4a7c498f20656ed2a0560de7c33..aa4354fca77c76b2282f81d86114c2698088d273 100644 (file)
@@ -854,6 +854,11 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                        skb_queue_tail(&hdev->raw_q, skb);
                        queue_work(hdev->workqueue, &hdev->tx_work);
                } else {
+                       /* Stand-alone HCI commands must be flaged as
+                        * single-command requests.
+                        */
+                       bt_cb(skb)->req.start = true;
+
                        skb_queue_tail(&hdev->cmd_q, skb);
                        queue_work(hdev->workqueue, &hdev->cmd_work);
                }
@@ -1121,8 +1126,6 @@ error:
 void hci_sock_cleanup(void)
 {
        bt_procfs_cleanup(&init_net, "hci");
-       if (bt_sock_unregister(BTPROTO_HCI) < 0)
-               BT_ERR("HCI socket unregistration failed");
-
+       bt_sock_unregister(BTPROTO_HCI);
        proto_unregister(&hci_sk_proto);
 }
index 23b4e242a31a9703cf27c206a1bc66cf8feb0c6b..ff38561385def476441d4fb1c5037f5fb4e9a06a 100644 (file)
@@ -590,10 +590,8 @@ int __init bt_sysfs_init(void)
        bt_debugfs = debugfs_create_dir("bluetooth", NULL);
 
        bt_class = class_create(THIS_MODULE, "bluetooth");
-       if (IS_ERR(bt_class))
-               return PTR_ERR(bt_class);
 
-       return 0;
+       return PTR_RET(bt_class);
 }
 
 void bt_sysfs_cleanup(void)
index a7352ff3fd1e7884c2fc43142fba9bc0033063b8..2342327f3335c69ab1bb7de610db392d8731810f 100644 (file)
@@ -311,6 +311,9 @@ static int hidp_get_raw_report(struct hid_device *hid,
        int numbered_reports = hid->report_enum[report_type].numbered;
        int ret;
 
+       if (atomic_read(&session->terminate))
+               return -EIO;
+
        switch (report_type) {
        case HID_FEATURE_REPORT:
                report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE;
@@ -722,6 +725,7 @@ static int hidp_session(void *arg)
                set_current_state(TASK_INTERRUPTIBLE);
        }
        set_current_state(TASK_RUNNING);
+       atomic_inc(&session->terminate);
        remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
        remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
 
index 82a829d90b0f4a8f60013653bf0eed34237cb5b0..5d0f1ca0a314660698e67d9c2d58d26dcc269ffa 100644 (file)
@@ -304,8 +304,6 @@ error:
 void __exit hidp_cleanup_sockets(void)
 {
        bt_procfs_cleanup(&init_net, "hidp");
-       if (bt_sock_unregister(BTPROTO_HIDP) < 0)
-               BT_ERR("Can't unregister HIDP socket");
-
+       bt_sock_unregister(BTPROTO_HIDP);
        proto_unregister(&hidp_proto);
 }
index 1bcfb8422fdcf55096e9b4131e07eab63d5c365c..7f9704993b74044ec1c93e3308c7172393bb062d 100644 (file)
@@ -1312,8 +1312,6 @@ error:
 void l2cap_cleanup_sockets(void)
 {
        bt_procfs_cleanup(&init_net, "l2cap");
-       if (bt_sock_unregister(BTPROTO_L2CAP) < 0)
-               BT_ERR("L2CAP socket unregistration failed");
-
+       bt_sock_unregister(BTPROTO_L2CAP);
        proto_unregister(&l2cap_proto);
 }
index 39395c7144aa16d0402ae9a71ecd2cd248a1affa..03e7e732215f7e11d9cfca1c6b7c93c70dc96090 100644 (file)
@@ -384,7 +384,8 @@ static u32 get_supported_settings(struct hci_dev *hdev)
 
        if (lmp_bredr_capable(hdev)) {
                settings |= MGMT_SETTING_CONNECTABLE;
-               settings |= MGMT_SETTING_FAST_CONNECTABLE;
+               if (hdev->hci_ver >= BLUETOOTH_VER_1_2)
+                       settings |= MGMT_SETTING_FAST_CONNECTABLE;
                settings |= MGMT_SETTING_DISCOVERABLE;
                settings |= MGMT_SETTING_BREDR;
                settings |= MGMT_SETTING_LINK_SECURITY;
@@ -409,6 +410,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
        if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
                settings |= MGMT_SETTING_CONNECTABLE;
 
+       if (test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags))
+               settings |= MGMT_SETTING_FAST_CONNECTABLE;
+
        if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
                settings |= MGMT_SETTING_DISCOVERABLE;
 
@@ -591,32 +595,33 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
        ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
 }
 
-static int update_eir(struct hci_dev *hdev)
+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 0;
+               return;
 
        if (!lmp_ext_inq_capable(hdev))
-               return 0;
+               return;
 
        if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
-               return 0;
+               return;
 
        if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
-               return 0;
+               return;
 
        memset(&cp, 0, sizeof(cp));
 
        create_eir(hdev, cp.data);
 
        if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
-               return 0;
+               return;
 
        memcpy(hdev->eir, cp.data, sizeof(cp.data));
 
-       return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+       hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
 }
 
 static u8 get_service_classes(struct hci_dev *hdev)
@@ -630,47 +635,48 @@ static u8 get_service_classes(struct hci_dev *hdev)
        return val;
 }
 
-static int update_class(struct hci_dev *hdev)
+static void update_class(struct hci_request *req)
 {
+       struct hci_dev *hdev = req->hdev;
        u8 cod[3];
-       int err;
 
        BT_DBG("%s", hdev->name);
 
        if (!hdev_is_powered(hdev))
-               return 0;
+               return;
 
        if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
-               return 0;
+               return;
 
        cod[0] = hdev->minor_class;
        cod[1] = hdev->major_class;
        cod[2] = get_service_classes(hdev);
 
        if (memcmp(cod, hdev->dev_class, 3) == 0)
-               return 0;
-
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
-       if (err == 0)
-               set_bit(HCI_PENDING_CLASS, &hdev->dev_flags);
+               return;
 
-       return err;
+       hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
 }
 
 static void service_cache_off(struct work_struct *work)
 {
        struct hci_dev *hdev = container_of(work, struct hci_dev,
                                            service_cache.work);
+       struct hci_request req;
 
        if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
                return;
 
+       hci_req_init(&req, hdev);
+
        hci_dev_lock(hdev);
 
-       update_eir(hdev);
-       update_class(hdev);
+       update_eir(&req);
+       update_class(&req);
 
        hci_dev_unlock(hdev);
+
+       hci_req_run(&req, NULL);
 }
 
 static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
@@ -994,11 +1000,64 @@ failed:
        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 (hdev->hci_ver < BLUETOOTH_VER_1_2)
+               return;
+
+       if (enable) {
+               type = PAGE_SCAN_TYPE_INTERLACED;
+
+               /* 160 msec page scan interval */
+               acp.interval = __constant_cpu_to_le16(0x0100);
+       } else {
+               type = PAGE_SCAN_TYPE_STANDARD; /* default */
+
+               /* default 1.28 sec page scan */
+               acp.interval = __constant_cpu_to_le16(0x0800);
+       }
+
+       acp.window = __constant_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)
+{
+       struct pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+       if (!cmd)
+               goto unlock;
+
+       send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
 static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                           u16 len)
 {
        struct mgmt_mode *cp = data;
        struct pending_cmd *cmd;
+       struct hci_request req;
        u8 scan;
        int err;
 
@@ -1065,7 +1124,20 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                        cancel_delayed_work(&hdev->discov_off);
        }
 
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       hci_req_init(&req, hdev);
+
+       hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+
+       /* If we're going from non-connectable to connectable or
+        * vice-versa when fast connectable is enabled ensure that fast
+        * connectable gets disabled. write_fast_connectable won't do
+        * anything if the page scan parameters are already what they
+        * should be.
+        */
+       if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags))
+               write_fast_connectable(&req, false);
+
+       err = hci_req_run(&req, set_connectable_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
@@ -1332,6 +1404,29 @@ unlock:
        return err;
 }
 
+/* This is a helper function to test for pending mgmt commands that can
+ * cause CoD or EIR HCI commands. We can only allow one such pending
+ * mgmt command at a time since otherwise we cannot easily track what
+ * the current values are, will be, and based on that calculate if a new
+ * HCI command needs to be sent and if yes with what value.
+ */
+static bool pending_eir_or_class(struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+
+       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+               switch (cmd->opcode) {
+               case MGMT_OP_ADD_UUID:
+               case MGMT_OP_REMOVE_UUID:
+               case MGMT_OP_SET_DEV_CLASS:
+               case MGMT_OP_SET_POWERED:
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 static const u8 bluetooth_base_uuid[] = {
                        0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
                        0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1351,10 +1446,37 @@ static u8 get_uuid_size(const u8 *uuid)
        return 16;
 }
 
+static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status)
+{
+       struct pending_cmd *cmd;
+
+       hci_dev_lock(hdev);
+
+       cmd = mgmt_pending_find(mgmt_op, hdev);
+       if (!cmd)
+               goto unlock;
+
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status),
+                    hdev->dev_class, 3);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static void add_uuid_complete(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("status 0x%02x", status);
+
+       mgmt_class_complete(hdev, MGMT_OP_ADD_UUID, status);
+}
+
 static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
        struct mgmt_cp_add_uuid *cp = data;
        struct pending_cmd *cmd;
+       struct hci_request req;
        struct bt_uuid *uuid;
        int err;
 
@@ -1362,7 +1484,7 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        hci_dev_lock(hdev);
 
-       if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
+       if (pending_eir_or_class(hdev)) {
                err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID,
                                 MGMT_STATUS_BUSY);
                goto failed;
@@ -1380,23 +1502,28 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        list_add_tail(&uuid->list, &hdev->uuids);
 
-       err = update_class(hdev);
-       if (err < 0)
-               goto failed;
+       hci_req_init(&req, hdev);
 
-       err = update_eir(hdev);
-       if (err < 0)
-               goto failed;
+       update_class(&req);
+       update_eir(&req);
+
+       err = hci_req_run(&req, add_uuid_complete);
+       if (err < 0) {
+               if (err != -ENODATA)
+                       goto failed;
 
-       if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
                err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0,
                                   hdev->dev_class, 3);
                goto failed;
        }
 
        cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len);
-       if (!cmd)
+       if (!cmd) {
                err = -ENOMEM;
+               goto failed;
+       }
+
+       err = 0;
 
 failed:
        hci_dev_unlock(hdev);
@@ -1417,6 +1544,13 @@ static bool enable_service_cache(struct hci_dev *hdev)
        return false;
 }
 
+static void remove_uuid_complete(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("status 0x%02x", status);
+
+       mgmt_class_complete(hdev, MGMT_OP_REMOVE_UUID, status);
+}
+
 static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
                       u16 len)
 {
@@ -1424,13 +1558,14 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
        struct pending_cmd *cmd;
        struct bt_uuid *match, *tmp;
        u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+       struct hci_request req;
        int err, found;
 
        BT_DBG("request for %s", hdev->name);
 
        hci_dev_lock(hdev);
 
-       if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
+       if (pending_eir_or_class(hdev)) {
                err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
                                 MGMT_STATUS_BUSY);
                goto unlock;
@@ -1466,34 +1601,47 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
 update_class:
-       err = update_class(hdev);
-       if (err < 0)
-               goto unlock;
+       hci_req_init(&req, hdev);
 
-       err = update_eir(hdev);
-       if (err < 0)
-               goto unlock;
+       update_class(&req);
+       update_eir(&req);
+
+       err = hci_req_run(&req, remove_uuid_complete);
+       if (err < 0) {
+               if (err != -ENODATA)
+                       goto unlock;
 
-       if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
                err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0,
                                   hdev->dev_class, 3);
                goto unlock;
        }
 
        cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len);
-       if (!cmd)
+       if (!cmd) {
                err = -ENOMEM;
+               goto unlock;
+       }
+
+       err = 0;
 
 unlock:
        hci_dev_unlock(hdev);
        return err;
 }
 
+static void set_class_complete(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("status 0x%02x", status);
+
+       mgmt_class_complete(hdev, MGMT_OP_SET_DEV_CLASS, status);
+}
+
 static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
                         u16 len)
 {
        struct mgmt_cp_set_dev_class *cp = data;
        struct pending_cmd *cmd;
+       struct hci_request req;
        int err;
 
        BT_DBG("request for %s", hdev->name);
@@ -1502,15 +1650,19 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
-       if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags))
-               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
-                                 MGMT_STATUS_BUSY);
+       hci_dev_lock(hdev);
 
-       if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0)
-               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
-                                 MGMT_STATUS_INVALID_PARAMS);
+       if (pending_eir_or_class(hdev)) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+                                MGMT_STATUS_BUSY);
+               goto unlock;
+       }
 
-       hci_dev_lock(hdev);
+       if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+                                MGMT_STATUS_INVALID_PARAMS);
+               goto unlock;
+       }
 
        hdev->major_class = cp->major;
        hdev->minor_class = cp->minor;
@@ -1521,26 +1673,34 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       hci_req_init(&req, hdev);
+
        if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
                hci_dev_unlock(hdev);
                cancel_delayed_work_sync(&hdev->service_cache);
                hci_dev_lock(hdev);
-               update_eir(hdev);
+               update_eir(&req);
        }
 
-       err = update_class(hdev);
-       if (err < 0)
-               goto unlock;
+       update_class(&req);
+
+       err = hci_req_run(&req, set_class_complete);
+       if (err < 0) {
+               if (err != -ENODATA)
+                       goto unlock;
 
-       if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
                err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
                                   hdev->dev_class, 3);
                goto unlock;
        }
 
        cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len);
-       if (!cmd)
+       if (!cmd) {
                err = -ENOMEM;
+               goto unlock;
+       }
+
+       err = 0;
 
 unlock:
        hci_dev_unlock(hdev);
@@ -2140,7 +2300,7 @@ unlock:
 }
 
 static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
-                            bdaddr_t *bdaddr, u8 type, u16 mgmt_op,
+                            struct mgmt_addr_info *addr, u16 mgmt_op,
                             u16 hci_op, __le32 passkey)
 {
        struct pending_cmd *cmd;
@@ -2150,37 +2310,41 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
-               err = cmd_status(sk, hdev->id, mgmt_op,
-                                MGMT_STATUS_NOT_POWERED);
+               err = cmd_complete(sk, hdev->id, mgmt_op,
+                                  MGMT_STATUS_NOT_POWERED, addr,
+                                  sizeof(*addr));
                goto done;
        }
 
-       if (type == BDADDR_BREDR)
-               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
+       if (addr->type == BDADDR_BREDR)
+               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &addr->bdaddr);
        else
-               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
+               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &addr->bdaddr);
 
        if (!conn) {
-               err = cmd_status(sk, hdev->id, mgmt_op,
-                                MGMT_STATUS_NOT_CONNECTED);
+               err = cmd_complete(sk, hdev->id, mgmt_op,
+                                  MGMT_STATUS_NOT_CONNECTED, addr,
+                                  sizeof(*addr));
                goto done;
        }
 
-       if (type == BDADDR_LE_PUBLIC || type == BDADDR_LE_RANDOM) {
+       if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) {
                /* Continue with pairing via SMP */
                err = smp_user_confirm_reply(conn, mgmt_op, passkey);
 
                if (!err)
-                       err = cmd_status(sk, hdev->id, mgmt_op,
-                                        MGMT_STATUS_SUCCESS);
+                       err = cmd_complete(sk, hdev->id, mgmt_op,
+                                          MGMT_STATUS_SUCCESS, addr,
+                                          sizeof(*addr));
                else
-                       err = cmd_status(sk, hdev->id, mgmt_op,
-                                        MGMT_STATUS_FAILED);
+                       err = cmd_complete(sk, hdev->id, mgmt_op,
+                                          MGMT_STATUS_FAILED, addr,
+                                          sizeof(*addr));
 
                goto done;
        }
 
-       cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
+       cmd = mgmt_pending_add(sk, mgmt_op, hdev, addr, sizeof(*addr));
        if (!cmd) {
                err = -ENOMEM;
                goto done;
@@ -2190,11 +2354,12 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
        if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
                struct hci_cp_user_passkey_reply cp;
 
-               bacpy(&cp.bdaddr, bdaddr);
+               bacpy(&cp.bdaddr, &addr->bdaddr);
                cp.passkey = passkey;
                err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
        } else
-               err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
+               err = hci_send_cmd(hdev, hci_op, sizeof(addr->bdaddr),
+                                  &addr->bdaddr);
 
        if (err < 0)
                mgmt_pending_remove(cmd);
@@ -2211,7 +2376,7 @@ static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev,
 
        BT_DBG("");
 
-       return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+       return user_pairing_resp(sk, hdev, &cp->addr,
                                MGMT_OP_PIN_CODE_NEG_REPLY,
                                HCI_OP_PIN_CODE_NEG_REPLY, 0);
 }
@@ -2227,7 +2392,7 @@ static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data,
                return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY,
                                  MGMT_STATUS_INVALID_PARAMS);
 
-       return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+       return user_pairing_resp(sk, hdev, &cp->addr,
                                 MGMT_OP_USER_CONFIRM_REPLY,
                                 HCI_OP_USER_CONFIRM_REPLY, 0);
 }
@@ -2239,7 +2404,7 @@ static int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev,
 
        BT_DBG("");
 
-       return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+       return user_pairing_resp(sk, hdev, &cp->addr,
                                 MGMT_OP_USER_CONFIRM_NEG_REPLY,
                                 HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
 }
@@ -2251,7 +2416,7 @@ static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("");
 
-       return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+       return user_pairing_resp(sk, hdev, &cp->addr,
                                 MGMT_OP_USER_PASSKEY_REPLY,
                                 HCI_OP_USER_PASSKEY_REPLY, cp->passkey);
 }
@@ -2263,18 +2428,47 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
 
        BT_DBG("");
 
-       return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+       return user_pairing_resp(sk, hdev, &cp->addr,
                                 MGMT_OP_USER_PASSKEY_NEG_REPLY,
                                 HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
 }
 
-static int update_name(struct hci_dev *hdev, const char *name)
+static void update_name(struct hci_request *req)
 {
+       struct hci_dev *hdev = req->hdev;
        struct hci_cp_write_local_name cp;
 
-       memcpy(cp.name, name, sizeof(cp.name));
+       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)
+{
+       struct mgmt_cp_set_local_name *cp;
+       struct pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
+       if (!cmd)
+               goto unlock;
+
+       cp = cmd->param;
 
-       return hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
+       if (status)
+               cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
+                          mgmt_status(status));
+       else
+               cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
+                            cp, sizeof(*cp));
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
 }
 
 static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -2282,12 +2476,24 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
 {
        struct mgmt_cp_set_local_name *cp = data;
        struct pending_cmd *cmd;
+       struct hci_request req;
        int err;
 
        BT_DBG("");
 
        hci_dev_lock(hdev);
 
+       /* If the old values are the same as the new ones just return a
+        * direct command complete event.
+        */
+       if (!memcmp(hdev->dev_name, cp->name, sizeof(hdev->dev_name)) &&
+           !memcmp(hdev->short_name, cp->short_name,
+                   sizeof(hdev->short_name))) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
+                                  data, len);
+               goto failed;
+       }
+
        memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name));
 
        if (!hdev_is_powered(hdev)) {
@@ -2310,7 +2516,19 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
-       err = update_name(hdev, cp->name);
+       memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name));
+
+       hci_req_init(&req, hdev);
+
+       if (lmp_bredr_capable(hdev)) {
+               update_name(&req);
+               update_eir(&req);
+       }
+
+       if (lmp_le_capable(hdev))
+               hci_update_ad(&req);
+
+       err = hci_req_run(&req, set_name_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
@@ -2698,6 +2916,7 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
                         u16 len)
 {
        struct mgmt_cp_set_device_id *cp = data;
+       struct hci_request req;
        int err;
        __u16 source;
 
@@ -2718,24 +2937,59 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0, NULL, 0);
 
-       update_eir(hdev);
+       hci_req_init(&req, hdev);
+       update_eir(&req);
+       hci_req_run(&req, NULL);
 
        hci_dev_unlock(hdev);
 
        return err;
 }
 
+static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
+{
+       struct pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev);
+       if (!cmd)
+               goto unlock;
+
+       if (status) {
+               cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+                          mgmt_status(status));
+       } else {
+               struct mgmt_mode *cp = cmd->param;
+
+               if (cp->val)
+                       set_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
+               else
+                       clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
+
+               send_settings_rsp(cmd->sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev);
+               new_settings(hdev, cmd->sk);
+       }
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
 static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
                                void *data, u16 len)
 {
        struct mgmt_mode *cp = data;
-       struct hci_cp_write_page_scan_activity acp;
-       u8 type;
+       struct pending_cmd *cmd;
+       struct hci_request req;
        int err;
 
        BT_DBG("%s", hdev->name);
 
-       if (!lmp_bredr_capable(hdev))
+       if (!lmp_bredr_capable(hdev) || hdev->hci_ver < BLUETOOTH_VER_1_2)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
@@ -2753,40 +3007,39 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
 
        hci_dev_lock(hdev);
 
-       if (cp->val) {
-               type = PAGE_SCAN_TYPE_INTERLACED;
+       if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+                                MGMT_STATUS_BUSY);
+               goto unlock;
+       }
 
-               /* 160 msec page scan interval */
-               acp.interval = __constant_cpu_to_le16(0x0100);
-       } else {
-               type = PAGE_SCAN_TYPE_STANDARD; /* default */
+       if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) {
+               err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE,
+                                       hdev);
+               goto unlock;
+       }
 
-               /* default 1.28 sec page scan */
-               acp.interval = __constant_cpu_to_le16(0x0800);
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev,
+                              data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
        }
 
-       /* default 11.25 msec page scan window */
-       acp.window = __constant_cpu_to_le16(0x0012);
+       hci_req_init(&req, hdev);
 
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp),
-                          &acp);
-       if (err < 0) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-                                MGMT_STATUS_FAILED);
-               goto done;
-       }
+       write_fast_connectable(&req, cp->val);
 
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
+       err = hci_req_run(&req, fast_connectable_complete);
        if (err < 0) {
                err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
                                 MGMT_STATUS_FAILED);
-               goto done;
+               mgmt_pending_remove(cmd);
        }
 
-       err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0,
-                          NULL, 0);
-done:
+unlock:
        hci_dev_unlock(hdev);
+
        return err;
 }
 
@@ -3043,79 +3296,115 @@ static void settings_rsp(struct pending_cmd *cmd, void *data)
        mgmt_pending_free(cmd);
 }
 
-static int set_bredr_scan(struct hci_dev *hdev)
+static void set_bredr_scan(struct hci_request *req)
 {
+       struct hci_dev *hdev = req->hdev;
        u8 scan = 0;
 
+       /* Ensure that fast connectable is disabled. This function will
+        * not do anything if the page scan parameters are already what
+        * they should be.
+        */
+       write_fast_connectable(req, false);
+
        if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
                scan |= SCAN_PAGE;
        if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
                scan |= SCAN_INQUIRY;
 
-       if (!scan)
-               return 0;
-
-       return hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       if (scan)
+               hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
 }
 
-int mgmt_powered(struct hci_dev *hdev, u8 powered)
+static void powered_complete(struct hci_dev *hdev, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
-       int err;
 
-       if (!test_bit(HCI_MGMT, &hdev->dev_flags))
-               return 0;
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
 
        mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
 
-       if (powered) {
-               u8 link_sec;
+       new_settings(hdev, match.sk);
 
-               if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
-                   !lmp_host_ssp_capable(hdev)) {
-                       u8 ssp = 1;
+       hci_dev_unlock(hdev);
 
-                       hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
-               }
+       if (match.sk)
+               sock_put(match.sk);
+}
 
-               if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
-                       struct hci_cp_write_le_host_supported cp;
+static int powered_update_hci(struct hci_dev *hdev)
+{
+       struct hci_request req;
+       u8 link_sec;
 
-                       cp.le = 1;
-                       cp.simul = lmp_le_br_capable(hdev);
+       hci_req_init(&req, hdev);
 
-                       /* 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_send_cmd(hdev,
-                                            HCI_OP_WRITE_LE_HOST_SUPPORTED,
-                                            sizeof(cp), &cp);
-               }
+       if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
+           !lmp_host_ssp_capable(hdev)) {
+               u8 ssp = 1;
 
-               link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
-               if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
-                       hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE,
-                                    sizeof(link_sec), &link_sec);
+               hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
+       }
 
-               if (lmp_bredr_capable(hdev)) {
-                       set_bredr_scan(hdev);
-                       update_class(hdev);
-                       update_name(hdev, hdev->dev_name);
-                       update_eir(hdev);
-               }
-       } else {
-               u8 status = MGMT_STATUS_NOT_POWERED;
-               u8 zero_cod[] = { 0, 0, 0 };
+       if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+               struct hci_cp_write_le_host_supported cp;
 
-               mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+               cp.le = 1;
+               cp.simul = lmp_le_br_capable(hdev);
 
-               if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
-                       mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
-                                  zero_cod, sizeof(zero_cod), NULL);
+               /* 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);
        }
 
+       link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+       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)) {
+               set_bredr_scan(&req);
+               update_class(&req);
+               update_name(&req);
+               update_eir(&req);
+       }
+
+       return hci_req_run(&req, powered_complete);
+}
+
+int mgmt_powered(struct hci_dev *hdev, u8 powered)
+{
+       struct cmd_lookup match = { NULL, hdev };
+       u8 status_not_powered = MGMT_STATUS_NOT_POWERED;
+       u8 zero_cod[] = { 0, 0, 0 };
+       int err;
+
+       if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+               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);
+       mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status_not_powered);
+
+       if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
+               mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
+                          zero_cod, sizeof(zero_cod), NULL);
+
+new_settings:
        err = new_settings(hdev, match.sk);
 
        if (match.sk)
@@ -3152,7 +3441,7 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
 
 int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
 {
-       struct cmd_lookup match = { NULL, hdev };
+       struct pending_cmd *cmd;
        bool changed = false;
        int err = 0;
 
@@ -3164,14 +3453,10 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
                        changed = true;
        }
 
-       mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
-                            &match);
+       cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
 
        if (changed)
-               err = new_settings(hdev, match.sk);
-
-       if (match.sk)
-               sock_put(match.sk);
+               err = new_settings(hdev, cmd ? cmd->sk : NULL);
 
        return err;
 }
@@ -3555,23 +3840,25 @@ int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
        return err;
 }
 
-static int clear_eir(struct hci_dev *hdev)
+static void clear_eir(struct hci_request *req)
 {
+       struct hci_dev *hdev = req->hdev;
        struct hci_cp_write_eir cp;
 
        if (!lmp_ext_inq_capable(hdev))
-               return 0;
+               return;
 
        memset(hdev->eir, 0, sizeof(hdev->eir));
 
        memset(&cp, 0, sizeof(cp));
 
-       return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+       hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
 }
 
 int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
+       struct hci_request req;
        bool changed = false;
        int err = 0;
 
@@ -3604,29 +3891,26 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
        if (match.sk)
                sock_put(match.sk);
 
+       hci_req_init(&req, hdev);
+
        if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
-               update_eir(hdev);
+               update_eir(&req);
        else
-               clear_eir(hdev);
+               clear_eir(&req);
+
+       hci_req_run(&req, NULL);
 
        return err;
 }
 
-static void class_rsp(struct pending_cmd *cmd, void *data)
+static void sk_lookup(struct pending_cmd *cmd, void *data)
 {
        struct cmd_lookup *match = data;
 
-       cmd_complete(cmd->sk, cmd->index, cmd->opcode, match->mgmt_status,
-                    match->hdev->dev_class, 3);
-
-       list_del(&cmd->list);
-
        if (match->sk == NULL) {
                match->sk = cmd->sk;
                sock_hold(match->sk);
        }
-
-       mgmt_pending_free(cmd);
 }
 
 int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
@@ -3635,11 +3919,9 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
        struct cmd_lookup match = { NULL, hdev, mgmt_status(status) };
        int err = 0;
 
-       clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags);
-
-       mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, class_rsp, &match);
-       mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, class_rsp, &match);
-       mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, class_rsp, &match);
+       mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match);
+       mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match);
+       mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
 
        if (!status)
                err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class,
@@ -3653,55 +3935,29 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
 
 int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
 {
-       struct pending_cmd *cmd;
        struct mgmt_cp_set_local_name ev;
-       bool changed = false;
-       int err = 0;
+       struct pending_cmd *cmd;
 
-       if (memcmp(name, hdev->dev_name, sizeof(hdev->dev_name)) != 0) {
-               memcpy(hdev->dev_name, name, sizeof(hdev->dev_name));
-               changed = true;
-       }
+       if (status)
+               return 0;
 
        memset(&ev, 0, sizeof(ev));
        memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
        memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH);
 
        cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
-       if (!cmd)
-               goto send_event;
-
-       /* Always assume that either the short or the complete name has
-        * changed if there was a pending mgmt command */
-       changed = true;
+       if (!cmd) {
+               memcpy(hdev->dev_name, name, sizeof(hdev->dev_name));
 
-       if (status) {
-               err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
-                                mgmt_status(status));
-               goto failed;
+               /* If this is a HCI command related to powering on the
+                * HCI dev don't send any mgmt signals.
+                */
+               if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
+                       return 0;
        }
 
-       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev,
-                          sizeof(ev));
-       if (err < 0)
-               goto failed;
-
-send_event:
-       if (changed)
-               err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev,
-                                sizeof(ev), cmd ? cmd->sk : NULL);
-
-       /* EIR is taken care of separately when powering on the
-        * adapter so only update them here if this is a name change
-        * unrelated to power on.
-        */
-       if (!test_bit(HCI_INIT, &hdev->flags))
-               update_eir(hdev);
-
-failed:
-       if (cmd)
-               mgmt_pending_remove(cmd);
-       return err;
+       return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
+                         cmd ? cmd->sk : NULL);
 }
 
 int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
index b23e2713fea829157959b80802671e79934e38d4..ca957d34b0c89fa29341a179ee16e325ac226e55 100644 (file)
@@ -69,7 +69,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
                                                        u8 sec_level,
                                                        int *err);
 static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst);
-static void rfcomm_session_del(struct rfcomm_session *s);
+static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s);
 
 /* ---- RFCOMM frame parsing macros ---- */
 #define __get_dlci(b)     ((b & 0xfc) >> 2)
@@ -108,12 +108,6 @@ static void rfcomm_schedule(void)
        wake_up_process(rfcomm_thread);
 }
 
-static void rfcomm_session_put(struct rfcomm_session *s)
-{
-       if (atomic_dec_and_test(&s->refcnt))
-               rfcomm_session_del(s);
-}
-
 /* ---- RFCOMM FCS computation ---- */
 
 /* reversed, 8-bit, poly=0x07 */
@@ -249,16 +243,14 @@ static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout)
 {
        BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout);
 
-       if (!mod_timer(&s->timer, jiffies + timeout))
-               rfcomm_session_hold(s);
+       mod_timer(&s->timer, jiffies + timeout);
 }
 
 static void rfcomm_session_clear_timer(struct rfcomm_session *s)
 {
        BT_DBG("session %p state %ld", s, s->state);
 
-       if (del_timer(&s->timer))
-               rfcomm_session_put(s);
+       del_timer_sync(&s->timer);
 }
 
 /* ---- RFCOMM DLCs ---- */
@@ -336,8 +328,6 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
 {
        BT_DBG("dlc %p session %p", d, s);
 
-       rfcomm_session_hold(s);
-
        rfcomm_session_clear_timer(s);
        rfcomm_dlc_hold(d);
        list_add(&d->list, &s->dlcs);
@@ -356,8 +346,6 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
 
        if (list_empty(&s->dlcs))
                rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT);
-
-       rfcomm_session_put(s);
 }
 
 static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
@@ -493,12 +481,34 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
 
 int rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
 {
-       int r;
+       int r = 0;
+       struct rfcomm_dlc *d_list;
+       struct rfcomm_session *s, *s_list;
+
+       BT_DBG("dlc %p state %ld dlci %d err %d", d, d->state, d->dlci, err);
 
        rfcomm_lock();
 
-       r = __rfcomm_dlc_close(d, err);
+       s = d->session;
+       if (!s)
+               goto no_session;
+
+       /* after waiting on the mutex check the session still exists
+        * then check the dlc still exists
+        */
+       list_for_each_entry(s_list, &session_list, list) {
+               if (s_list == s) {
+                       list_for_each_entry(d_list, &s->dlcs, list) {
+                               if (d_list == d) {
+                                       r = __rfcomm_dlc_close(d, err);
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
 
+no_session:
        rfcomm_unlock();
        return r;
 }
@@ -609,7 +619,7 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
        return s;
 }
 
-static void rfcomm_session_del(struct rfcomm_session *s)
+static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s)
 {
        int state = s->state;
 
@@ -617,15 +627,14 @@ static void rfcomm_session_del(struct rfcomm_session *s)
 
        list_del(&s->list);
 
-       if (state == BT_CONNECTED)
-               rfcomm_send_disc(s, 0);
-
        rfcomm_session_clear_timer(s);
        sock_release(s->sock);
        kfree(s);
 
        if (state != BT_LISTEN)
                module_put(THIS_MODULE);
+
+       return NULL;
 }
 
 static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
@@ -644,17 +653,16 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
        return NULL;
 }
 
-static void rfcomm_session_close(struct rfcomm_session *s, int err)
+static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s,
+                                                  int err)
 {
        struct rfcomm_dlc *d;
        struct list_head *p, *n;
 
-       BT_DBG("session %p state %ld err %d", s, s->state, err);
-
-       rfcomm_session_hold(s);
-
        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);
@@ -663,7 +671,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err)
        }
 
        rfcomm_session_clear_timer(s);
-       rfcomm_session_put(s);
+       return rfcomm_session_del(s);
 }
 
 static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
@@ -715,8 +723,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
        if (*err == 0 || *err == -EINPROGRESS)
                return s;
 
-       rfcomm_session_del(s);
-       return NULL;
+       return rfcomm_session_del(s);
 
 failed:
        sock_release(sock);
@@ -1105,7 +1112,7 @@ static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
 }
 
 /* ---- RFCOMM frame reception ---- */
-static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
+static struct rfcomm_session *rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
 {
        BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
 
@@ -1114,7 +1121,7 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
                struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
                if (!d) {
                        rfcomm_send_dm(s, dlci);
-                       return 0;
+                       return s;
                }
 
                switch (d->state) {
@@ -1150,25 +1157,14 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
                        break;
 
                case BT_DISCONN:
-                       /* rfcomm_session_put is called later so don't do
-                        * anything here otherwise we will mess up the session
-                        * reference counter:
-                        *
-                        * (a) when we are the initiator dlc_unlink will drive
-                        * the reference counter to 0 (there is no initial put
-                        * after session_add)
-                        *
-                        * (b) when we are not the initiator rfcomm_rx_process
-                        * will explicitly call put to balance the initial hold
-                        * done after session add.
-                        */
+                       s = rfcomm_session_close(s, ECONNRESET);
                        break;
                }
        }
-       return 0;
+       return s;
 }
 
-static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci)
+static struct rfcomm_session *rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci)
 {
        int err = 0;
 
@@ -1192,13 +1188,13 @@ static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci)
                else
                        err = ECONNRESET;
 
-               s->state = BT_CLOSED;
-               rfcomm_session_close(s, err);
+               s = rfcomm_session_close(s, err);
        }
-       return 0;
+       return s;
 }
 
-static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
+static struct rfcomm_session *rfcomm_recv_disc(struct rfcomm_session *s,
+                                              u8 dlci)
 {
        int err = 0;
 
@@ -1227,11 +1223,9 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
                else
                        err = ECONNRESET;
 
-               s->state = BT_CLOSED;
-               rfcomm_session_close(s, err);
+               s = rfcomm_session_close(s, err);
        }
-
-       return 0;
+       return s;
 }
 
 void rfcomm_dlc_accept(struct rfcomm_dlc *d)
@@ -1652,11 +1646,18 @@ drop:
        return 0;
 }
 
-static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
+static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,
+                                               struct sk_buff *skb)
 {
        struct rfcomm_hdr *hdr = (void *) skb->data;
        u8 type, dlci, fcs;
 
+       if (!s) {
+               /* no session, so free socket data */
+               kfree_skb(skb);
+               return s;
+       }
+
        dlci = __get_dlci(hdr->addr);
        type = __get_type(hdr->ctrl);
 
@@ -1667,7 +1668,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
        if (__check_fcs(skb->data, type, fcs)) {
                BT_ERR("bad checksum in packet");
                kfree_skb(skb);
-               return -EILSEQ;
+               return s;
        }
 
        if (__test_ea(hdr->len))
@@ -1683,22 +1684,23 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
 
        case RFCOMM_DISC:
                if (__test_pf(hdr->ctrl))
-                       rfcomm_recv_disc(s, dlci);
+                       s = rfcomm_recv_disc(s, dlci);
                break;
 
        case RFCOMM_UA:
                if (__test_pf(hdr->ctrl))
-                       rfcomm_recv_ua(s, dlci);
+                       s = rfcomm_recv_ua(s, dlci);
                break;
 
        case RFCOMM_DM:
-               rfcomm_recv_dm(s, dlci);
+               s = rfcomm_recv_dm(s, dlci);
                break;
 
        case RFCOMM_UIH:
-               if (dlci)
-                       return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb);
-
+               if (dlci) {
+                       rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb);
+                       return s;
+               }
                rfcomm_recv_mcc(s, skb);
                break;
 
@@ -1707,7 +1709,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
                break;
        }
        kfree_skb(skb);
-       return 0;
+       return s;
 }
 
 /* ---- Connection and data processing ---- */
@@ -1844,7 +1846,7 @@ static void rfcomm_process_dlcs(struct rfcomm_session *s)
        }
 }
 
-static void rfcomm_process_rx(struct rfcomm_session *s)
+static struct rfcomm_session *rfcomm_process_rx(struct rfcomm_session *s)
 {
        struct socket *sock = s->sock;
        struct sock *sk = sock->sk;
@@ -1856,17 +1858,15 @@ static void rfcomm_process_rx(struct rfcomm_session *s)
        while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
                skb_orphan(skb);
                if (!skb_linearize(skb))
-                       rfcomm_recv_frame(s, skb);
+                       s = rfcomm_recv_frame(s, skb);
                else
                        kfree_skb(skb);
        }
 
-       if (sk->sk_state == BT_CLOSED) {
-               if (!s->initiator)
-                       rfcomm_session_put(s);
+       if (s && (sk->sk_state == BT_CLOSED))
+               s = rfcomm_session_close(s, sk->sk_err);
 
-               rfcomm_session_close(s, sk->sk_err);
-       }
+       return s;
 }
 
 static void rfcomm_accept_connection(struct rfcomm_session *s)
@@ -1891,8 +1891,6 @@ static void rfcomm_accept_connection(struct rfcomm_session *s)
 
        s = rfcomm_session_add(nsock, BT_OPEN);
        if (s) {
-               rfcomm_session_hold(s);
-
                /* We should adjust MTU on incoming sessions.
                 * L2CAP MTU minus UIH header and FCS. */
                s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu,
@@ -1903,7 +1901,7 @@ static void rfcomm_accept_connection(struct rfcomm_session *s)
                sock_release(nsock);
 }
 
-static void rfcomm_check_connection(struct rfcomm_session *s)
+static struct rfcomm_session *rfcomm_check_connection(struct rfcomm_session *s)
 {
        struct sock *sk = s->sock->sk;
 
@@ -1921,10 +1919,10 @@ static void rfcomm_check_connection(struct rfcomm_session *s)
                break;
 
        case BT_CLOSED:
-               s->state = BT_CLOSED;
-               rfcomm_session_close(s, sk->sk_err);
+               s = rfcomm_session_close(s, sk->sk_err);
                break;
        }
+       return s;
 }
 
 static void rfcomm_process_sessions(void)
@@ -1940,7 +1938,6 @@ static void rfcomm_process_sessions(void)
                if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) {
                        s->state = BT_DISCONN;
                        rfcomm_send_disc(s, 0);
-                       rfcomm_session_put(s);
                        continue;
                }
 
@@ -1949,21 +1946,18 @@ static void rfcomm_process_sessions(void)
                        continue;
                }
 
-               rfcomm_session_hold(s);
-
                switch (s->state) {
                case BT_BOUND:
-                       rfcomm_check_connection(s);
+                       s = rfcomm_check_connection(s);
                        break;
 
                default:
-                       rfcomm_process_rx(s);
+                       s = rfcomm_process_rx(s);
                        break;
                }
 
-               rfcomm_process_dlcs(s);
-
-               rfcomm_session_put(s);
+               if (s)
+                       rfcomm_process_dlcs(s);
        }
 
        rfcomm_unlock();
@@ -2010,10 +2004,11 @@ static int rfcomm_add_listener(bdaddr_t *ba)
 
        /* Add listening session */
        s = rfcomm_session_add(sock, BT_LISTEN);
-       if (!s)
+       if (!s) {
+               err = -ENOMEM;
                goto failed;
+       }
 
-       rfcomm_session_hold(s);
        return 0;
 failed:
        sock_release(sock);
@@ -2071,8 +2066,6 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
        if (!s)
                return;
 
-       rfcomm_session_hold(s);
-
        list_for_each_safe(p, n, &s->dlcs) {
                d = list_entry(p, struct rfcomm_dlc, list);
 
@@ -2104,8 +2097,6 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
                        set_bit(RFCOMM_AUTH_REJECT, &d->flags);
        }
 
-       rfcomm_session_put(s);
-
        rfcomm_schedule();
 }
 
index 7c9224bcce178c493ce0a19f3f8250b1828d39b8..a8638b58c4bf28e13298fb66e50edfca08741d2f 100644 (file)
@@ -1066,8 +1066,7 @@ void __exit rfcomm_cleanup_sockets(void)
 
        debugfs_remove(rfcomm_sock_debugfs);
 
-       if (bt_sock_unregister(BTPROTO_RFCOMM) < 0)
-               BT_ERR("RFCOMM socket layer unregistration failed");
+       bt_sock_unregister(BTPROTO_RFCOMM);
 
        proto_unregister(&rfcomm_proto);
 }
index fb6192c9812e81bcac3d8dea31f60095aa40ffda..2c80553505107540f75c1d4521f8a61e900b61cd 100644 (file)
@@ -1113,8 +1113,7 @@ void __exit sco_exit(void)
 
        debugfs_remove(sco_debugfs);
 
-       if (bt_sock_unregister(BTPROTO_SCO) < 0)
-               BT_ERR("SCO socket unregistration failed");
+       bt_sock_unregister(BTPROTO_SCO);
 
        proto_unregister(&sco_proto);
 }
index 314c73ed418fc43e977c7d47e4bce88ea1104b53..967312803e4130f4e27712daed6d9dd9e2a1a9d6 100644 (file)
@@ -348,10 +348,10 @@ void br_dev_setup(struct net_device *dev)
 
        dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
                        NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | NETIF_F_LLTX |
-                       NETIF_F_NETNS_LOCAL | NETIF_F_HW_VLAN_TX;
+                       NETIF_F_NETNS_LOCAL | NETIF_F_HW_VLAN_CTAG_TX;
        dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
                           NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
-                          NETIF_F_HW_VLAN_TX;
+                          NETIF_F_HW_VLAN_CTAG_TX;
 
        br->dev = dev;
        spin_lock_init(&br->lock);
index bab338e6270df4604efe792f40025a40d7682347..c581f1200ef7f0afe3c0b8b65f184060b7a90436 100644 (file)
@@ -161,9 +161,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
        if (!pv)
                return;
 
-       for (vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid);
-            vid < BR_VLAN_BITMAP_LEN;
-            vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid+1)) {
+       for_each_set_bit_from(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
                f = __br_fdb_get(br, br->dev->dev_addr, vid);
                if (f && f->is_local && !f->dst)
                        fdb_delete(br, f);
@@ -724,13 +722,10 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                 * specify a VLAN.  To be nice, add/update entry for every
                 * vlan on this port.
                 */
-               vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
-               while (vid < BR_VLAN_BITMAP_LEN) {
+               for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
                        err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
                        if (err)
                                goto out;
-                       vid = find_next_bit(pv->vlan_bitmap,
-                                           BR_VLAN_BITMAP_LEN, vid+1);
                }
        }
 
@@ -815,11 +810,8 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
                 * vlan on this port.
                 */
                err = -ENOENT;
-               vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
-               while (vid < BR_VLAN_BITMAP_LEN) {
+               for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
                        err &= __br_fdb_delete(p, addr, vid);
-                       vid = find_next_bit(pv->vlan_bitmap,
-                                           BR_VLAN_BITMAP_LEN, vid+1);
                }
        }
 out:
index 459dab22b3f6009dc027c01e29fe587270291053..4cdba60926ffc91c52c031793eb562fb38a84a4a 100644 (file)
@@ -149,7 +149,6 @@ static void del_nbp(struct net_bridge_port *p)
        dev->priv_flags &= ~IFF_BRIDGE_PORT;
 
        netdev_rx_handler_unregister(dev);
-       synchronize_net();
 
        netdev_upper_dev_unlink(dev, br->dev);
 
index ee79f3f20383c9112ff2685ef10b6d69a1d40c97..19942e38fd2d93904522d10209dd7ae3a2d403c3 100644 (file)
@@ -382,7 +382,7 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
        return ret;
 }
 
-static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct br_mdb_entry *entry;
@@ -458,7 +458,7 @@ unlock:
        return err;
 }
 
-static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net_device *dev;
        struct br_mdb_entry *entry;
index 923fbeaf7afdbb957afb732fc226c4fb669e6a2e..81f2389f78eb884e80cafbd624d3029aa48e3ed1 100644 (file)
@@ -1369,7 +1369,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
                return -EINVAL;
 
        if (iph->protocol != IPPROTO_IGMP) {
-               if ((iph->daddr & IGMP_LOCAL_GROUP_MASK) != IGMP_LOCAL_GROUP)
+               if (!ipv4_is_local_multicast(iph->daddr))
                        BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
                return 0;
        }
index fe43bc7b063f5f49b31c237f3f418b56de15379d..1ed75bfd8d1d72f6ae89bef20191eb824d3aac6c 100644 (file)
@@ -535,7 +535,8 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct
        if (brnf_pass_vlan_indev == 0 || !vlan_tx_tag_present(skb))
                return br;
 
-       vlan = __vlan_find_dev_deep(br, vlan_tx_tag_get(skb) & VLAN_VID_MASK);
+       vlan = __vlan_find_dev_deep(br, skb->vlan_proto,
+                                   vlan_tx_tag_get(skb) & VLAN_VID_MASK);
 
        return vlan ? vlan : br;
 }
index 299fc5f40a26c12ef39786d8bc3885c0c96ca7be..8e3abf564798a9044feb2c6e668fda46f30f2dae 100644 (file)
@@ -136,10 +136,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,
                        goto nla_put_failure;
 
                pvid = br_get_pvid(pv);
-               for (vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
-                    vid < BR_VLAN_BITMAP_LEN;
-                    vid = find_next_bit(pv->vlan_bitmap,
-                                        BR_VLAN_BITMAP_LEN, vid+1)) {
+               for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
                        vinfo.vid = vid;
                        vinfo.flags = 0;
                        if (vid == pvid)
@@ -355,17 +352,14 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 /* Change state and parameters on port. */
 int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
 {
-       struct ifinfomsg *ifm;
        struct nlattr *protinfo;
        struct nlattr *afspec;
        struct net_bridge_port *p;
        struct nlattr *tb[IFLA_BRPORT_MAX + 1];
-       int err;
-
-       ifm = nlmsg_data(nlh);
+       int err = 0;
 
-       protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
-       afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
+       protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_PROTINFO);
+       afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
        if (!protinfo && !afspec)
                return 0;
 
@@ -373,7 +367,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
        /* We want to accept dev as bridge itself if the AF_SPEC
         * is set to see if someone is setting vlan info on the brigde
         */
-       if (!p && ((dev->priv_flags & IFF_EBRIDGE) && !afspec))
+       if (!p && !afspec)
                return -EINVAL;
 
        if (p && protinfo) {
@@ -414,14 +408,11 @@ out:
 /* Delete port information */
 int br_dellink(struct net_device *dev, struct nlmsghdr *nlh)
 {
-       struct ifinfomsg *ifm;
        struct nlattr *afspec;
        struct net_bridge_port *p;
        int err;
 
-       ifm = nlmsg_data(nlh);
-
-       afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
+       afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
        if (!afspec)
                return 0;
 
index 93dde75923f02cc8a879c50711f04d4c5c7cc4a3..bd58b45f5f901fd4c6a3ad00abe068baeba8d898 100644 (file)
@@ -34,6 +34,7 @@ static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags)
 
 static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 {
+       const struct net_device_ops *ops;
        struct net_bridge_port *p = NULL;
        struct net_bridge *br;
        struct net_device *dev;
@@ -53,15 +54,17 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
                        br = v->parent.br;
                        dev = br->dev;
                }
+               ops = dev->netdev_ops;
 
-               if (p && (dev->features & NETIF_F_HW_VLAN_FILTER)) {
+               if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
                        /* Add VLAN to the device filter if it is supported.
                         * Stricly speaking, this is not necessary now, since
                         * devices are made promiscuous by the bridge, but if
                         * that ever changes this code will allow tagged
                         * traffic to enter the bridge.
                         */
-                       err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid);
+                       err = ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q),
+                                                      vid);
                        if (err)
                                return err;
                }
@@ -82,8 +85,8 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
        return 0;
 
 out_filt:
-       if (p && (dev->features & NETIF_F_HW_VLAN_FILTER))
-               dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
+       if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER))
+               ops->ndo_vlan_rx_kill_vid(dev, htons(ETH_P_8021Q), vid);
        return err;
 }
 
@@ -97,9 +100,10 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
 
        if (v->port_idx && vid) {
                struct net_device *dev = v->parent.port->dev;
+               const struct net_device_ops *ops = dev->netdev_ops;
 
-               if (dev->features & NETIF_F_HW_VLAN_FILTER)
-                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
+               if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+                       ops->ndo_vlan_rx_kill_vid(dev, htons(ETH_P_8021Q), vid);
        }
 
        clear_bit(vid, v->vlan_bitmap);
@@ -171,7 +175,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
                         * mac header.
                         */
                        skb_push(skb, ETH_HLEN);
-                       skb = __vlan_put_tag(skb, skb->vlan_tci);
+                       skb = __vlan_put_tag(skb, skb->vlan_proto, skb->vlan_tci);
                        if (!skb)
                                goto out;
                        /* put skb->data back to where it was */
@@ -213,7 +217,7 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
                /* PVID is set on this port.  Any untagged ingress
                 * frame is considered to belong to this vlan.
                 */
-               __vlan_hwaccel_put_tag(skb, pvid);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
                return true;
        }
 
index 92de5e5f9db211fb004a5b8f095c2bd215276682..9878eb8204c524334be49d0445cae9c29927ca0d 100644 (file)
@@ -78,6 +78,11 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
    const char *prefix)
 {
        unsigned int bitmask;
+       struct net *net = dev_net(in ? in : out);
+
+       /* FIXME: Disabled from containers until syslog ns is supported */
+       if (!net_eq(net, &init_net))
+               return;
 
        spin_lock_bh(&ebt_log_lock);
        printk(KERN_SOH "%c%s IN=%s OUT=%s MAC source = %pM MAC dest = %pM proto = 0x%04x",
@@ -176,17 +181,18 @@ ebt_log_tg(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct ebt_log_info *info = par->targinfo;
        struct nf_loginfo li;
+       struct net *net = dev_net(par->in ? par->in : par->out);
 
        li.type = NF_LOG_TYPE_LOG;
        li.u.log.level = info->loglevel;
        li.u.log.logflags = info->bitmask;
 
        if (info->bitmask & EBT_LOG_NFLOG)
-               nf_log_packet(NFPROTO_BRIDGE, par->hooknum, skb, par->in,
-                             par->out, &li, "%s", info->prefix);
+               nf_log_packet(net, NFPROTO_BRIDGE, par->hooknum, skb,
+                             par->in, par->out, &li, "%s", info->prefix);
        else
                ebt_log_packet(NFPROTO_BRIDGE, par->hooknum, skb, par->in,
-                              par->out, &li, info->prefix);
+                              par->out, &li, info->prefix);
        return EBT_CONTINUE;
 }
 
@@ -206,19 +212,47 @@ static struct nf_logger ebt_log_logger __read_mostly = {
        .me             = THIS_MODULE,
 };
 
+static int __net_init ebt_log_net_init(struct net *net)
+{
+       nf_log_set(net, NFPROTO_BRIDGE, &ebt_log_logger);
+       return 0;
+}
+
+static void __net_exit ebt_log_net_fini(struct net *net)
+{
+       nf_log_unset(net, &ebt_log_logger);
+}
+
+static struct pernet_operations ebt_log_net_ops = {
+       .init = ebt_log_net_init,
+       .exit = ebt_log_net_fini,
+};
+
 static int __init ebt_log_init(void)
 {
        int ret;
 
+       ret = register_pernet_subsys(&ebt_log_net_ops);
+       if (ret < 0)
+               goto err_pernet;
+
        ret = xt_register_target(&ebt_log_tg_reg);
        if (ret < 0)
-               return ret;
+               goto err_target;
+
        nf_log_register(NFPROTO_BRIDGE, &ebt_log_logger);
-       return 0;
+
+       return ret;
+
+err_target:
+       unregister_pernet_subsys(&ebt_log_net_ops);
+err_pernet:
+       return ret;
 }
 
 static void __exit ebt_log_fini(void)
 {
+       unregister_pernet_subsys(&ebt_log_net_ops);
        nf_log_unregister(&ebt_log_logger);
        xt_unregister_target(&ebt_log_tg_reg);
 }
index 5be68bbcc3419585c99762a549a5fa8f820cdb06..59ac7952010de4b3f76901521fdd192ab1f748b0 100644 (file)
@@ -24,14 +24,15 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct ebt_nflog_info *info = par->targinfo;
        struct nf_loginfo li;
+       struct net *net = dev_net(par->in ? par->in : par->out);
 
        li.type = NF_LOG_TYPE_ULOG;
        li.u.ulog.copy_len = info->len;
        li.u.ulog.group = info->group;
        li.u.ulog.qthreshold = info->threshold;
 
-       nf_log_packet(PF_BRIDGE, par->hooknum, skb, par->in, par->out,
-                     &li, "%s", info->prefix);
+       nf_log_packet(net, PF_BRIDGE, par->hooknum, skb, par->in,
+                     par->out, &li, "%s", info->prefix);
        return EBT_CONTINUE;
 }
 
index 3bf43f7bb9d4b3aabe97856c7b999c039a909472..fc1905c514178a1ae439060588e06a7c5d26e446 100644 (file)
 #include <linux/skbuff.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <linux/netdevice.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter_bridge/ebtables.h>
 #include <linux/netfilter_bridge/ebt_ulog.h>
 #include <net/netfilter/nf_log.h>
+#include <net/netns/generic.h>
 #include <net/sock.h>
 #include "../br_private.h"
 
@@ -62,13 +63,22 @@ typedef struct {
        spinlock_t lock;                /* the per-queue lock */
 } ebt_ulog_buff_t;
 
-static ebt_ulog_buff_t ulog_buffers[EBT_ULOG_MAXNLGROUPS];
-static struct sock *ebtulognl;
+static int ebt_ulog_net_id __read_mostly;
+struct ebt_ulog_net {
+       unsigned int nlgroup[EBT_ULOG_MAXNLGROUPS];
+       ebt_ulog_buff_t ulog_buffers[EBT_ULOG_MAXNLGROUPS];
+       struct sock *ebtulognl;
+};
+
+static struct ebt_ulog_net *ebt_ulog_pernet(struct net *net)
+{
+       return net_generic(net, ebt_ulog_net_id);
+}
 
 /* send one ulog_buff_t to userspace */
-static void ulog_send(unsigned int nlgroup)
+static void ulog_send(struct ebt_ulog_net *ebt, unsigned int nlgroup)
 {
-       ebt_ulog_buff_t *ub = &ulog_buffers[nlgroup];
+       ebt_ulog_buff_t *ub = &ebt->ulog_buffers[nlgroup];
 
        del_timer(&ub->timer);
 
@@ -80,7 +90,7 @@ static void ulog_send(unsigned int nlgroup)
                ub->lastnlh->nlmsg_type = NLMSG_DONE;
 
        NETLINK_CB(ub->skb).dst_group = nlgroup + 1;
-       netlink_broadcast(ebtulognl, ub->skb, 0, nlgroup + 1, GFP_ATOMIC);
+       netlink_broadcast(ebt->ebtulognl, ub->skb, 0, nlgroup + 1, GFP_ATOMIC);
 
        ub->qlen = 0;
        ub->skb = NULL;
@@ -89,10 +99,15 @@ static void ulog_send(unsigned int nlgroup)
 /* timer function to flush queue in flushtimeout time */
 static void ulog_timer(unsigned long data)
 {
-       spin_lock_bh(&ulog_buffers[data].lock);
-       if (ulog_buffers[data].skb)
-               ulog_send(data);
-       spin_unlock_bh(&ulog_buffers[data].lock);
+       struct ebt_ulog_net *ebt = container_of((void *)data,
+                                               struct ebt_ulog_net,
+                                               nlgroup[*(unsigned int *)data]);
+
+       ebt_ulog_buff_t *ub = &ebt->ulog_buffers[*(unsigned int *)data];
+       spin_lock_bh(&ub->lock);
+       if (ub->skb)
+               ulog_send(ebt, *(unsigned int *)data);
+       spin_unlock_bh(&ub->lock);
 }
 
 static struct sk_buff *ulog_alloc_skb(unsigned int size)
@@ -123,8 +138,10 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
        ebt_ulog_packet_msg_t *pm;
        size_t size, copy_len;
        struct nlmsghdr *nlh;
+       struct net *net = dev_net(in ? in : out);
+       struct ebt_ulog_net *ebt = ebt_ulog_pernet(net);
        unsigned int group = uloginfo->nlgroup;
-       ebt_ulog_buff_t *ub = &ulog_buffers[group];
+       ebt_ulog_buff_t *ub = &ebt->ulog_buffers[group];
        spinlock_t *lock = &ub->lock;
        ktime_t kt;
 
@@ -134,7 +151,7 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
        else
                copy_len = uloginfo->cprange;
 
-       size = NLMSG_SPACE(sizeof(*pm) + copy_len);
+       size = nlmsg_total_size(sizeof(*pm) + copy_len);
        if (size > nlbufsiz) {
                pr_debug("Size %Zd needed, but nlbufsiz=%d\n", size, nlbufsiz);
                return;
@@ -146,7 +163,7 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
                if (!(ub->skb = ulog_alloc_skb(size)))
                        goto unlock;
        } else if (size > skb_tailroom(ub->skb)) {
-               ulog_send(group);
+               ulog_send(ebt, group);
 
                if (!(ub->skb = ulog_alloc_skb(size)))
                        goto unlock;
@@ -205,7 +222,7 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
        ub->lastnlh = nlh;
 
        if (ub->qlen >= uloginfo->qthreshold)
-               ulog_send(group);
+               ulog_send(ebt, group);
        else if (!timer_pending(&ub->timer)) {
                ub->timer.expires = jiffies + flushtimeout * HZ / 100;
                add_timer(&ub->timer);
@@ -277,56 +294,89 @@ static struct nf_logger ebt_ulog_logger __read_mostly = {
        .me             = THIS_MODULE,
 };
 
-static int __init ebt_ulog_init(void)
+static int __net_init ebt_ulog_net_init(struct net *net)
 {
-       int ret;
        int i;
+       struct ebt_ulog_net *ebt = ebt_ulog_pernet(net);
+
        struct netlink_kernel_cfg cfg = {
                .groups = EBT_ULOG_MAXNLGROUPS,
        };
 
-       if (nlbufsiz >= 128*1024) {
-               pr_warning("Netlink buffer has to be <= 128kB,"
-                          " please try a smaller nlbufsiz parameter.\n");
-               return -EINVAL;
-       }
-
        /* initialize ulog_buffers */
        for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
-               setup_timer(&ulog_buffers[i].timer, ulog_timer, i);
-               spin_lock_init(&ulog_buffers[i].lock);
+               ebt->nlgroup[i] = i;
+               setup_timer(&ebt->ulog_buffers[i].timer, ulog_timer,
+                           (unsigned long)&ebt->nlgroup[i]);
+               spin_lock_init(&ebt->ulog_buffers[i].lock);
        }
 
-       ebtulognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, &cfg);
-       if (!ebtulognl)
-               ret = -ENOMEM;
-       else if ((ret = xt_register_target(&ebt_ulog_tg_reg)) != 0)
-               netlink_kernel_release(ebtulognl);
+       ebt->ebtulognl = netlink_kernel_create(net, NETLINK_NFLOG, &cfg);
+       if (!ebt->ebtulognl)
+               return -ENOMEM;
 
-       if (ret == 0)
-               nf_log_register(NFPROTO_BRIDGE, &ebt_ulog_logger);
-
-       return ret;
+       nf_log_set(net, NFPROTO_BRIDGE, &ebt_ulog_logger);
+       return 0;
 }
 
-static void __exit ebt_ulog_fini(void)
+static void __net_exit ebt_ulog_net_fini(struct net *net)
 {
-       ebt_ulog_buff_t *ub;
        int i;
+       struct ebt_ulog_net *ebt = ebt_ulog_pernet(net);
 
-       nf_log_unregister(&ebt_ulog_logger);
-       xt_unregister_target(&ebt_ulog_tg_reg);
+       nf_log_unset(net, &ebt_ulog_logger);
        for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
-               ub = &ulog_buffers[i];
+               ebt_ulog_buff_t *ub = &ebt->ulog_buffers[i];
                del_timer(&ub->timer);
-               spin_lock_bh(&ub->lock);
+
                if (ub->skb) {
                        kfree_skb(ub->skb);
                        ub->skb = NULL;
                }
-               spin_unlock_bh(&ub->lock);
        }
-       netlink_kernel_release(ebtulognl);
+       netlink_kernel_release(ebt->ebtulognl);
+}
+
+static struct pernet_operations ebt_ulog_net_ops = {
+       .init = ebt_ulog_net_init,
+       .exit = ebt_ulog_net_fini,
+       .id   = &ebt_ulog_net_id,
+       .size = sizeof(struct ebt_ulog_net),
+};
+
+static int __init ebt_ulog_init(void)
+{
+       int ret;
+
+       if (nlbufsiz >= 128*1024) {
+               pr_warn("Netlink buffer has to be <= 128kB,"
+                       "please try a smaller nlbufsiz parameter.\n");
+               return -EINVAL;
+       }
+
+       ret = register_pernet_subsys(&ebt_ulog_net_ops);
+       if (ret)
+               goto out_pernet;
+
+       ret = xt_register_target(&ebt_ulog_tg_reg);
+       if (ret)
+               goto out_target;
+
+       nf_log_register(NFPROTO_BRIDGE, &ebt_ulog_logger);
+
+       return 0;
+
+out_target:
+       unregister_pernet_subsys(&ebt_ulog_net_ops);
+out_pernet:
+       return ret;
+}
+
+static void __exit ebt_ulog_fini(void)
+{
+       nf_log_unregister(&ebt_ulog_logger);
+       xt_unregister_target(&ebt_ulog_tg_reg);
+       unregister_pernet_subsys(&ebt_ulog_net_ops);
 }
 
 module_init(ebt_ulog_init);
index 40d8258bf74f89fbd31193c72b436b1d802f8a7f..70f656ce0f4ac30c45f66753d78d3b060082ad9b 100644 (file)
@@ -64,9 +64,7 @@ static int ebt_broute(struct sk_buff *skb)
 static int __net_init broute_net_init(struct net *net)
 {
        net->xt.broute_table = ebt_register_table(net, &broute_table);
-       if (IS_ERR(net->xt.broute_table))
-               return PTR_ERR(net->xt.broute_table);
-       return 0;
+       return PTR_RET(net->xt.broute_table);
 }
 
 static void __net_exit broute_net_exit(struct net *net)
index 8d493c91a56277d48605e68db7354aa526550c2f..3d110c4fc7870dda1d6efcb00f9169f819dc6526 100644 (file)
@@ -138,7 +138,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
                ethproto = h->h_proto;
 
        if (e->bitmask & EBT_802_3) {
-               if (FWINV2(ntohs(ethproto) >= 1536, EBT_IPROTO))
+               if (FWINV2(ntohs(ethproto) >= ETH_P_802_3_MIN, EBT_IPROTO))
                        return 1;
        } else if (!(e->bitmask & EBT_NOPROTO) &&
           FWINV2(e->ethproto != ethproto, EBT_IPROTO))
index 21760f0089749217e03790e02b49998fc5a7958c..df6d56d8689ab93fec66f1e9284fda48ebca331c 100644 (file)
@@ -301,10 +301,11 @@ static void dev_flowctrl(struct net_device *dev, int on)
 }
 
 void caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
-                       struct cflayer *link_support, int head_room,
-                       struct cflayer **layer, int (**rcv_func)(
-                               struct sk_buff *, struct net_device *,
-                               struct packet_type *, struct net_device *))
+                    struct cflayer *link_support, int head_room,
+                    struct cflayer **layer,
+                    int (**rcv_func)(struct sk_buff *, struct net_device *,
+                                     struct packet_type *,
+                                     struct net_device *))
 {
        struct caif_device_entry *caifd;
        enum cfcnfg_phy_preference pref;
index ff2ff3ce6965a73dcf74918cd5c5bcc1b4a7d51b..630b8be6e748d92fd48a4dc6fb82db303d57a636 100644 (file)
@@ -197,8 +197,8 @@ static void cfsk_put(struct cflayer *layr)
 
 /* Packet Control Callback function called from CAIF */
 static void caif_ctrl_cb(struct cflayer *layr,
-                               enum caif_ctrlcmd flow,
-                               int phyid)
+                        enum caif_ctrlcmd flow,
+                        int phyid)
 {
        struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
        switch (flow) {
@@ -274,7 +274,7 @@ static void caif_check_flow_release(struct sock *sk)
  * changed locking, address handling and added MSG_TRUNC.
  */
 static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock,
-                               struct msghdr *m, size_t len, int flags)
+                              struct msghdr *m, size_t len, int flags)
 
 {
        struct sock *sk = sock->sk;
@@ -348,8 +348,8 @@ static long caif_stream_data_wait(struct sock *sk, long timeo)
  * changed locking calls, changed address handling.
  */
 static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
-                               struct msghdr *msg, size_t size,
-                               int flags)
+                              struct msghdr *msg, size_t size,
+                              int flags)
 {
        struct sock *sk = sock->sk;
        int copied = 0;
@@ -464,7 +464,7 @@ out:
  * CAIF flow-on and sock_writable.
  */
 static long caif_wait_for_flow_on(struct caifsock *cf_sk,
-                               int wait_writeable, long timeo, int *err)
+                                 int wait_writeable, long timeo, int *err)
 {
        struct sock *sk = &cf_sk->sk;
        DEFINE_WAIT(wait);
@@ -518,7 +518,7 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk,
 
 /* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */
 static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock,
-                       struct msghdr *msg, size_t len)
+                              struct msghdr *msg, size_t len)
 {
        struct sock *sk = sock->sk;
        struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
@@ -593,7 +593,7 @@ err:
  * and other minor adaptations.
  */
 static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
-                               struct msghdr *msg, size_t len)
+                              struct msghdr *msg, size_t len)
 {
        struct sock *sk = sock->sk;
        struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
@@ -672,7 +672,7 @@ out_err:
 }
 
 static int setsockopt(struct socket *sock,
-                       int lvl, int opt, char __user *ov, unsigned int ol)
+                     int lvl, int opt, char __user *ov, unsigned int ol)
 {
        struct sock *sk = sock->sk;
        struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
@@ -934,7 +934,7 @@ static int caif_release(struct socket *sock)
 
 /* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */
 static unsigned int caif_poll(struct file *file,
-                               struct socket *sock, poll_table *wait)
+                             struct socket *sock, poll_table *wait)
 {
        struct sock *sk = sock->sk;
        unsigned int mask;
@@ -1024,7 +1024,7 @@ static void caif_sock_destructor(struct sock *sk)
 }
 
 static int caif_create(struct net *net, struct socket *sock, int protocol,
-                       int kern)
+                      int kern)
 {
        struct sock *sk = NULL;
        struct caifsock *cf_sk = NULL;
index ef8ebaa993cf3ca9c915f8d2a230a3d04af79c34..d76278d644b82438e62fa290a136181e98de0d82 100644 (file)
@@ -75,7 +75,7 @@ static int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt)
 }
 
 static void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                                       int phyid)
+                          int phyid)
 {
        if (layr->up && layr->up->ctrlcmd)
                layr->up->ctrlcmd(layr->up, ctrl, layr->id);
@@ -121,7 +121,7 @@ static struct packet_type caif_usb_type __read_mostly = {
 };
 
 static int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
-                             void *arg)
+                               void *arg)
 {
        struct net_device *dev = arg;
        struct caif_dev_common common;
index f1dbddb95a6cae5f21d00b65f11a91a6a9c27ca7..246ac3aa8de51616081803d2befca90dcdf7f92d 100644 (file)
@@ -61,11 +61,11 @@ struct cfcnfg {
 };
 
 static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id,
-                            enum cfctrl_srv serv, u8 phyid,
-                            struct cflayer *adapt_layer);
+                             enum cfctrl_srv serv, u8 phyid,
+                             struct cflayer *adapt_layer);
 static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id);
 static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
-                            struct cflayer *adapt_layer);
+                             struct cflayer *adapt_layer);
 static void cfctrl_resp_func(void);
 static void cfctrl_enum_resp(void);
 
@@ -131,7 +131,7 @@ static void cfctrl_resp_func(void)
 }
 
 static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg,
-                                                       u8 phyid)
+                                                    u8 phyid)
 {
        struct cfcnfg_phyinfo *phy;
 
@@ -216,8 +216,8 @@ static const int protohead[CFCTRL_SRV_MASK] = {
 
 
 static int caif_connect_req_to_link_param(struct cfcnfg *cnfg,
-                                  struct caif_connect_request *s,
-                                  struct cfctrl_link_param *l)
+                                         struct caif_connect_request *s,
+                                         struct cfctrl_link_param *l)
 {
        struct dev_info *dev_info;
        enum cfcnfg_phy_preference pref;
@@ -301,8 +301,7 @@ static int caif_connect_req_to_link_param(struct cfcnfg *cnfg,
 
 int caif_connect_client(struct net *net, struct caif_connect_request *conn_req,
                        struct cflayer *adap_layer, int *ifindex,
-                               int *proto_head,
-                               int *proto_tail)
+                       int *proto_head, int *proto_tail)
 {
        struct cflayer *frml;
        struct cfcnfg_phyinfo *phy;
@@ -364,7 +363,7 @@ unlock:
 EXPORT_SYMBOL(caif_connect_client);
 
 static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
-                            struct cflayer *adapt_layer)
+                             struct cflayer *adapt_layer)
 {
        if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
                adapt_layer->ctrlcmd(adapt_layer,
@@ -526,7 +525,7 @@ out_err:
 EXPORT_SYMBOL(cfcnfg_add_phy_layer);
 
 int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer,
-               bool up)
+                        bool up)
 {
        struct cfcnfg_phyinfo *phyinfo;
 
index a376ec1ac0a7b086354b24f498af65bc4d5fc6b2..9cd057c59c5948bf2046077b8c7b8a3fd37b70ea 100644 (file)
 
 #ifdef CAIF_NO_LOOP
 static int handle_loop(struct cfctrl *ctrl,
-                             int cmd, struct cfpkt *pkt){
+                      int cmd, struct cfpkt *pkt){
        return -1;
 }
 #else
 static int handle_loop(struct cfctrl *ctrl,
-               int cmd, struct cfpkt *pkt);
+                      int cmd, struct cfpkt *pkt);
 #endif
 static int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt);
 static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
@@ -72,7 +72,7 @@ void cfctrl_remove(struct cflayer *layer)
 }
 
 static bool param_eq(const struct cfctrl_link_param *p1,
-                       const struct cfctrl_link_param *p2)
+                    const struct cfctrl_link_param *p2)
 {
        bool eq =
            p1->linktype == p2->linktype &&
@@ -197,8 +197,8 @@ void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
 }
 
 int cfctrl_linkup_request(struct cflayer *layer,
-                          struct cfctrl_link_param *param,
-                          struct cflayer *user_layer)
+                         struct cfctrl_link_param *param,
+                         struct cflayer *user_layer)
 {
        struct cfctrl *cfctrl = container_obj(layer);
        u32 tmp32;
@@ -301,7 +301,7 @@ int cfctrl_linkup_request(struct cflayer *layer,
 }
 
 int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
-                               struct cflayer *client)
+                       struct cflayer *client)
 {
        int ret;
        struct cfpkt *pkt;
@@ -555,7 +555,7 @@ error:
 }
 
 static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                       int phyid)
+                          int phyid)
 {
        struct cfctrl *this = container_obj(layr);
        switch (ctrl) {
index 0a7df7ef062d9605515d973c197f26b522fed0bd..204c5e226a61b5e4cf22031c65f44e9c70ab89a9 100644 (file)
@@ -28,7 +28,7 @@ struct cffrml {
 static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt);
 static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt);
 static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                               int phyid);
+                          int phyid);
 
 static u32 cffrml_rcv_error;
 static u32 cffrml_rcv_checsum_error;
@@ -167,7 +167,7 @@ static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt)
 }
 
 static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                                       int phyid)
+                          int phyid)
 {
        if (layr->up && layr->up->ctrlcmd)
                layr->up->ctrlcmd(layr->up, ctrl, layr->id);
index 94b08612a4d8d76e7056cef299ea58f6b38598d6..154d9f8f964c681594e924ba06e651f815602f78 100644 (file)
@@ -42,7 +42,7 @@ struct cfmuxl {
 static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt);
 static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt);
 static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                               int phyid);
+                          int phyid);
 static struct cflayer *get_up(struct cfmuxl *muxl, u16 id);
 
 struct cflayer *cfmuxl_create(void)
@@ -244,7 +244,7 @@ static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt)
 }
 
 static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                               int phyid)
+                          int phyid)
 {
        struct cfmuxl *muxl = container_obj(layr);
        struct cflayer *layer;
index 863dedd91bb6b60766c3a91f0c321c29b6291afc..e8f9c149504d9c2f5881d180888bd042f0bccadd 100644 (file)
@@ -266,8 +266,8 @@ inline u16 cfpkt_getlen(struct cfpkt *pkt)
 }
 
 inline u16 cfpkt_iterate(struct cfpkt *pkt,
-                           u16 (*iter_func)(u16, void *, u16),
-                           u16 data)
+                        u16 (*iter_func)(u16, void *, u16),
+                        u16 data)
 {
        /*
         * Don't care about the performance hit of linearizing,
@@ -307,8 +307,8 @@ int cfpkt_setlen(struct cfpkt *pkt, u16 len)
 }
 
 struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,
-                            struct cfpkt *addpkt,
-                            u16 expectlen)
+                          struct cfpkt *addpkt,
+                          u16 expectlen)
 {
        struct sk_buff *dst = pkt_to_skb(dstpkt);
        struct sk_buff *add = pkt_to_skb(addpkt);
index 2b563ad04597de1b049e8d52c4944d9ed48b5da3..db51830c8587c07d01ed0004942621672cf633c6 100644 (file)
@@ -43,7 +43,7 @@ static void cfrfml_release(struct cflayer *layer)
 }
 
 struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info,
-                                       int mtu_size)
+                             int mtu_size)
 {
        int tmp;
        struct cfrfml *this = kzalloc(sizeof(struct cfrfml), GFP_ATOMIC);
@@ -69,7 +69,7 @@ struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info,
 }
 
 static struct cfpkt *rfm_append(struct cfrfml *rfml, char *seghead,
-                       struct cfpkt *pkt, int *err)
+                               struct cfpkt *pkt, int *err)
 {
        struct cfpkt *tmppkt;
        *err = -EPROTO;
index 8e68b97f13ee71c23186fac06573590ae3664f78..147c232b12858d84f26a536f335ac5293392c641 100644 (file)
@@ -29,7 +29,7 @@ struct cfserl {
 static int cfserl_receive(struct cflayer *layr, struct cfpkt *pkt);
 static int cfserl_transmit(struct cflayer *layr, struct cfpkt *pkt);
 static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                               int phyid);
+                          int phyid);
 
 struct cflayer *cfserl_create(int instance, bool use_stx)
 {
@@ -182,7 +182,7 @@ static int cfserl_transmit(struct cflayer *layer, struct cfpkt *newpkt)
 }
 
 static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                               int phyid)
+                          int phyid)
 {
        layr->up->ctrlcmd(layr->up, ctrl, phyid);
 }
index ba217e90765e11024251feae5bbcb46450101ea7..95f7f5ea30efda9d53a1cf1eae73ae6119862932 100644 (file)
@@ -25,7 +25,7 @@
 #define container_obj(layr) container_of(layr, struct cfsrvl, layer)
 
 static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
-                               int phyid)
+                           int phyid)
 {
        struct cfsrvl *service = container_obj(layr);
 
@@ -158,10 +158,9 @@ static void cfsrvl_release(struct cflayer *layer)
 }
 
 void cfsrvl_init(struct cfsrvl *service,
-                       u8 channel_id,
-                       struct dev_info *dev_info,
-                       bool supports_flowctrl
-                       )
+                u8 channel_id,
+                struct dev_info *dev_info,
+                bool supports_flowctrl)
 {
        caif_assert(offsetof(struct cfsrvl, layer) == 0);
        service->open = false;
@@ -207,8 +206,8 @@ void caif_free_client(struct cflayer *adap_layer)
 EXPORT_SYMBOL(caif_free_client);
 
 void caif_client_register_refcnt(struct cflayer *adapt_layer,
-                                       void (*hold)(struct cflayer *lyr),
-                                       void (*put)(struct cflayer *lyr))
+                                void (*hold)(struct cflayer *lyr),
+                                void (*put)(struct cflayer *lyr))
 {
        struct cfsrvl *service;
 
index e597733affb82f088203bab569644f312e8d5463..26a4e4e3a767c6e19c0298530f24a4fd6e2d2211 100644 (file)
@@ -167,7 +167,7 @@ static void chnl_put(struct cflayer *lyr)
 }
 
 static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow,
-                               int phyid)
+                            int phyid)
 {
        struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);
        pr_debug("NET flowctrl func called flow: %s\n",
@@ -443,7 +443,7 @@ nla_put_failure:
 }
 
 static void caif_netlink_parms(struct nlattr *data[],
-                               struct caif_connect_request *conn_req)
+                              struct caif_connect_request *conn_req)
 {
        if (!data) {
                pr_warn("no params data found\n");
@@ -488,7 +488,7 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev,
 }
 
 static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[],
-                               struct nlattr *data[])
+                            struct nlattr *data[])
 {
        struct chnl_net *caifdev;
        ASSERT_RTNL();
index c48e5220bbac6ada201102ddf60dbb6668b4e868..c4e50852c9f4aa8e195ed57e711890210cea1e1c 100644 (file)
@@ -525,7 +525,7 @@ void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
 
        d = find_dev_rcv_lists(dev);
        if (!d) {
-               printk(KERN_ERR "BUG: receive list not found for "
+               pr_err("BUG: receive list not found for "
                       "dev %s, id %03X, mask %03X\n",
                       DNAME(dev), can_id, mask);
                goto out;
@@ -546,16 +546,13 @@ void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
        }
 
        /*
-        * Check for bugs in CAN protocol implementations:
-        * If no matching list item was found, the list cursor variable next
-        * will be NULL, while r will point to the last item of the list.
+        * Check for bugs in CAN protocol implementations using af_can.c:
+        * 'r' will be NULL if no matching list item was found for removal.
         */
 
        if (!r) {
-               printk(KERN_ERR "BUG: receive list entry not found for "
-                      "dev %s, id %03X, mask %03X\n",
-                      DNAME(dev), can_id, mask);
-               r = NULL;
+               WARN(1, "BUG: receive list entry not found for dev %s, "
+                    "id %03X, mask %03X\n", DNAME(dev), can_id, mask);
                goto out;
        }
 
@@ -749,8 +746,7 @@ int can_proto_register(const struct can_proto *cp)
        int err = 0;
 
        if (proto < 0 || proto >= CAN_NPROTO) {
-               printk(KERN_ERR "can: protocol number %d out of range\n",
-                      proto);
+               pr_err("can: protocol number %d out of range\n", proto);
                return -EINVAL;
        }
 
@@ -761,8 +757,7 @@ int can_proto_register(const struct can_proto *cp)
        mutex_lock(&proto_tab_lock);
 
        if (proto_tab[proto]) {
-               printk(KERN_ERR "can: protocol %d already registered\n",
-                      proto);
+               pr_err("can: protocol %d already registered\n", proto);
                err = -EBUSY;
        } else
                RCU_INIT_POINTER(proto_tab[proto], cp);
@@ -816,11 +811,8 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg,
 
                /* create new dev_rcv_lists for this device */
                d = kzalloc(sizeof(*d), GFP_KERNEL);
-               if (!d) {
-                       printk(KERN_ERR
-                              "can: allocation of receive list failed\n");
+               if (!d)
                        return NOTIFY_DONE;
-               }
                BUG_ON(dev->ml_priv);
                dev->ml_priv = d;
 
@@ -838,8 +830,8 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg,
                                dev->ml_priv = NULL;
                        }
                } else
-                       printk(KERN_ERR "can: notifier: receive list not "
-                              "found for dev %s\n", dev->name);
+                       pr_err("can: notifier: receive list not found for dev "
+                              "%s\n", dev->name);
 
                spin_unlock(&can_rcvlists_lock);
 
@@ -927,7 +919,7 @@ static __exit void can_exit(void)
        /* remove created dev_rcv_lists from still registered CAN devices */
        rcu_read_lock();
        for_each_netdev_rcu(&init_net, dev) {
-               if (dev->type == ARPHRD_CAN && dev->ml_priv){
+               if (dev->type == ARPHRD_CAN && dev->ml_priv) {
 
                        struct dev_rcv_lists *d = dev->ml_priv;
 
index 117814a7e73c720b20026289f4349528e8ed6f6a..3ee690e8c7d32354a525ad398291b7b7c5155215 100644 (file)
@@ -778,8 +778,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
        return 0;
 }
 
-static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh,
-                         void *arg)
+static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
 {
        struct rtcanmsg *r;
        struct cgw_job *gwj;
@@ -868,7 +867,7 @@ static void cgw_remove_all_jobs(void)
        }
 }
 
-static int cgw_remove_job(struct sk_buff *skb,  struct nlmsghdr *nlh, void *arg)
+static int cgw_remove_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
 {
        struct cgw_job *gwj = NULL;
        struct hlist_node *nx;
index c1764e41ddaf924fed4e31972b938a84bb87b6f7..1085e65f848eab9a3a634ad16b37d70bd80830b5 100644 (file)
@@ -711,9 +711,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
        err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
        if (err < 0)
                goto free_skb;
-       err = sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
-       if (err < 0)
-               goto free_skb;
+
+       sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
 
        skb->dev = dev;
        skb->sk  = sk;
index 368f9c3f9dc6505e693f56066da6dcb19ec2ebd6..ebba65d7e0da58b077bfe85a7c83f3805d52a55a 100644 (file)
@@ -749,7 +749,9 @@ unsigned int datagram_poll(struct file *file, struct socket *sock,
 
        /* exceptional events? */
        if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
-               mask |= POLLERR;
+               mask |= POLLERR |
+                       (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
+
        if (sk->sk_shutdown & RCV_SHUTDOWN)
                mask |= POLLRDHUP | POLLIN | POLLRDNORM;
        if (sk->sk_shutdown == SHUTDOWN_MASK)
index b24ab0e98eb4d8dd8cef2a20ef2a39b97d9ece92..9e26b8d9eafee123e661b519f90192450e9dd3c7 100644 (file)
@@ -200,7 +200,7 @@ static inline void rps_unlock(struct softnet_data *sd)
 }
 
 /* Device list insertion */
-static int list_netdevice(struct net_device *dev)
+static void list_netdevice(struct net_device *dev)
 {
        struct net *net = dev_net(dev);
 
@@ -214,8 +214,6 @@ static int list_netdevice(struct net_device *dev)
        write_unlock_bh(&dev_base_lock);
 
        dev_base_seq_inc(net);
-
-       return 0;
 }
 
 /* Device list removal
@@ -2210,30 +2208,40 @@ out:
 }
 EXPORT_SYMBOL(skb_checksum_help);
 
-/**
- *     skb_mac_gso_segment - mac layer segmentation handler.
- *     @skb: buffer to segment
- *     @features: features for the output path (see dev->features)
- */
-struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
-                                   netdev_features_t features)
+__be16 skb_network_protocol(struct sk_buff *skb)
 {
-       struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
-       struct packet_offload *ptype;
        __be16 type = skb->protocol;
        int vlan_depth = ETH_HLEN;
 
-       while (type == htons(ETH_P_8021Q)) {
+       while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
                struct vlan_hdr *vh;
 
                if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
-                       return ERR_PTR(-EINVAL);
+                       return 0;
 
                vh = (struct vlan_hdr *)(skb->data + vlan_depth);
                type = vh->h_vlan_encapsulated_proto;
                vlan_depth += VLAN_HLEN;
        }
 
+       return type;
+}
+
+/**
+ *     skb_mac_gso_segment - mac layer segmentation handler.
+ *     @skb: buffer to segment
+ *     @features: features for the output path (see dev->features)
+ */
+struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
+                                   netdev_features_t features)
+{
+       struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
+       struct packet_offload *ptype;
+       __be16 type = skb_network_protocol(skb);
+
+       if (unlikely(!type))
+               return ERR_PTR(-EINVAL);
+
        __skb_pull(skb, skb->mac_len);
 
        rcu_read_lock();
@@ -2400,24 +2408,12 @@ static int dev_gso_segment(struct sk_buff *skb, netdev_features_t features)
        return 0;
 }
 
-static 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)));
-}
-
 static netdev_features_t harmonize_features(struct sk_buff *skb,
        __be16 protocol, netdev_features_t features)
 {
        if (skb->ip_summed != CHECKSUM_NONE &&
            !can_checksum_protocol(features, protocol)) {
                features &= ~NETIF_F_ALL_CSUM;
-               features &= ~NETIF_F_SG;
        } else if (illegal_highdma(skb->dev, skb)) {
                features &= ~NETIF_F_SG;
        }
@@ -2433,20 +2429,22 @@ netdev_features_t netif_skb_features(struct sk_buff *skb)
        if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs)
                features &= ~NETIF_F_GSO_MASK;
 
-       if (protocol == htons(ETH_P_8021Q)) {
+       if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) {
                struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
                protocol = veh->h_vlan_encapsulated_proto;
        } else if (!vlan_tx_tag_present(skb)) {
                return harmonize_features(skb, protocol, features);
        }
 
-       features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_TX);
+       features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX |
+                                              NETIF_F_HW_VLAN_STAG_TX);
 
-       if (protocol != htons(ETH_P_8021Q)) {
+       if (protocol != htons(ETH_P_8021Q) && protocol != htons(ETH_P_8021AD)) {
                return harmonize_features(skb, protocol, features);
        } else {
                features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
-                               NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_TX;
+                               NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_CTAG_TX |
+                               NETIF_F_HW_VLAN_STAG_TX;
                return harmonize_features(skb, protocol, features);
        }
 }
@@ -2487,8 +2485,9 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
                features = netif_skb_features(skb);
 
                if (vlan_tx_tag_present(skb) &&
-                   !(features & NETIF_F_HW_VLAN_TX)) {
-                       skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+                   !vlan_hw_offload_capable(features, skb->vlan_proto)) {
+                       skb = __vlan_put_tag(skb, skb->vlan_proto,
+                                            vlan_tx_tag_get(skb));
                        if (unlikely(!skb))
                                goto out;
 
@@ -2592,6 +2591,7 @@ static void qdisc_pkt_len_init(struct sk_buff *skb)
         */
        if (shinfo->gso_size)  {
                unsigned int hdr_len;
+               u16 gso_segs = shinfo->gso_segs;
 
                /* mac layer + network layer */
                hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
@@ -2601,7 +2601,12 @@ static void qdisc_pkt_len_init(struct sk_buff *skb)
                        hdr_len += tcp_hdrlen(skb);
                else
                        hdr_len += sizeof(struct udphdr);
-               qdisc_skb_cb(skb)->pkt_len += (shinfo->gso_segs - 1) * hdr_len;
+
+               if (shinfo->gso_type & SKB_GSO_DODGY)
+                       gso_segs = DIV_ROUND_UP(skb->len - hdr_len,
+                                               shinfo->gso_size);
+
+               qdisc_skb_cb(skb)->pkt_len += (gso_segs - 1) * hdr_len;
        }
 }
 
@@ -3329,7 +3334,7 @@ EXPORT_SYMBOL_GPL(netdev_rx_handler_register);
  *     netdev_rx_handler_unregister - unregister receive handler
  *     @dev: device to unregister a handler from
  *
- *     Unregister a receive hander from a device.
+ *     Unregister a receive handler from a device.
  *
  *     The caller must hold the rtnl_mutex.
  */
@@ -3358,6 +3363,7 @@ static bool skb_pfmemalloc_protocol(struct sk_buff *skb)
        case __constant_htons(ETH_P_IP):
        case __constant_htons(ETH_P_IPV6):
        case __constant_htons(ETH_P_8021Q):
+       case __constant_htons(ETH_P_8021AD):
                return true;
        default:
                return false;
@@ -3398,7 +3404,8 @@ another_round:
 
        __this_cpu_inc(softnet_data.processed);
 
-       if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
+       if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
+           skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
                skb = vlan_untag(skb);
                if (unlikely(!skb))
                        goto unlock;
@@ -4066,6 +4073,9 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
        napi->gro_list = NULL;
        napi->skb = NULL;
        napi->poll = poll;
+       if (weight > NAPI_POLL_WEIGHT)
+               pr_err_once("netif_napi_add() called with weight %d on device %s\n",
+                           weight, dev->name);
        napi->weight = weight;
        list_add(&napi->dev_list, &dev->napi_list);
        napi->dev = dev;
@@ -4927,20 +4937,25 @@ static netdev_features_t netdev_fix_features(struct net_device *dev,
                features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
        }
 
-       /* Fix illegal SG+CSUM combinations. */
-       if ((features & NETIF_F_SG) &&
-           !(features & NETIF_F_ALL_CSUM)) {
-               netdev_dbg(dev,
-                       "Dropping NETIF_F_SG since no checksum feature.\n");
-               features &= ~NETIF_F_SG;
-       }
-
        /* TSO requires that SG is present as well. */
        if ((features & NETIF_F_ALL_TSO) && !(features & NETIF_F_SG)) {
                netdev_dbg(dev, "Dropping TSO features since no SG feature.\n");
                features &= ~NETIF_F_ALL_TSO;
        }
 
+       if ((features & NETIF_F_TSO) && !(features & NETIF_F_HW_CSUM) &&
+                                       !(features & NETIF_F_IP_CSUM)) {
+               netdev_dbg(dev, "Dropping TSO features since no CSUM feature.\n");
+               features &= ~NETIF_F_TSO;
+               features &= ~NETIF_F_TSO_ECN;
+       }
+
+       if ((features & NETIF_F_TSO6) && !(features & NETIF_F_HW_CSUM) &&
+                                        !(features & NETIF_F_IPV6_CSUM)) {
+               netdev_dbg(dev, "Dropping TSO6 features since no CSUM feature.\n");
+               features &= ~NETIF_F_TSO6;
+       }
+
        /* TSO ECN requires that TSO is present as well. */
        if ((features & NETIF_F_ALL_TSO) == NETIF_F_TSO_ECN)
                features &= ~NETIF_F_TSO_ECN;
@@ -5171,7 +5186,8 @@ int register_netdevice(struct net_device *dev)
                }
        }
 
-       if (((dev->hw_features | dev->features) & NETIF_F_HW_VLAN_FILTER) &&
+       if (((dev->hw_features | dev->features) &
+            NETIF_F_HW_VLAN_CTAG_FILTER) &&
            (!dev->netdev_ops->ndo_vlan_rx_add_vid ||
             !dev->netdev_ops->ndo_vlan_rx_kill_vid)) {
                netdev_WARN(dev, "Buggy VLAN acceleration in driver!\n");
@@ -5208,6 +5224,10 @@ int register_netdevice(struct net_device *dev)
         */
        dev->vlan_features |= NETIF_F_HIGHDMA;
 
+       /* Make NETIF_F_SG inheritable to tunnel devices.
+        */
+       dev->hw_enc_features |= NETIF_F_SG;
+
        ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);
        ret = notifier_to_errno(ret);
        if (ret)
index abdc9e6ef33e988ed3e2e00fada13b4ac3147143..c013f38482a1ef6572a9de27a2aa39946971b40b 100644 (file)
@@ -22,7 +22,8 @@
 
 static int __hw_addr_create_ex(struct netdev_hw_addr_list *list,
                               const unsigned char *addr, int addr_len,
-                              unsigned char addr_type, bool global)
+                              unsigned char addr_type, bool global,
+                              bool sync)
 {
        struct netdev_hw_addr *ha;
        int alloc_size;
@@ -37,7 +38,7 @@ static int __hw_addr_create_ex(struct netdev_hw_addr_list *list,
        ha->type = addr_type;
        ha->refcount = 1;
        ha->global_use = global;
-       ha->synced = 0;
+       ha->synced = sync;
        list_add_tail_rcu(&ha->list, &list->list);
        list->count++;
 
@@ -46,7 +47,7 @@ static int __hw_addr_create_ex(struct netdev_hw_addr_list *list,
 
 static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
                            const unsigned char *addr, int addr_len,
-                           unsigned char addr_type, bool global)
+                           unsigned char addr_type, bool global, bool sync)
 {
        struct netdev_hw_addr *ha;
 
@@ -63,43 +64,62 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
                                else
                                        ha->global_use = true;
                        }
+                       if (sync) {
+                               if (ha->synced)
+                                       return 0;
+                               else
+                                       ha->synced = true;
+                       }
                        ha->refcount++;
                        return 0;
                }
        }
 
-       return __hw_addr_create_ex(list, addr, addr_len, addr_type, global);
+       return __hw_addr_create_ex(list, addr, addr_len, addr_type, global,
+                                  sync);
 }
 
 static int __hw_addr_add(struct netdev_hw_addr_list *list,
                         const unsigned char *addr, int addr_len,
                         unsigned char addr_type)
 {
-       return __hw_addr_add_ex(list, addr, addr_len, addr_type, false);
+       return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false);
+}
+
+static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
+                              struct netdev_hw_addr *ha, bool global,
+                              bool sync)
+{
+       if (global && !ha->global_use)
+               return -ENOENT;
+
+       if (sync && !ha->synced)
+               return -ENOENT;
+
+       if (global)
+               ha->global_use = false;
+
+       if (sync)
+               ha->synced = false;
+
+       if (--ha->refcount)
+               return 0;
+       list_del_rcu(&ha->list);
+       kfree_rcu(ha, rcu_head);
+       list->count--;
+       return 0;
 }
 
 static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
                            const unsigned char *addr, int addr_len,
-                           unsigned char addr_type, bool global)
+                           unsigned char addr_type, bool global, bool sync)
 {
        struct netdev_hw_addr *ha;
 
        list_for_each_entry(ha, &list->list, list) {
                if (!memcmp(ha->addr, addr, addr_len) &&
-                   (ha->type == addr_type || !addr_type)) {
-                       if (global) {
-                               if (!ha->global_use)
-                                       break;
-                               else
-                                       ha->global_use = false;
-                       }
-                       if (--ha->refcount)
-                               return 0;
-                       list_del_rcu(&ha->list);
-                       kfree_rcu(ha, rcu_head);
-                       list->count--;
-                       return 0;
-               }
+                   (ha->type == addr_type || !addr_type))
+                       return __hw_addr_del_entry(list, ha, global, sync);
        }
        return -ENOENT;
 }
@@ -108,7 +128,57 @@ static int __hw_addr_del(struct netdev_hw_addr_list *list,
                         const unsigned char *addr, int addr_len,
                         unsigned char addr_type)
 {
-       return __hw_addr_del_ex(list, addr, addr_len, addr_type, false);
+       return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
+}
+
+static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
+                              struct netdev_hw_addr *ha,
+                              int addr_len)
+{
+       int err;
+
+       err = __hw_addr_add_ex(to_list, ha->addr, addr_len, ha->type,
+                              false, true);
+       if (err)
+               return err;
+       ha->sync_cnt++;
+       ha->refcount++;
+
+       return 0;
+}
+
+static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
+                                struct netdev_hw_addr_list *from_list,
+                                struct netdev_hw_addr *ha,
+                                int addr_len)
+{
+       int err;
+
+       err = __hw_addr_del_ex(to_list, ha->addr, addr_len, ha->type,
+                              false, true);
+       if (err)
+               return;
+       ha->sync_cnt--;
+       __hw_addr_del_entry(from_list, ha, false, true);
+}
+
+static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
+                                  struct netdev_hw_addr_list *from_list,
+                                  int addr_len)
+{
+       int err = 0;
+       struct netdev_hw_addr *ha, *tmp;
+
+       list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
+               if (ha->sync_cnt == ha->refcount) {
+                       __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
+               } else {
+                       err = __hw_addr_sync_one(to_list, ha, addr_len);
+                       if (err)
+                               break;
+               }
+       }
+       return err;
 }
 
 int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list,
@@ -152,6 +222,11 @@ void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list,
 }
 EXPORT_SYMBOL(__hw_addr_del_multiple);
 
+/* This function only works where there is a strict 1-1 relationship
+ * between source and destionation of they synch. If you ever need to
+ * sync addresses to more then 1 destination, you need to use
+ * __hw_addr_sync_multiple().
+ */
 int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
                   struct netdev_hw_addr_list *from_list,
                   int addr_len)
@@ -160,17 +235,12 @@ int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
        struct netdev_hw_addr *ha, *tmp;
 
        list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
-               if (!ha->synced) {
-                       err = __hw_addr_add(to_list, ha->addr,
-                                           addr_len, ha->type);
+               if (!ha->sync_cnt) {
+                       err = __hw_addr_sync_one(to_list, ha, addr_len);
                        if (err)
                                break;
-                       ha->synced++;
-                       ha->refcount++;
-               } else if (ha->refcount == 1) {
-                       __hw_addr_del(to_list, ha->addr, addr_len, ha->type);
-                       __hw_addr_del(from_list, ha->addr, addr_len, ha->type);
-               }
+               } else if (ha->refcount == 1)
+                       __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
        }
        return err;
 }
@@ -183,13 +253,8 @@ void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
        struct netdev_hw_addr *ha, *tmp;
 
        list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
-               if (ha->synced) {
-                       __hw_addr_del(to_list, ha->addr,
-                                     addr_len, ha->type);
-                       ha->synced--;
-                       __hw_addr_del(from_list, ha->addr,
-                                     addr_len, ha->type);
-               }
+               if (ha->sync_cnt)
+                       __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
        }
 }
 EXPORT_SYMBOL(__hw_addr_unsync);
@@ -406,7 +471,7 @@ int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr)
                }
        }
        err = __hw_addr_create_ex(&dev->uc, addr, dev->addr_len,
-                                 NETDEV_HW_ADDR_T_UNICAST, true);
+                                 NETDEV_HW_ADDR_T_UNICAST, true, false);
        if (!err)
                __dev_set_rx_mode(dev);
 out:
@@ -469,7 +534,8 @@ EXPORT_SYMBOL(dev_uc_del);
  *     locked by netif_addr_lock_bh.
  *
  *     This function is intended to be called from the dev->set_rx_mode
- *     function of layered software devices.
+ *     function of layered software devices.  This function assumes that
+ *     addresses will only ever be synced to the @to devices and no other.
  */
 int dev_uc_sync(struct net_device *to, struct net_device *from)
 {
@@ -487,6 +553,36 @@ int dev_uc_sync(struct net_device *to, struct net_device *from)
 }
 EXPORT_SYMBOL(dev_uc_sync);
 
+/**
+ *     dev_uc_sync_multiple - Synchronize device's unicast list to another
+ *     device, but allow for multiple calls to sync to multiple devices.
+ *     @to: destination device
+ *     @from: source device
+ *
+ *     Add newly added addresses to the destination device and release
+ *     addresses that have been deleted from the source. The source device
+ *     must be locked by netif_addr_lock_bh.
+ *
+ *     This function is intended to be called from the dev->set_rx_mode
+ *     function of layered software devices.  It allows for a single source
+ *     device to be synced to multiple destination devices.
+ */
+int dev_uc_sync_multiple(struct net_device *to, struct net_device *from)
+{
+       int err = 0;
+
+       if (to->addr_len != from->addr_len)
+               return -EINVAL;
+
+       netif_addr_lock_nested(to);
+       err = __hw_addr_sync_multiple(&to->uc, &from->uc, to->addr_len);
+       if (!err)
+               __dev_set_rx_mode(to);
+       netif_addr_unlock(to);
+       return err;
+}
+EXPORT_SYMBOL(dev_uc_sync_multiple);
+
 /**
  *     dev_uc_unsync - Remove synchronized addresses from the destination device
  *     @to: destination device
@@ -559,7 +655,7 @@ int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr)
                }
        }
        err = __hw_addr_create_ex(&dev->mc, addr, dev->addr_len,
-                                 NETDEV_HW_ADDR_T_MULTICAST, true);
+                                 NETDEV_HW_ADDR_T_MULTICAST, true, false);
        if (!err)
                __dev_set_rx_mode(dev);
 out:
@@ -575,7 +671,7 @@ static int __dev_mc_add(struct net_device *dev, const unsigned char *addr,
 
        netif_addr_lock_bh(dev);
        err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len,
-                              NETDEV_HW_ADDR_T_MULTICAST, global);
+                              NETDEV_HW_ADDR_T_MULTICAST, global, false);
        if (!err)
                __dev_set_rx_mode(dev);
        netif_addr_unlock_bh(dev);
@@ -615,7 +711,7 @@ static int __dev_mc_del(struct net_device *dev, const unsigned char *addr,
 
        netif_addr_lock_bh(dev);
        err = __hw_addr_del_ex(&dev->mc, addr, dev->addr_len,
-                              NETDEV_HW_ADDR_T_MULTICAST, global);
+                              NETDEV_HW_ADDR_T_MULTICAST, global, false);
        if (!err)
                __dev_set_rx_mode(dev);
        netif_addr_unlock_bh(dev);
@@ -678,6 +774,36 @@ int dev_mc_sync(struct net_device *to, struct net_device *from)
 }
 EXPORT_SYMBOL(dev_mc_sync);
 
+/**
+ *     dev_mc_sync_multiple - Synchronize device's unicast list to another
+ *     device, but allow for multiple calls to sync to multiple devices.
+ *     @to: destination device
+ *     @from: source device
+ *
+ *     Add newly added addresses to the destination device and release
+ *     addresses that have no users left. The source device must be
+ *     locked by netif_addr_lock_bh.
+ *
+ *     This function is intended to be called from the ndo_set_rx_mode
+ *     function of layered software devices.  It allows for a single
+ *     source device to be synced to multiple destination devices.
+ */
+int dev_mc_sync_multiple(struct net_device *to, struct net_device *from)
+{
+       int err = 0;
+
+       if (to->addr_len != from->addr_len)
+               return -EINVAL;
+
+       netif_addr_lock_nested(to);
+       err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len);
+       if (!err)
+               __dev_set_rx_mode(to);
+       netif_addr_unlock(to);
+       return err;
+}
+EXPORT_SYMBOL(dev_mc_sync_multiple);
+
 /**
  *     dev_mc_unsync - Remove synchronized addresses from the destination device
  *     @to: destination device
index 35fd12f1a69c38ad899a12392fcdfc768081bdb9..df9cc810ec8e3a78bbdb3b4480deeb8fd35df721 100644 (file)
@@ -320,27 +320,28 @@ void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old)
 EXPORT_SYMBOL(__dst_destroy_metrics_generic);
 
 /**
- * skb_dst_set_noref - sets skb dst, without a reference
+ * __skb_dst_set_noref - sets skb dst, without a reference
  * @skb: buffer
  * @dst: dst entry
+ * @force: if force is set, use noref version even for DST_NOCACHE entries
  *
  * Sets skb dst, assuming a reference was not taken on dst
  * skb_dst_drop() should not dst_release() this dst
  */
-void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst)
+void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst, bool force)
 {
        WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
        /* If dst not in cache, we must take a reference, because
         * dst_release() will destroy dst as soon as its refcount becomes zero
         */
-       if (unlikely(dst->flags & DST_NOCACHE)) {
+       if (unlikely((dst->flags & DST_NOCACHE) && !force)) {
                dst_hold(dst);
                skb_dst_set(skb, dst);
        } else {
                skb->_skb_refdst = (unsigned long)dst | SKB_DST_NOREF;
        }
 }
-EXPORT_SYMBOL(skb_dst_set_noref);
+EXPORT_SYMBOL(__skb_dst_set_noref);
 
 /* Dirty hack. We did it in 2.2 (in __dst_free),
  * we have _very_ good reasons not to repeat
index 3e9b2c3e30f063cb3e59545f2af31176ea7b0151..5a934ef90f8bc7a70a5236aa1d784dc1f5ff315f 100644 (file)
@@ -60,10 +60,13 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
        [NETIF_F_IPV6_CSUM_BIT] =        "tx-checksum-ipv6",
        [NETIF_F_HIGHDMA_BIT] =          "highdma",
        [NETIF_F_FRAGLIST_BIT] =         "tx-scatter-gather-fraglist",
-       [NETIF_F_HW_VLAN_TX_BIT] =       "tx-vlan-hw-insert",
+       [NETIF_F_HW_VLAN_CTAG_TX_BIT] =  "tx-vlan-ctag-hw-insert",
 
-       [NETIF_F_HW_VLAN_RX_BIT] =       "rx-vlan-hw-parse",
-       [NETIF_F_HW_VLAN_FILTER_BIT] =   "rx-vlan-filter",
+       [NETIF_F_HW_VLAN_CTAG_RX_BIT] =  "rx-vlan-ctag-hw-parse",
+       [NETIF_F_HW_VLAN_CTAG_FILTER_BIT] = "rx-vlan-ctag-filter",
+       [NETIF_F_HW_VLAN_STAG_TX_BIT] =  "tx-vlan-stag-hw-insert",
+       [NETIF_F_HW_VLAN_STAG_RX_BIT] =  "rx-vlan-stag-hw-parse",
+       [NETIF_F_HW_VLAN_STAG_FILTER_BIT] = "rx-vlan-stag-filter",
        [NETIF_F_VLAN_CHALLENGED_BIT] =  "vlan-challenged",
        [NETIF_F_GSO_BIT] =              "tx-generic-segmentation",
        [NETIF_F_LLTX_BIT] =             "tx-lockless",
@@ -78,6 +81,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
        [NETIF_F_TSO6_BIT] =             "tx-tcp6-segmentation",
        [NETIF_F_FSO_BIT] =              "tx-fcoe-segmentation",
        [NETIF_F_GSO_GRE_BIT] =          "tx-gre-segmentation",
+       [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",
@@ -266,18 +270,19 @@ static int ethtool_set_one_feature(struct net_device *dev,
 
 #define ETH_ALL_FLAGS    (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | \
                          ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH)
-#define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_RX | \
-                         NETIF_F_HW_VLAN_TX | NETIF_F_NTUPLE | NETIF_F_RXHASH)
+#define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_CTAG_RX | \
+                         NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_NTUPLE | \
+                         NETIF_F_RXHASH)
 
 static u32 __ethtool_get_flags(struct net_device *dev)
 {
        u32 flags = 0;
 
-       if (dev->features & NETIF_F_LRO)        flags |= ETH_FLAG_LRO;
-       if (dev->features & NETIF_F_HW_VLAN_RX) flags |= ETH_FLAG_RXVLAN;
-       if (dev->features & NETIF_F_HW_VLAN_TX) flags |= ETH_FLAG_TXVLAN;
-       if (dev->features & NETIF_F_NTUPLE)     flags |= ETH_FLAG_NTUPLE;
-       if (dev->features & NETIF_F_RXHASH)     flags |= ETH_FLAG_RXHASH;
+       if (dev->features & NETIF_F_LRO)             flags |= ETH_FLAG_LRO;
+       if (dev->features & NETIF_F_HW_VLAN_CTAG_RX) flags |= ETH_FLAG_RXVLAN;
+       if (dev->features & NETIF_F_HW_VLAN_CTAG_TX) flags |= ETH_FLAG_TXVLAN;
+       if (dev->features & NETIF_F_NTUPLE)          flags |= ETH_FLAG_NTUPLE;
+       if (dev->features & NETIF_F_RXHASH)          flags |= ETH_FLAG_RXHASH;
 
        return flags;
 }
@@ -290,8 +295,8 @@ static int __ethtool_set_flags(struct net_device *dev, u32 data)
                return -EINVAL;
 
        if (data & ETH_FLAG_LRO)        features |= NETIF_F_LRO;
-       if (data & ETH_FLAG_RXVLAN)     features |= NETIF_F_HW_VLAN_RX;
-       if (data & ETH_FLAG_TXVLAN)     features |= NETIF_F_HW_VLAN_TX;
+       if (data & ETH_FLAG_RXVLAN)     features |= NETIF_F_HW_VLAN_CTAG_RX;
+       if (data & ETH_FLAG_TXVLAN)     features |= NETIF_F_HW_VLAN_CTAG_TX;
        if (data & ETH_FLAG_NTUPLE)     features |= NETIF_F_NTUPLE;
        if (data & ETH_FLAG_RXHASH)     features |= NETIF_F_RXHASH;
 
index 58a4ba27dfe3117d439ada122000e1339a23f48f..d5a9f8ead0d864305110f2ca6f1dc70fca5b84cd 100644 (file)
@@ -266,7 +266,7 @@ errout:
        return err;
 }
 
-static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct fib_rule_hdr *frh = nlmsg_data(nlh);
@@ -415,7 +415,7 @@ errout:
        return err;
 }
 
-static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct fib_rule_hdr *frh = nlmsg_data(nlh);
index 2e20b55a7830c43e25bdd4221cc134f374d7148e..dad2a178f9f8a477488f091962c5e771d1d117b3 100644 (file)
@@ -348,6 +348,9 @@ load_b:
                case BPF_S_ANC_VLAN_TAG_PRESENT:
                        A = !!vlan_tx_tag_present(skb);
                        continue;
+               case BPF_S_ANC_PAY_OFFSET:
+                       A = __skb_get_poff(skb);
+                       continue;
                case BPF_S_ANC_NLATTR: {
                        struct nlattr *nla;
 
@@ -612,6 +615,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
                        ANCILLARY(ALU_XOR_X);
                        ANCILLARY(VLAN_TAG);
                        ANCILLARY(VLAN_TAG_PRESENT);
+                       ANCILLARY(PAY_OFFSET);
                        }
 
                        /* ancillary operation unknown or unsupported */
@@ -814,6 +818,7 @@ static void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to)
                [BPF_S_ANC_SECCOMP_LD_W] = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_ANC_VLAN_TAG]    = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS,
+               [BPF_S_ANC_PAY_OFFSET]  = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_LD_W_LEN]        = BPF_LD|BPF_W|BPF_LEN,
                [BPF_S_LD_W_IND]        = BPF_LD|BPF_W|BPF_IND,
                [BPF_S_LD_H_IND]        = BPF_LD|BPF_H|BPF_IND,
index 2bfd081c59f7706cbd9d8a5ba8a5780aaaae23d1..7102f166482d326f4135556bb8000f75fadf8b21 100644 (file)
@@ -323,6 +323,24 @@ static void flow_cache_flush_tasklet(unsigned long data)
                complete(&info->completion);
 }
 
+/*
+ * Return whether a cpu needs flushing.  Conservatively, we assume
+ * the presence of any entries means the core may require flushing,
+ * since the flow_cache_ops.check() function may assume it's running
+ * on the same core as the per-cpu cache component.
+ */
+static int flow_cache_percpu_empty(struct flow_cache *fc, int cpu)
+{
+       struct flow_cache_percpu *fcp;
+       int i;
+
+       fcp = per_cpu_ptr(fc->percpu, cpu);
+       for (i = 0; i < flow_cache_hash_size(fc); i++)
+               if (!hlist_empty(&fcp->hash_table[i]))
+                       return 0;
+       return 1;
+}
+
 static void flow_cache_flush_per_cpu(void *data)
 {
        struct flow_flush_info *info = data;
@@ -337,22 +355,40 @@ void flow_cache_flush(void)
 {
        struct flow_flush_info info;
        static DEFINE_MUTEX(flow_flush_sem);
+       cpumask_var_t mask;
+       int i, self;
+
+       /* Track which cpus need flushing to avoid disturbing all cores. */
+       if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+               return;
+       cpumask_clear(mask);
 
        /* Don't want cpus going down or up during this. */
        get_online_cpus();
        mutex_lock(&flow_flush_sem);
        info.cache = &flow_cache_global;
-       atomic_set(&info.cpuleft, num_online_cpus());
+       for_each_online_cpu(i)
+               if (!flow_cache_percpu_empty(info.cache, i))
+                       cpumask_set_cpu(i, mask);
+       atomic_set(&info.cpuleft, cpumask_weight(mask));
+       if (atomic_read(&info.cpuleft) == 0)
+               goto done;
+
        init_completion(&info.completion);
 
        local_bh_disable();
-       smp_call_function(flow_cache_flush_per_cpu, &info, 0);
-       flow_cache_flush_tasklet((unsigned long)&info);
+       self = cpumask_test_and_clear_cpu(smp_processor_id(), mask);
+       on_each_cpu_mask(mask, flow_cache_flush_per_cpu, &info, 0);
+       if (self)
+               flow_cache_flush_tasklet((unsigned long)&info);
        local_bh_enable();
 
        wait_for_completion(&info.completion);
+
+done:
        mutex_unlock(&flow_flush_sem);
        put_online_cpus();
+       free_cpumask_var(mask);
 }
 
 static void flow_cache_flush_task(struct work_struct *work)
index e187bf06d673b0ec37d401a8389bba372a8fb3d9..00ee068efc1c2374bc0195d9458eca00392359c6 100644 (file)
@@ -5,6 +5,10 @@
 #include <linux/if_vlan.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
+#include <linux/igmp.h>
+#include <linux/icmp.h>
+#include <linux/sctp.h>
+#include <linux/dccp.h>
 #include <linux/if_tunnel.h>
 #include <linux/if_pppox.h>
 #include <linux/ppp_defs.h>
@@ -119,6 +123,17 @@ ipv6:
                                nhoff += 4;
                        if (hdr->flags & GRE_SEQ)
                                nhoff += 4;
+                       if (proto == htons(ETH_P_TEB)) {
+                               const struct ethhdr *eth;
+                               struct ethhdr _eth;
+
+                               eth = skb_header_pointer(skb, nhoff,
+                                                        sizeof(_eth), &_eth);
+                               if (!eth)
+                                       return false;
+                               proto = eth->h_proto;
+                               nhoff += sizeof(*eth);
+                       }
                        goto again;
                }
                break;
@@ -217,6 +232,59 @@ u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb,
 }
 EXPORT_SYMBOL(__skb_tx_hash);
 
+/* __skb_get_poff() returns the offset to the payload as far as it could
+ * be dissected. The main user is currently BPF, so that we can dynamically
+ * truncate packets without needing to push actual payload to the user
+ * space and can analyze headers only, instead.
+ */
+u32 __skb_get_poff(const struct sk_buff *skb)
+{
+       struct flow_keys keys;
+       u32 poff = 0;
+
+       if (!skb_flow_dissect(skb, &keys))
+               return 0;
+
+       poff += keys.thoff;
+       switch (keys.ip_proto) {
+       case IPPROTO_TCP: {
+               const struct tcphdr *tcph;
+               struct tcphdr _tcph;
+
+               tcph = skb_header_pointer(skb, poff, sizeof(_tcph), &_tcph);
+               if (!tcph)
+                       return poff;
+
+               poff += max_t(u32, sizeof(struct tcphdr), tcph->doff * 4);
+               break;
+       }
+       case IPPROTO_UDP:
+       case IPPROTO_UDPLITE:
+               poff += sizeof(struct udphdr);
+               break;
+       /* For the rest, we do not really care about header
+        * extensions at this point for now.
+        */
+       case IPPROTO_ICMP:
+               poff += sizeof(struct icmphdr);
+               break;
+       case IPPROTO_ICMPV6:
+               poff += sizeof(struct icmp6hdr);
+               break;
+       case IPPROTO_IGMP:
+               poff += sizeof(struct igmphdr);
+               break;
+       case IPPROTO_DCCP:
+               poff += sizeof(struct dccp_hdr);
+               break;
+       case IPPROTO_SCTP:
+               poff += sizeof(struct sctphdr);
+               break;
+       }
+
+       return poff;
+}
+
 static inline u16 dev_cap_txqueue(struct net_device *dev, u16 queue_index)
 {
        if (unlikely(queue_index >= dev->real_num_tx_queues)) {
index 3863b8f639c50827dfdf4a0c6c26bd6284f39f58..89a3a07d85fbd3318ff03c255a51cf0aaeacbe7b 100644 (file)
 #include <linux/string.h>
 #include <linux/log2.h>
 
+#define DEBUG
 #define NEIGH_DEBUG 1
-
-#define NEIGH_PRINTK(x...) printk(x)
-#define NEIGH_NOPRINTK(x...) do { ; } while(0)
-#define NEIGH_PRINTK1 NEIGH_NOPRINTK
-#define NEIGH_PRINTK2 NEIGH_NOPRINTK
-
-#if NEIGH_DEBUG >= 1
-#undef NEIGH_PRINTK1
-#define NEIGH_PRINTK1 NEIGH_PRINTK
-#endif
-#if NEIGH_DEBUG >= 2
-#undef NEIGH_PRINTK2
-#define NEIGH_PRINTK2 NEIGH_PRINTK
-#endif
+#define neigh_dbg(level, fmt, ...)             \
+do {                                           \
+       if (level <= NEIGH_DEBUG)               \
+               pr_debug(fmt, ##__VA_ARGS__);   \
+} while (0)
 
 #define PNEIGH_HASHMASK                0xF
 
@@ -246,7 +238,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
                                        n->nud_state = NUD_NOARP;
                                else
                                        n->nud_state = NUD_NONE;
-                               NEIGH_PRINTK2("neigh %p is stray.\n", n);
+                               neigh_dbg(2, "neigh %p is stray\n", n);
                        }
                        write_unlock(&n->lock);
                        neigh_cleanup_and_release(n);
@@ -542,7 +534,7 @@ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
                                                     lockdep_is_held(&tbl->lock)));
        rcu_assign_pointer(nht->hash_buckets[hash_val], n);
        write_unlock_bh(&tbl->lock);
-       NEIGH_PRINTK2("neigh %p is created.\n", n);
+       neigh_dbg(2, "neigh %p is created\n", n);
        rc = n;
 out:
        return rc;
@@ -725,7 +717,7 @@ void neigh_destroy(struct neighbour *neigh)
        dev_put(dev);
        neigh_parms_put(neigh->parms);
 
-       NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh);
+       neigh_dbg(2, "neigh %p is destroyed\n", neigh);
 
        atomic_dec(&neigh->tbl->entries);
        kfree_rcu(neigh, rcu);
@@ -739,7 +731,7 @@ EXPORT_SYMBOL(neigh_destroy);
  */
 static void neigh_suspect(struct neighbour *neigh)
 {
-       NEIGH_PRINTK2("neigh %p is suspected.\n", neigh);
+       neigh_dbg(2, "neigh %p is suspected\n", neigh);
 
        neigh->output = neigh->ops->output;
 }
@@ -751,7 +743,7 @@ static void neigh_suspect(struct neighbour *neigh)
  */
 static void neigh_connect(struct neighbour *neigh)
 {
-       NEIGH_PRINTK2("neigh %p is connected.\n", neigh);
+       neigh_dbg(2, "neigh %p is connected\n", neigh);
 
        neigh->output = neigh->ops->connected_output;
 }
@@ -852,7 +844,7 @@ static void neigh_invalidate(struct neighbour *neigh)
        struct sk_buff *skb;
 
        NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
-       NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
+       neigh_dbg(2, "neigh %p is failed\n", neigh);
        neigh->updated = jiffies;
 
        /* It is very thin place. report_unreachable is very complicated
@@ -904,17 +896,17 @@ static void neigh_timer_handler(unsigned long arg)
        if (state & NUD_REACHABLE) {
                if (time_before_eq(now,
                                   neigh->confirmed + neigh->parms->reachable_time)) {
-                       NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
+                       neigh_dbg(2, "neigh %p is still alive\n", neigh);
                        next = neigh->confirmed + neigh->parms->reachable_time;
                } else if (time_before_eq(now,
                                          neigh->used + neigh->parms->delay_probe_time)) {
-                       NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
+                       neigh_dbg(2, "neigh %p is delayed\n", neigh);
                        neigh->nud_state = NUD_DELAY;
                        neigh->updated = jiffies;
                        neigh_suspect(neigh);
                        next = now + neigh->parms->delay_probe_time;
                } else {
-                       NEIGH_PRINTK2("neigh %p is suspected.\n", neigh);
+                       neigh_dbg(2, "neigh %p is suspected\n", neigh);
                        neigh->nud_state = NUD_STALE;
                        neigh->updated = jiffies;
                        neigh_suspect(neigh);
@@ -923,14 +915,14 @@ static void neigh_timer_handler(unsigned long arg)
        } else if (state & NUD_DELAY) {
                if (time_before_eq(now,
                                   neigh->confirmed + neigh->parms->delay_probe_time)) {
-                       NEIGH_PRINTK2("neigh %p is now reachable.\n", neigh);
+                       neigh_dbg(2, "neigh %p is now reachable\n", neigh);
                        neigh->nud_state = NUD_REACHABLE;
                        neigh->updated = jiffies;
                        neigh_connect(neigh);
                        notify = 1;
                        next = neigh->confirmed + neigh->parms->reachable_time;
                } else {
-                       NEIGH_PRINTK2("neigh %p is probed.\n", neigh);
+                       neigh_dbg(2, "neigh %p is probed\n", neigh);
                        neigh->nud_state = NUD_PROBE;
                        neigh->updated = jiffies;
                        atomic_set(&neigh->probes, 0);
@@ -997,7 +989,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
                        return 1;
                }
        } else if (neigh->nud_state & NUD_STALE) {
-               NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
+               neigh_dbg(2, "neigh %p is delayed\n", neigh);
                neigh->nud_state = NUD_DELAY;
                neigh->updated = jiffies;
                neigh_add_timer(neigh,
@@ -1320,8 +1312,7 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
 out:
        return rc;
 discard:
-       NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n",
-                     dst, neigh);
+       neigh_dbg(1, "%s: dst=%p neigh=%p\n", __func__, dst, neigh);
 out_kfree_skb:
        rc = -EINVAL;
        kfree_skb(skb);
@@ -1498,7 +1489,7 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
                }
        }
        write_unlock_bh(&tbl->lock);
-       NEIGH_PRINTK1("neigh_parms_release: not found\n");
+       neigh_dbg(1, "%s: not found\n", __func__);
 }
 EXPORT_SYMBOL(neigh_parms_release);
 
@@ -1613,7 +1604,7 @@ int neigh_table_clear(struct neigh_table *tbl)
 }
 EXPORT_SYMBOL(neigh_table_clear);
 
-static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ndmsg *ndm;
@@ -1677,7 +1668,7 @@ out:
        return err;
 }
 
-static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ndmsg *ndm;
@@ -1955,7 +1946,7 @@ static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = {
        [NDTPA_LOCKTIME]                = { .type = NLA_U64 },
 };
 
-static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct neigh_table *tbl;
index 3174f1998ee6b33509bd606df6a4890e24420f6b..569d355fec3e8adac815c1819e43abbac98fcf39 100644 (file)
@@ -271,7 +271,7 @@ static int ptype_seq_show(struct seq_file *seq, void *v)
                else
                        seq_printf(seq, "%04x", ntohs(pt->type));
 
-               seq_printf(seq, " %-8s %pF\n",
+               seq_printf(seq, " %-8s %pf\n",
                           pt->dev ? pt->dev->name : "", pt->func);
        }
 
index fa32899006a275e475e8b2b43c88fbfb72efd5dc..209d84253dd50ae02b2b9f2b616cc7ff8d013efe 100644 (file)
@@ -47,7 +47,7 @@ static struct sk_buff_head skb_pool;
 
 static atomic_t trapped;
 
-static struct srcu_struct netpoll_srcu;
+DEFINE_STATIC_SRCU(netpoll_srcu);
 
 #define USEC_PER_POLL  50
 #define NETPOLL_RX_ENABLED  1
@@ -383,8 +383,9 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
                        if (__netif_tx_trylock(txq)) {
                                if (!netif_xmit_stopped(txq)) {
                                        if (vlan_tx_tag_present(skb) &&
-                                           !(netif_skb_features(skb) & NETIF_F_HW_VLAN_TX)) {
-                                               skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+                                           !vlan_hw_offload_capable(netif_skb_features(skb),
+                                                                    skb->vlan_proto)) {
+                                               skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb));
                                                if (unlikely(!skb))
                                                        break;
                                                skb->vlan_tci = 0;
@@ -1212,7 +1213,6 @@ EXPORT_SYMBOL(netpoll_setup);
 static int __init netpoll_init(void)
 {
        skb_queue_head_init(&skb_pool);
-       init_srcu_struct(&netpoll_srcu);
        return 0;
 }
 core_initcall(netpoll_init);
index 23854b51a259cb8b65c2bb1ee4be8e32e0ca0eb8..18af08a73f0a719ecaf3e56ae0f89783c8d90f77 100644 (file)
@@ -517,32 +517,6 @@ out:
        return err;
 }
 
-static const int rtm_min[RTM_NR_FAMILIES] =
-{
-       [RTM_FAM(RTM_NEWLINK)]      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-       [RTM_FAM(RTM_NEWADDR)]      = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
-       [RTM_FAM(RTM_NEWROUTE)]     = NLMSG_LENGTH(sizeof(struct rtmsg)),
-       [RTM_FAM(RTM_NEWRULE)]      = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)),
-       [RTM_FAM(RTM_NEWQDISC)]     = NLMSG_LENGTH(sizeof(struct tcmsg)),
-       [RTM_FAM(RTM_NEWTCLASS)]    = NLMSG_LENGTH(sizeof(struct tcmsg)),
-       [RTM_FAM(RTM_NEWTFILTER)]   = NLMSG_LENGTH(sizeof(struct tcmsg)),
-       [RTM_FAM(RTM_NEWACTION)]    = NLMSG_LENGTH(sizeof(struct tcamsg)),
-       [RTM_FAM(RTM_GETMULTICAST)] = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
-       [RTM_FAM(RTM_GETANYCAST)]   = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
-};
-
-static const int rta_max[RTM_NR_FAMILIES] =
-{
-       [RTM_FAM(RTM_NEWLINK)]      = IFLA_MAX,
-       [RTM_FAM(RTM_NEWADDR)]      = IFA_MAX,
-       [RTM_FAM(RTM_NEWROUTE)]     = RTA_MAX,
-       [RTM_FAM(RTM_NEWRULE)]      = FRA_MAX,
-       [RTM_FAM(RTM_NEWQDISC)]     = TCA_MAX,
-       [RTM_FAM(RTM_NEWTCLASS)]    = TCA_MAX,
-       [RTM_FAM(RTM_NEWTFILTER)]   = TCA_MAX,
-       [RTM_FAM(RTM_NEWACTION)]    = TCAA_MAX,
-};
-
 int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, int echo)
 {
        struct sock *rtnl = net->rtnl;
@@ -1539,7 +1513,7 @@ errout:
        return err;
 }
 
-static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifinfomsg *ifm;
@@ -1580,7 +1554,7 @@ errout:
        return err;
 }
 
-static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        const struct rtnl_link_ops *ops;
@@ -1711,7 +1685,7 @@ static int rtnl_group_changelink(struct net *net, int group,
        return 0;
 }
 
-static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        const struct rtnl_link_ops *ops;
@@ -1866,7 +1840,7 @@ out:
        }
 }
 
-static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifinfomsg *ifm;
@@ -1957,8 +1931,11 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
                if (rtnl_msg_handlers[idx] == NULL ||
                    rtnl_msg_handlers[idx][type].dumpit == NULL)
                        continue;
-               if (idx > s_idx)
+               if (idx > s_idx) {
                        memset(&cb->args[0], 0, sizeof(cb->args));
+                       cb->prev_seq = 0;
+                       cb->seq = 0;
+               }
                if (rtnl_msg_handlers[idx][type].dumpit(skb, cb))
                        break;
        }
@@ -2051,7 +2028,39 @@ errout:
        rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
 }
 
-static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+/**
+ * ndo_dflt_fdb_add - default netdevice operation to add an FDB entry
+ */
+int ndo_dflt_fdb_add(struct ndmsg *ndm,
+                    struct nlattr *tb[],
+                    struct net_device *dev,
+                    const unsigned char *addr,
+                    u16 flags)
+{
+       int err = -EINVAL;
+
+       /* If aging addresses are supported device will need to
+        * implement its own handler for this.
+        */
+       if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) {
+               pr_info("%s: FDB only supports static addresses\n", dev->name);
+               return err;
+       }
+
+       if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
+               err = dev_uc_add_excl(dev, addr);
+       else if (is_multicast_ether_addr(addr))
+               err = dev_mc_add_excl(dev, addr);
+
+       /* Only return duplicate errors if NLM_F_EXCL is set */
+       if (err == -EEXIST && !(flags & NLM_F_EXCL))
+               err = 0;
+
+       return err;
+}
+EXPORT_SYMBOL(ndo_dflt_fdb_add);
+
+static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ndmsg *ndm;
@@ -2082,7 +2091,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        }
 
        addr = nla_data(tb[NDA_LLADDR]);
-       if (!is_valid_ether_addr(addr)) {
+       if (is_zero_ether_addr(addr)) {
                pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ether address\n");
                return -EINVAL;
        }
@@ -2103,10 +2112,13 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        }
 
        /* Embedded bridge, macvlan, and any other device support */
-       if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_add) {
-               err = dev->netdev_ops->ndo_fdb_add(ndm, tb,
-                                                  dev, addr,
-                                                  nlh->nlmsg_flags);
+       if ((ndm->ndm_flags & NTF_SELF)) {
+               if (dev->netdev_ops->ndo_fdb_add)
+                       err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr,
+                                                          nlh->nlmsg_flags);
+               else
+                       err = ndo_dflt_fdb_add(ndm, tb, dev, addr,
+                                              nlh->nlmsg_flags);
 
                if (!err) {
                        rtnl_fdb_notify(dev, addr, RTM_NEWNEIGH);
@@ -2117,7 +2129,36 @@ out:
        return err;
 }
 
-static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+/**
+ * ndo_dflt_fdb_del - default netdevice operation to delete an FDB entry
+ */
+int ndo_dflt_fdb_del(struct ndmsg *ndm,
+                    struct nlattr *tb[],
+                    struct net_device *dev,
+                    const unsigned char *addr)
+{
+       int err = -EOPNOTSUPP;
+
+       /* If aging addresses are supported device will need to
+        * implement its own handler for this.
+        */
+       if (ndm->ndm_state & NUD_PERMANENT) {
+               pr_info("%s: FDB only supports static addresses\n", dev->name);
+               return -EINVAL;
+       }
+
+       if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
+               err = dev_uc_del(dev, addr);
+       else if (is_multicast_ether_addr(addr))
+               err = dev_mc_del(dev, addr);
+       else
+               err = -EINVAL;
+
+       return err;
+}
+EXPORT_SYMBOL(ndo_dflt_fdb_del);
+
+static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ndmsg *ndm;
@@ -2174,8 +2215,11 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        }
 
        /* Embedded bridge, macvlan, and any other device support */
-       if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) {
-               err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
+       if (ndm->ndm_flags & NTF_SELF) {
+               if (dev->netdev_ops->ndo_fdb_del)
+                       err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
+               else
+                       err = ndo_dflt_fdb_del(ndm, tb, dev, addr);
 
                if (!err) {
                        rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);
@@ -2220,7 +2264,7 @@ skip:
  * @dev: netdevice
  *
  * Default netdevice operation to dump the existing unicast address list.
- * Returns zero on success.
+ * Returns number of addresses from list put in skb.
  */
 int ndo_dflt_fdb_dump(struct sk_buff *skb,
                      struct netlink_callback *cb,
@@ -2260,6 +2304,8 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
                if (dev->netdev_ops->ndo_fdb_dump)
                        idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx);
+               else
+                       idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
        }
        rcu_read_unlock();
 
@@ -2411,8 +2457,7 @@ errout:
        return err;
 }
 
-static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
-                              void *arg)
+static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifinfomsg *ifm;
@@ -2482,8 +2527,7 @@ out:
        return err;
 }
 
-static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
-                              void *arg)
+static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifinfomsg *ifm;
@@ -2553,10 +2597,6 @@ out:
        return err;
 }
 
-/* Protected by RTNL sempahore.  */
-static struct rtattr **rta_buf;
-static int rtattr_max;
-
 /* Process one rtnetlink message. */
 
 static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -2564,7 +2604,6 @@ 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 min_len;
        int family;
        int type;
        int err;
@@ -2576,10 +2615,10 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        type -= RTM_BASE;
 
        /* All the messages must have at least 1 byte length */
-       if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtgenmsg)))
+       if (nlmsg_len(nlh) < sizeof(struct rtgenmsg))
                return 0;
 
-       family = ((struct rtgenmsg *)NLMSG_DATA(nlh))->rtgen_family;
+       family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
        sz_idx = type>>2;
        kind = type&3;
 
@@ -2612,32 +2651,11 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                return err;
        }
 
-       memset(rta_buf, 0, (rtattr_max * sizeof(struct rtattr *)));
-
-       min_len = rtm_min[sz_idx];
-       if (nlh->nlmsg_len < min_len)
-               return -EINVAL;
-
-       if (nlh->nlmsg_len > min_len) {
-               int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
-               struct rtattr *attr = (void *)nlh + NLMSG_ALIGN(min_len);
-
-               while (RTA_OK(attr, attrlen)) {
-                       unsigned int flavor = attr->rta_type & NLA_TYPE_MASK;
-                       if (flavor) {
-                               if (flavor > rta_max[sz_idx])
-                                       return -EINVAL;
-                               rta_buf[flavor-1] = attr;
-                       }
-                       attr = RTA_NEXT(attr, attrlen);
-               }
-       }
-
        doit = rtnl_get_doit(family, type);
        if (doit == NULL)
                return -EOPNOTSUPP;
 
-       return doit(skb, nlh, (void *)&rta_buf[0]);
+       return doit(skb, nlh);
 }
 
 static void rtnetlink_rcv(struct sk_buff *skb)
@@ -2707,16 +2725,6 @@ static struct pernet_operations rtnetlink_net_ops = {
 
 void __init rtnetlink_init(void)
 {
-       int i;
-
-       rtattr_max = 0;
-       for (i = 0; i < ARRAY_SIZE(rta_max); i++)
-               if (rta_max[i] > rtattr_max)
-                       rtattr_max = rta_max[i];
-       rta_buf = kmalloc(rtattr_max * sizeof(struct rtattr *), GFP_KERNEL);
-       if (!rta_buf)
-               panic("rtnetlink_init: cannot allocate rta_buf\n");
-
        if (register_pernet_subsys(&rtnetlink_net_ops))
                panic("rtnetlink_init: cannot initialize rtnetlink\n");
 
index 2dc6cdaaae8abc5f31afa57a7ccf765cc978b6ac..03795d0147f2995e09495c98dcf3c94cb3d5511f 100644 (file)
@@ -187,22 +187,6 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 
                        p->creds.uid = uid;
                        p->creds.gid = gid;
-
-                       if (!p->cred ||
-                           !uid_eq(p->cred->euid, uid) ||
-                           !gid_eq(p->cred->egid, gid)) {
-                               struct cred *cred;
-                               err = -ENOMEM;
-                               cred = prepare_creds();
-                               if (!cred)
-                                       goto error;
-
-                               cred->uid = cred->euid = uid;
-                               cred->gid = cred->egid = gid;
-                               if (p->cred)
-                                       put_cred(p->cred);
-                               p->cred = cred;
-                       }
                        break;
                }
                default:
@@ -306,8 +290,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, current);
-                       sock_update_classid(sock->sk, current);
+                       sock_update_netprioidx(sock->sk);
+                       sock_update_classid(sock->sk);
                }
                fd_install(new_fd, get_file(fp[i]));
        }
index 33245ef54c3bd6982abed459cc6c0ef7935cb21f..898cf5c566f921c93fbbaf1a5ee7d0fc0bb72619 100644 (file)
@@ -179,6 +179,33 @@ out:
  *
  */
 
+struct sk_buff *__alloc_skb_head(gfp_t gfp_mask, int node)
+{
+       struct sk_buff *skb;
+
+       /* Get the HEAD */
+       skb = kmem_cache_alloc_node(skbuff_head_cache,
+                                   gfp_mask & ~__GFP_DMA, node);
+       if (!skb)
+               goto out;
+
+       /*
+        * Only clear those fields we need to clear, not those that we will
+        * actually initialise below. Hence, don't put any more fields after
+        * the tail pointer in struct sk_buff!
+        */
+       memset(skb, 0, offsetof(struct sk_buff, tail));
+       skb->data = NULL;
+       skb->truesize = sizeof(struct sk_buff);
+       atomic_set(&skb->users, 1);
+
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       skb->mac_header = ~0U;
+#endif
+out:
+       return skb;
+}
+
 /**
  *     __alloc_skb     -       allocate a network buffer
  *     @size: size to allocate
@@ -584,7 +611,8 @@ static void skb_release_head_state(struct sk_buff *skb)
 static void skb_release_all(struct sk_buff *skb)
 {
        skb_release_head_state(skb);
-       skb_release_data(skb);
+       if (likely(skb->data))
+               skb_release_data(skb);
 }
 
 /**
@@ -673,6 +701,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->mac_header         = old->mac_header;
        new->inner_transport_header = old->inner_transport_header;
        new->inner_network_header = old->inner_network_header;
+       new->inner_mac_header = old->inner_mac_header;
        skb_dst_copy(new, old);
        new->rxhash             = old->rxhash;
        new->ooo_okay           = old->ooo_okay;
@@ -706,6 +735,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->tc_verd            = old->tc_verd;
 #endif
 #endif
+       new->vlan_proto         = old->vlan_proto;
        new->vlan_tci           = old->vlan_tci;
 
        skb_copy_secmark(new, old);
@@ -867,6 +897,18 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
 }
 EXPORT_SYMBOL(skb_clone);
 
+static void skb_headers_offset_update(struct sk_buff *skb, int off)
+{
+       /* {transport,network,mac}_header and tail are relative to skb->head */
+       skb->transport_header += off;
+       skb->network_header   += off;
+       if (skb_mac_header_was_set(skb))
+               skb->mac_header += off;
+       skb->inner_transport_header += off;
+       skb->inner_network_header += off;
+       skb->inner_mac_header += off;
+}
+
 static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 {
 #ifndef NET_SKBUFF_DATA_USES_OFFSET
@@ -879,13 +921,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        __copy_skb_header(new, old);
 
 #ifndef NET_SKBUFF_DATA_USES_OFFSET
-       /* {transport,network,mac}_header are relative to skb->head */
-       new->transport_header += offset;
-       new->network_header   += offset;
-       if (skb_mac_header_was_set(new))
-               new->mac_header       += offset;
-       new->inner_transport_header += offset;
-       new->inner_network_header   += offset;
+       skb_headers_offset_update(new, offset);
 #endif
        skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
        skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
@@ -1077,14 +1113,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 #else
        skb->end      = skb->head + size;
 #endif
-       /* {transport,network,mac}_header and tail are relative to skb->head */
        skb->tail             += off;
-       skb->transport_header += off;
-       skb->network_header   += off;
-       if (skb_mac_header_was_set(skb))
-               skb->mac_header += off;
-       skb->inner_transport_header += off;
-       skb->inner_network_header += off;
+       skb_headers_offset_update(skb, off);
        /* Only adjust this if it actually is csum_start rather than csum */
        if (skb->ip_summed == CHECKSUM_PARTIAL)
                skb->csum_start += nhead;
@@ -1180,12 +1210,7 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
        if (n->ip_summed == CHECKSUM_PARTIAL)
                n->csum_start += off;
 #ifdef NET_SKBUFF_DATA_USES_OFFSET
-       n->transport_header += off;
-       n->network_header   += off;
-       if (skb_mac_header_was_set(skb))
-               n->mac_header += off;
-       n->inner_transport_header += off;
-       n->inner_network_header    += off;
+       skb_headers_offset_update(n, off);
 #endif
 
        return n;
@@ -2741,12 +2766,19 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
        unsigned int tnl_hlen = skb_tnl_header_len(skb);
        unsigned int headroom;
        unsigned int len;
+       __be16 proto;
+       bool csum;
        int sg = !!(features & NETIF_F_SG);
        int nfrags = skb_shinfo(skb)->nr_frags;
        int err = -ENOMEM;
        int i = 0;
        int pos;
 
+       proto = skb_network_protocol(skb);
+       if (unlikely(!proto))
+               return ERR_PTR(-EINVAL);
+
+       csum = !!can_checksum_protocol(features, proto);
        __skb_push(skb, doffset);
        headroom = skb_headroom(skb);
        pos = skb_headlen(skb);
@@ -2884,6 +2916,12 @@ skip_fraglist:
                nskb->data_len = len - hsize;
                nskb->len += nskb->data_len;
                nskb->truesize += nskb->data_len;
+
+               if (!csum) {
+                       nskb->csum = skb_checksum(nskb, doffset,
+                                                 nskb->len - doffset, 0);
+                       nskb->ip_summed = CHECKSUM_NONE;
+               }
        } while ((offset += len) < skb->len);
 
        return segs;
@@ -3361,6 +3399,7 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off)
        skb->ip_summed = CHECKSUM_PARTIAL;
        skb->csum_start = skb_headroom(skb) + start;
        skb->csum_offset = off;
+       skb_set_transport_header(skb, start);
        return true;
 }
 EXPORT_SYMBOL_GPL(skb_partial_csum_set);
index b261a79777462624d77639bd9aad398b6de6e961..d4f4cea726e7bfdb0d9cc47a779d5252738bf37c 100644 (file)
@@ -907,6 +907,10 @@ set_rcvbuf:
                sock_valbool_flag(sk, SOCK_NOFCS, valbool);
                break;
 
+       case SO_SELECT_ERR_QUEUE:
+               sock_valbool_flag(sk, SOCK_SELECT_ERR_QUEUE, valbool);
+               break;
+
        default:
                ret = -ENOPROTOOPT;
                break;
@@ -1160,6 +1164,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                v.val = sock_flag(sk, SOCK_FILTER_LOCKED);
                break;
 
+       case SO_SELECT_ERR_QUEUE:
+               v.val = sock_flag(sk, SOCK_SELECT_ERR_QUEUE);
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
@@ -1298,13 +1306,12 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
        module_put(owner);
 }
 
-#ifdef CONFIG_CGROUPS
 #if IS_ENABLED(CONFIG_NET_CLS_CGROUP)
-void sock_update_classid(struct sock *sk, struct task_struct *task)
+void sock_update_classid(struct sock *sk)
 {
        u32 classid;
 
-       classid = task_cls_classid(task);
+       classid = task_cls_classid(current);
        if (classid != sk->sk_classid)
                sk->sk_classid = classid;
 }
@@ -1312,16 +1319,15 @@ EXPORT_SYMBOL(sock_update_classid);
 #endif
 
 #if IS_ENABLED(CONFIG_NETPRIO_CGROUP)
-void sock_update_netprioidx(struct sock *sk, struct task_struct *task)
+void sock_update_netprioidx(struct sock *sk)
 {
        if (in_interrupt())
                return;
 
-       sk->sk_cgrp_prioidx = task_netprioidx(task);
+       sk->sk_cgrp_prioidx = task_netprioidx(current);
 }
 EXPORT_SYMBOL_GPL(sock_update_netprioidx);
 #endif
-#endif
 
 /**
  *     sk_alloc - All socket objects are allocated here
@@ -1347,8 +1353,8 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
                sock_net_set(sk, get_net(net));
                atomic_set(&sk->sk_wmem_alloc, 1);
 
-               sock_update_classid(sk, current);
-               sock_update_netprioidx(sk, current);
+               sock_update_classid(sk);
+               sock_update_netprioidx(sk);
        }
 
        return sk;
index e3487e46193912fec01410593fcbcb49266ce374..3c7f5b51b979c88a3dc1d0bc9cf6c44f2e74d182 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
+#include <linux/ctype.h>
 #include <linux/inet.h>
 #include <linux/mm.h>
 #include <linux/net.h>
@@ -348,9 +349,7 @@ int mac_pton(const char *s, u8 *mac)
 
        /* Don't dirty result unless string is valid MAC. */
        for (i = 0; i < ETH_ALEN; i++) {
-               if (!strchr("0123456789abcdefABCDEF", s[i * 3]))
-                       return 0;
-               if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1]))
+               if (!isxdigit(s[i * 3]) || !isxdigit(s[i * 3 + 1]))
                        return 0;
                if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':')
                        return 0;
index 1d9eb7c60a6850d51da5a7615c012c3e55681f99..4f72fc40bf029b6e18f2828c57d666ab6ba0bf44 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/notifier.h>
 #include <linux/export.h>
+#include <net/dcbevent.h>
 
 static ATOMIC_NOTIFIER_HEAD(dcbevent_notif_chain);
 
index 21291f1abcd6d0d228241b6771984d704bc1699e..40d5829ed36aaa6945c9c4c2056325b68ab64c03 100644 (file)
@@ -1658,7 +1658,7 @@ static const struct reply_func reply_funcs[DCB_CMD_MAX+1] = {
        [DCB_CMD_CEE_GET]       = { RTM_GETDCB, dcbnl_cee_get },
 };
 
-static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct net_device *netdev;
index 4f9f5eb478f1e97762860d0ac6d118be9b80f39f..ebc54fef85a5ed15110fe39002defd42d6a0b8f7 100644 (file)
@@ -500,8 +500,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk,
        return &rt->dst;
 }
 
-static int dccp_v4_send_response(struct sock *sk, struct request_sock *req,
-                                struct request_values *rv_unused)
+static int dccp_v4_send_response(struct sock *sk, struct request_sock *req)
 {
        int err = -1;
        struct sk_buff *skb;
@@ -658,7 +657,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        dreq->dreq_gss     = dreq->dreq_iss;
        dreq->dreq_service = service;
 
-       if (dccp_v4_send_response(sk, req, NULL))
+       if (dccp_v4_send_response(sk, req))
                goto drop_and_free;
 
        inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
index 6e05981f271e554ef4ddde25bb4029c7e3e95933..9c61f9c02fdb81df0f87f62994ac13330631c59a 100644 (file)
@@ -213,8 +213,7 @@ out:
 }
 
 
-static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
-                                struct request_values *rv_unused)
+static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
 {
        struct inet6_request_sock *ireq6 = inet6_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -428,7 +427,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        dreq->dreq_gss     = dreq->dreq_iss;
        dreq->dreq_service = service;
 
-       if (dccp_v6_send_response(sk, req, NULL))
+       if (dccp_v6_send_response(sk, req))
                goto drop_and_free;
 
        inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
index c8da116d84a455b53aa917f47d504bd6ce07388b..7d9197063ebb98beee6c778b25c5e675ecedfb4b 100644 (file)
@@ -563,7 +563,7 @@ static const struct nla_policy dn_ifa_policy[IFA_MAX+1] = {
                                    .len = IFNAMSIZ - 1 },
 };
 
-static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[IFA_MAX+1];
@@ -607,7 +607,7 @@ errout:
        return err;
 }
 
-static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[IFA_MAX+1];
index e36614eccc0476d8c9fd1a3ebfbcb59bf14ffe4f..57dc159245ecfff38e318626cf0ea1ffa9db1cae 100644 (file)
@@ -145,22 +145,10 @@ static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi
        return NULL;
 }
 
-__le16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type)
+static int dn_fib_count_nhs(const struct nlattr *attr)
 {
-       while(RTA_OK(attr,attrlen)) {
-               if (attr->rta_type == type)
-                       return *(__le16*)RTA_DATA(attr);
-               attr = RTA_NEXT(attr, attrlen);
-       }
-
-       return 0;
-}
-
-static int dn_fib_count_nhs(struct rtattr *rta)
-{
-       int nhs = 0;
-       struct rtnexthop *nhp = RTA_DATA(rta);
-       int nhlen = RTA_PAYLOAD(rta);
+       struct rtnexthop *nhp = nla_data(attr);
+       int nhs = 0, nhlen = nla_len(attr);
 
        while(nhlen >= (int)sizeof(struct rtnexthop)) {
                if ((nhlen -= nhp->rtnh_len) < 0)
@@ -172,10 +160,11 @@ static int dn_fib_count_nhs(struct rtattr *rta)
        return nhs;
 }
 
-static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
+static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr,
+                         const struct rtmsg *r)
 {
-       struct rtnexthop *nhp = RTA_DATA(rta);
-       int nhlen = RTA_PAYLOAD(rta);
+       struct rtnexthop *nhp = nla_data(attr);
+       int nhlen = nla_len(attr);
 
        change_nexthops(fi) {
                int attrlen = nhlen - sizeof(struct rtnexthop);
@@ -187,7 +176,10 @@ static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, cons
                nh->nh_weight = nhp->rtnh_hops + 1;
 
                if (attrlen) {
-                       nh->nh_gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
+                       struct nlattr *gw_attr;
+
+                       gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY);
+                       nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0;
                }
                nhp = RTNH_NEXT(nhp);
        } endfor_nexthops(fi);
@@ -268,7 +260,8 @@ out:
 }
 
 
-struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta *rta, const struct nlmsghdr *nlh, int *errp)
+struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct nlattr *attrs[],
+                                      const struct nlmsghdr *nlh, int *errp)
 {
        int err;
        struct dn_fib_info *fi = NULL;
@@ -281,11 +274,9 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
        if (dn_fib_props[r->rtm_type].scope > r->rtm_scope)
                goto err_inval;
 
-       if (rta->rta_mp) {
-               nhs = dn_fib_count_nhs(rta->rta_mp);
-               if (nhs == 0)
-                       goto err_inval;
-       }
+       if (attrs[RTA_MULTIPATH] &&
+           (nhs = dn_fib_count_nhs(attrs[RTA_MULTIPATH])) == 0)
+               goto err_inval;
 
        fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL);
        err = -ENOBUFS;
@@ -295,53 +286,65 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
        fi->fib_protocol = r->rtm_protocol;
        fi->fib_nhs = nhs;
        fi->fib_flags = r->rtm_flags;
-       if (rta->rta_priority)
-               fi->fib_priority = *rta->rta_priority;
-       if (rta->rta_mx) {
-               int attrlen = RTA_PAYLOAD(rta->rta_mx);
-               struct rtattr *attr = RTA_DATA(rta->rta_mx);
 
-               while(RTA_OK(attr, attrlen)) {
-                       unsigned int flavour = attr->rta_type;
+       if (attrs[RTA_PRIORITY])
+               fi->fib_priority = nla_get_u32(attrs[RTA_PRIORITY]);
+
+       if (attrs[RTA_METRICS]) {
+               struct nlattr *attr;
+               int rem;
 
-                       if (flavour) {
-                               if (flavour > RTAX_MAX)
+               nla_for_each_nested(attr, attrs[RTA_METRICS], rem) {
+                       int type = nla_type(attr);
+
+                       if (type) {
+                               if (type > RTAX_MAX || nla_len(attr) < 4)
                                        goto err_inval;
-                               fi->fib_metrics[flavour-1] = *(unsigned int *)RTA_DATA(attr);
+
+                               fi->fib_metrics[type-1] = nla_get_u32(attr);
                        }
-                       attr = RTA_NEXT(attr, attrlen);
                }
        }
-       if (rta->rta_prefsrc)
-               memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2);
 
-       if (rta->rta_mp) {
-               if ((err = dn_fib_get_nhs(fi, rta->rta_mp, r)) != 0)
+       if (attrs[RTA_PREFSRC])
+               fi->fib_prefsrc = nla_get_le16(attrs[RTA_PREFSRC]);
+
+       if (attrs[RTA_MULTIPATH]) {
+               if ((err = dn_fib_get_nhs(fi, attrs[RTA_MULTIPATH], r)) != 0)
                        goto failure;
-               if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)
+
+               if (attrs[RTA_OIF] &&
+                   fi->fib_nh->nh_oif != nla_get_u32(attrs[RTA_OIF]))
                        goto err_inval;
-               if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 2))
+
+               if (attrs[RTA_GATEWAY] &&
+                   fi->fib_nh->nh_gw != nla_get_le16(attrs[RTA_GATEWAY]))
                        goto err_inval;
        } else {
                struct dn_fib_nh *nh = fi->fib_nh;
-               if (rta->rta_oif)
-                       nh->nh_oif = *rta->rta_oif;
-               if (rta->rta_gw)
-                       memcpy(&nh->nh_gw, rta->rta_gw, 2);
+
+               if (attrs[RTA_OIF])
+                       nh->nh_oif = nla_get_u32(attrs[RTA_OIF]);
+
+               if (attrs[RTA_GATEWAY])
+                       nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]);
+
                nh->nh_flags = r->rtm_flags;
                nh->nh_weight = 1;
        }
 
        if (r->rtm_type == RTN_NAT) {
-               if (rta->rta_gw == NULL || nhs != 1 || rta->rta_oif)
+               if (!attrs[RTA_GATEWAY] || nhs != 1 || attrs[RTA_OIF])
                        goto err_inval;
-               memcpy(&fi->fib_nh->nh_gw, rta->rta_gw, 2);
+
+               fi->fib_nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]);
                goto link_it;
        }
 
        if (dn_fib_props[r->rtm_type].error) {
-               if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
+               if (attrs[RTA_GATEWAY] || attrs[RTA_OIF] || attrs[RTA_MULTIPATH])
                        goto err_inval;
+
                goto link_it;
        }
 
@@ -367,8 +370,8 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
        }
 
        if (fi->fib_prefsrc) {
-               if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL ||
-                   memcmp(&fi->fib_prefsrc, rta->rta_dst, 2))
+               if (r->rtm_type != RTN_LOCAL || !attrs[RTA_DST] ||
+                   fi->fib_prefsrc != nla_get_le16(attrs[RTA_DST]))
                        if (dnet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
                                goto err_inval;
        }
@@ -486,39 +489,21 @@ void dn_fib_select_multipath(const struct flowidn *fld, struct dn_fib_res *res)
        spin_unlock_bh(&dn_fib_multipath_lock);
 }
 
-
-static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
-{
-       int i;
-
-       for(i = 1; i <= RTA_MAX; i++) {
-               struct rtattr *attr = rta[i-1];
-               if (attr) {
-                       if (RTA_PAYLOAD(attr) < 4 && RTA_PAYLOAD(attr) != 2)
-                               return -EINVAL;
-                       if (i != RTA_MULTIPATH && i != RTA_METRICS &&
-                           i != RTA_TABLE)
-                               rta[i-1] = (struct rtattr *)RTA_DATA(attr);
-               }
-       }
-
-       return 0;
-}
-
-static inline u32 rtm_get_table(struct rtattr **rta, u8 table)
+static inline u32 rtm_get_table(struct nlattr *attrs[], u8 table)
 {
-       if (rta[RTA_TABLE - 1])
-               table = nla_get_u32((struct nlattr *) rta[RTA_TABLE - 1]);
+       if (attrs[RTA_TABLE])
+               table = nla_get_u32(attrs[RTA_TABLE]);
 
        return table;
 }
 
-static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct dn_fib_table *tb;
-       struct rtattr **rta = arg;
-       struct rtmsg *r = NLMSG_DATA(nlh);
+       struct rtmsg *r = nlmsg_data(nlh);
+       struct nlattr *attrs[RTA_MAX+1];
+       int err;
 
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
@@ -526,22 +511,24 @@ static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *
        if (!net_eq(net, &init_net))
                return -EINVAL;
 
-       if (dn_fib_check_attr(r, rta))
-               return -EINVAL;
+       err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy);
+       if (err < 0)
+               return err;
 
-       tb = dn_fib_get_table(rtm_get_table(rta, r->rtm_table), 0);
-       if (tb)
-               return tb->delete(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
+       tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 0);
+       if (!tb)
+               return -ESRCH;
 
-       return -ESRCH;
+       return tb->delete(tb, r, attrs, nlh, &NETLINK_CB(skb));
 }
 
-static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct dn_fib_table *tb;
-       struct rtattr **rta = arg;
-       struct rtmsg *r = NLMSG_DATA(nlh);
+       struct rtmsg *r = nlmsg_data(nlh);
+       struct nlattr *attrs[RTA_MAX+1];
+       int err;
 
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
@@ -549,14 +536,15 @@ static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *
        if (!net_eq(net, &init_net))
                return -EINVAL;
 
-       if (dn_fib_check_attr(r, rta))
-               return -EINVAL;
+       err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy);
+       if (err < 0)
+               return err;
 
-       tb = dn_fib_get_table(rtm_get_table(rta, r->rtm_table), 1);
-       if (tb)
-               return tb->insert(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
+       tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 1);
+       if (!tb)
+               return -ENOBUFS;
 
-       return -ENOBUFS;
+       return tb->insert(tb, r, attrs, nlh, &NETLINK_CB(skb));
 }
 
 static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifaddr *ifa)
@@ -566,10 +554,31 @@ static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifad
                struct nlmsghdr nlh;
                struct rtmsg rtm;
        } req;
-       struct dn_kern_rta rta;
+       struct {
+               struct nlattr hdr;
+               __le16 dst;
+       } dst_attr = {
+               .dst = dst,
+       };
+       struct {
+               struct nlattr hdr;
+               __le16 prefsrc;
+       } prefsrc_attr = {
+               .prefsrc = ifa->ifa_local,
+       };
+       struct {
+               struct nlattr hdr;
+               u32 oif;
+       } oif_attr = {
+               .oif = ifa->ifa_dev->dev->ifindex,
+       };
+       struct nlattr *attrs[RTA_MAX+1] = {
+               [RTA_DST] = (struct nlattr *) &dst_attr,
+               [RTA_PREFSRC] = (struct nlattr * ) &prefsrc_attr,
+               [RTA_OIF] = (struct nlattr *) &oif_attr,
+       };
 
        memset(&req.rtm, 0, sizeof(req.rtm));
-       memset(&rta, 0, sizeof(rta));
 
        if (type == RTN_UNICAST)
                tb = dn_fib_get_table(RT_MIN_TABLE, 1);
@@ -591,14 +600,10 @@ static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifad
        req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);
        req.rtm.rtm_type = type;
 
-       rta.rta_dst = &dst;
-       rta.rta_prefsrc = &ifa->ifa_local;
-       rta.rta_oif = &ifa->ifa_dev->dev->ifindex;
-
        if (cmd == RTM_NEWROUTE)
-               tb->insert(tb, &req.rtm, &rta, &req.nlh, NULL);
+               tb->insert(tb, &req.rtm, attrs, &req.nlh, NULL);
        else
-               tb->delete(tb, &req.rtm, &rta, &req.nlh, NULL);
+               tb->delete(tb, &req.rtm, attrs, &req.nlh, NULL);
 }
 
 static void dn_fib_add_ifaddr(struct dn_ifaddr *ifa)
index 5ac0e153ef834ee04e46fab749e3d5e69a190f2d..fe32388ea24f7cc6811ea17ee3fb8d9f47042ee7 100644 (file)
@@ -1613,23 +1613,41 @@ errout:
        return -EMSGSIZE;
 }
 
+const struct nla_policy rtm_dn_policy[RTA_MAX + 1] = {
+       [RTA_DST]               = { .type = NLA_U16 },
+       [RTA_SRC]               = { .type = NLA_U16 },
+       [RTA_IIF]               = { .type = NLA_U32 },
+       [RTA_OIF]               = { .type = NLA_U32 },
+       [RTA_GATEWAY]           = { .type = NLA_U16 },
+       [RTA_PRIORITY]          = { .type = NLA_U32 },
+       [RTA_PREFSRC]           = { .type = NLA_U16 },
+       [RTA_METRICS]           = { .type = NLA_NESTED },
+       [RTA_MULTIPATH]         = { .type = NLA_NESTED },
+       [RTA_TABLE]             = { .type = NLA_U32 },
+       [RTA_MARK]              = { .type = NLA_U32 },
+};
+
 /*
  * This is called by both endnodes and routers now.
  */
-static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
+static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(in_skb->sk);
-       struct rtattr **rta = arg;
        struct rtmsg *rtm = nlmsg_data(nlh);
        struct dn_route *rt = NULL;
        struct dn_skb_cb *cb;
        int err;
        struct sk_buff *skb;
        struct flowidn fld;
+       struct nlattr *tb[RTA_MAX+1];
 
        if (!net_eq(net, &init_net))
                return -EINVAL;
 
+       err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_dn_policy);
+       if (err < 0)
+               return err;
+
        memset(&fld, 0, sizeof(fld));
        fld.flowidn_proto = DNPROTO_NSP;
 
@@ -1639,12 +1657,14 @@ static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void
        skb_reset_mac_header(skb);
        cb = DN_SKB_CB(skb);
 
-       if (rta[RTA_SRC-1])
-               memcpy(&fld.saddr, RTA_DATA(rta[RTA_SRC-1]), 2);
-       if (rta[RTA_DST-1])
-               memcpy(&fld.daddr, RTA_DATA(rta[RTA_DST-1]), 2);
-       if (rta[RTA_IIF-1])
-               memcpy(&fld.flowidn_iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
+       if (tb[RTA_SRC])
+               fld.saddr = nla_get_le16(tb[RTA_SRC]);
+
+       if (tb[RTA_DST])
+               fld.daddr = nla_get_le16(tb[RTA_DST]);
+
+       if (tb[RTA_IIF])
+               fld.flowidn_iif = nla_get_u32(tb[RTA_IIF]);
 
        if (fld.flowidn_iif) {
                struct net_device *dev;
@@ -1669,10 +1689,9 @@ static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void
                if (!err && -rt->dst.error)
                        err = rt->dst.error;
        } else {
-               int oif = 0;
-               if (rta[RTA_OIF - 1])
-                       memcpy(&oif, RTA_DATA(rta[RTA_OIF - 1]), sizeof(int));
-               fld.flowidn_oif = oif;
+               if (tb[RTA_OIF])
+                       fld.flowidn_oif = nla_get_u32(tb[RTA_OIF]);
+
                err = dn_route_output_key((struct dst_entry **)&rt, &fld, 0);
        }
 
index 6c2445bcaba1ec3687de66a5b247a1d0fc39f215..86e3807052e9ab4feb56fcf88a06391c18f1472e 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/sockios.h>
 #include <linux/init.h>
 #include <linux/skbuff.h>
-#include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/proc_fs.h>
 #include <linux/netdevice.h>
@@ -224,26 +223,27 @@ static struct dn_zone *dn_new_zone(struct dn_hash *table, int z)
 }
 
 
-static int dn_fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct dn_kern_rta *rta, struct dn_fib_info *fi)
+static int dn_fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct nlattr *attrs[], struct dn_fib_info *fi)
 {
        struct rtnexthop *nhp;
        int nhlen;
 
-       if (rta->rta_priority && *rta->rta_priority != fi->fib_priority)
+       if (attrs[RTA_PRIORITY] &&
+           nla_get_u32(attrs[RTA_PRIORITY]) != fi->fib_priority)
                return 1;
 
-       if (rta->rta_oif || rta->rta_gw) {
-               if ((!rta->rta_oif || *rta->rta_oif == fi->fib_nh->nh_oif) &&
-                   (!rta->rta_gw  || memcmp(rta->rta_gw, &fi->fib_nh->nh_gw, 2) == 0))
+       if (attrs[RTA_OIF] || attrs[RTA_GATEWAY]) {
+               if ((!attrs[RTA_OIF] || nla_get_u32(attrs[RTA_OIF]) == fi->fib_nh->nh_oif) &&
+                   (!attrs[RTA_GATEWAY]  || nla_get_le16(attrs[RTA_GATEWAY]) != fi->fib_nh->nh_gw))
                        return 0;
                return 1;
        }
 
-       if (rta->rta_mp == NULL)
+       if (!attrs[RTA_MULTIPATH])
                return 0;
 
-       nhp = RTA_DATA(rta->rta_mp);
-       nhlen = RTA_PAYLOAD(rta->rta_mp);
+       nhp = nla_data(attrs[RTA_MULTIPATH]);
+       nhlen = nla_len(attrs[RTA_MULTIPATH]);
 
        for_nexthops(fi) {
                int attrlen = nhlen - sizeof(struct rtnexthop);
@@ -254,7 +254,10 @@ static int dn_fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct dn_kern
                if (nhp->rtnh_ifindex && nhp->rtnh_ifindex != nh->nh_oif)
                        return 1;
                if (attrlen) {
-                       gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
+                       struct nlattr *gw_attr;
+
+                       gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY);
+                       gw = gw_attr ? nla_get_le16(gw_attr) : 0;
 
                        if (gw && gw != nh->nh_gw)
                                return 1;
@@ -488,7 +491,7 @@ int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
        if (!net_eq(net, &init_net))
                return 0;
 
-       if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
+       if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) &&
                ((struct rtmsg *)nlmsg_data(cb->nlh))->rtm_flags&RTM_F_CLONED)
                        return dn_cache_dump(skb, cb);
 
@@ -517,7 +520,8 @@ out:
        return skb->len;
 }
 
-static int dn_fib_table_insert(struct dn_fib_table *tb, struct rtmsg *r, struct dn_kern_rta *rta, struct nlmsghdr *n, struct netlink_skb_parms *req)
+static int dn_fib_table_insert(struct dn_fib_table *tb, struct rtmsg *r, struct nlattr *attrs[],
+                              struct nlmsghdr *n, struct netlink_skb_parms *req)
 {
        struct dn_hash *table = (struct dn_hash *)tb->data;
        struct dn_fib_node *new_f, *f, **fp, **del_fp;
@@ -536,15 +540,14 @@ static int dn_fib_table_insert(struct dn_fib_table *tb, struct rtmsg *r, struct
                return -ENOBUFS;
 
        dz_key_0(key);
-       if (rta->rta_dst) {
-               __le16 dst;
-               memcpy(&dst, rta->rta_dst, 2);
+       if (attrs[RTA_DST]) {
+               __le16 dst = nla_get_le16(attrs[RTA_DST]);
                if (dst & ~DZ_MASK(dz))
                        return -EINVAL;
                key = dz_key(dst, dz);
        }
 
-       if ((fi = dn_fib_create_info(r, rta, n, &err)) == NULL)
+       if ((fi = dn_fib_create_info(r, attrs, n, &err)) == NULL)
                return err;
 
        if (dz->dz_nent > (dz->dz_divisor << 2) &&
@@ -654,7 +657,8 @@ out:
 }
 
 
-static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct dn_kern_rta *rta, struct nlmsghdr *n, struct netlink_skb_parms *req)
+static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct nlattr *attrs[],
+                              struct nlmsghdr *n, struct netlink_skb_parms *req)
 {
        struct dn_hash *table = (struct dn_hash*)tb->data;
        struct dn_fib_node **fp, **del_fp, *f;
@@ -671,9 +675,8 @@ static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct
                return -ESRCH;
 
        dz_key_0(key);
-       if (rta->rta_dst) {
-               __le16 dst;
-               memcpy(&dst, rta->rta_dst, 2);
+       if (attrs[RTA_DST]) {
+               __le16 dst = nla_get_le16(attrs[RTA_DST]);
                if (dst & ~DZ_MASK(dz))
                        return -EINVAL;
                key = dz_key(dst, dz);
@@ -703,7 +706,7 @@ static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct
                                (r->rtm_scope == RT_SCOPE_NOWHERE || f->fn_scope == r->rtm_scope) &&
                                (!r->rtm_protocol ||
                                        fi->fib_protocol == r->rtm_protocol) &&
-                               dn_fib_nh_match(r, n, rta, fi) == 0)
+                               dn_fib_nh_match(r, n, attrs, fi) == 0)
                        del_fp = fp;
        }
 
index dfe42012a044142dbc9c0bf6197f539fd1996a8b..2a7efe388344fdd37b10487c50ba3d712eedbf8a 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/netdevice.h>
 #include <linux/netfilter.h>
 #include <linux/spinlock.h>
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <linux/netfilter_decnet.h>
 
 #include <net/sock.h>
@@ -39,21 +39,21 @@ static struct sk_buff *dnrmg_build_message(struct sk_buff *rt_skb, int *errp)
        unsigned char *ptr;
        struct nf_dn_rtmsg *rtm;
 
-       size = NLMSG_SPACE(rt_skb->len);
-       size += NLMSG_ALIGN(sizeof(struct nf_dn_rtmsg));
-       skb = alloc_skb(size, GFP_ATOMIC);
+       size = NLMSG_ALIGN(rt_skb->len) +
+              NLMSG_ALIGN(sizeof(struct nf_dn_rtmsg));
+       skb = nlmsg_new(size, GFP_ATOMIC);
        if (!skb) {
                *errp = -ENOMEM;
                return NULL;
        }
        old_tail = skb->tail;
-       nlh = nlmsg_put(skb, 0, 0, 0, size - sizeof(*nlh), 0);
+       nlh = nlmsg_put(skb, 0, 0, 0, size, 0);
        if (!nlh) {
                kfree_skb(skb);
                *errp = -ENOMEM;
                return NULL;
        }
-       rtm = (struct nf_dn_rtmsg *)NLMSG_DATA(nlh);
+       rtm = (struct nf_dn_rtmsg *)nlmsg_data(nlh);
        rtm->nfdn_ifindex = rt_skb->dev->ifindex;
        ptr = NFDN_RTMSG(rtm);
        skb_copy_from_linear_data(rt_skb, ptr, rt_skb->len);
index 2bc62ea857c8b6842c27ed9d6ee12b564fb2d1e6..0eb5d5e76dfbe1f99537e8a561f1671389c09e16 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * net/dsa/dsa.c - Hardware switch handling
  * Copyright (c) 2008-2009 Marvell Semiconductor
+ * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
  *
  * 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
@@ -14,6 +15,9 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <net/dsa.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
 #include "dsa_priv.h"
 
 char dsa_driver_version[] = "0.1";
@@ -287,34 +291,239 @@ static struct net_device *dev_to_net_device(struct device *dev)
        return NULL;
 }
 
+#ifdef CONFIG_OF
+static int dsa_of_setup_routing_table(struct dsa_platform_data *pd,
+                                       struct dsa_chip_data *cd,
+                                       int chip_index,
+                                       struct device_node *link)
+{
+       int ret;
+       const __be32 *reg;
+       int link_port_addr;
+       int link_sw_addr;
+       struct device_node *parent_sw;
+       int len;
+
+       parent_sw = of_get_parent(link);
+       if (!parent_sw)
+               return -EINVAL;
+
+       reg = of_get_property(parent_sw, "reg", &len);
+       if (!reg || (len != sizeof(*reg) * 2))
+               return -EINVAL;
+
+       link_sw_addr = be32_to_cpup(reg + 1);
+
+       if (link_sw_addr >= pd->nr_chips)
+               return -EINVAL;
+
+       /* First time routing table allocation */
+       if (!cd->rtable) {
+               cd->rtable = kmalloc(pd->nr_chips * sizeof(s8), GFP_KERNEL);
+               if (!cd->rtable)
+                       return -ENOMEM;
+
+               /* default to no valid uplink/downlink */
+               memset(cd->rtable, -1, pd->nr_chips * sizeof(s8));
+       }
+
+       reg = of_get_property(link, "reg", NULL);
+       if (!reg) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       link_port_addr = be32_to_cpup(reg);
+
+       cd->rtable[link_sw_addr] = link_port_addr;
+
+       return 0;
+out:
+       kfree(cd->rtable);
+       return ret;
+}
+
+static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
+{
+       int i;
+       int port_index;
+
+       for (i = 0; i < pd->nr_chips; i++) {
+               port_index = 0;
+               while (port_index < DSA_MAX_PORTS) {
+                       if (pd->chip[i].port_names[port_index])
+                               kfree(pd->chip[i].port_names[port_index]);
+                       port_index++;
+               }
+               kfree(pd->chip[i].rtable);
+       }
+       kfree(pd->chip);
+}
+
+static int dsa_of_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *child, *mdio, *ethernet, *port, *link;
+       struct mii_bus *mdio_bus;
+       struct platform_device *ethernet_dev;
+       struct dsa_platform_data *pd;
+       struct dsa_chip_data *cd;
+       const char *port_name;
+       int chip_index, port_index;
+       const unsigned int *sw_addr, *port_reg;
+       int ret;
+
+       mdio = of_parse_phandle(np, "dsa,mii-bus", 0);
+       if (!mdio)
+               return -EINVAL;
+
+       mdio_bus = of_mdio_find_bus(mdio);
+       if (!mdio_bus)
+               return -EINVAL;
+
+       ethernet = of_parse_phandle(np, "dsa,ethernet", 0);
+       if (!ethernet)
+               return -EINVAL;
+
+       ethernet_dev = of_find_device_by_node(ethernet);
+       if (!ethernet_dev)
+               return -ENODEV;
+
+       pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+       if (!pd)
+               return -ENOMEM;
+
+       pdev->dev.platform_data = pd;
+       pd->netdev = &ethernet_dev->dev;
+       pd->nr_chips = of_get_child_count(np);
+       if (pd->nr_chips > DSA_MAX_SWITCHES)
+               pd->nr_chips = DSA_MAX_SWITCHES;
+
+       pd->chip = kzalloc(pd->nr_chips * sizeof(struct dsa_chip_data),
+                       GFP_KERNEL);
+       if (!pd->chip) {
+               ret = -ENOMEM;
+               goto out_free;
+       }
+
+       chip_index = 0;
+       for_each_available_child_of_node(np, child) {
+               cd = &pd->chip[chip_index];
+
+               cd->mii_bus = &mdio_bus->dev;
+
+               sw_addr = of_get_property(child, "reg", NULL);
+               if (!sw_addr)
+                       continue;
+
+               cd->sw_addr = be32_to_cpup(sw_addr);
+               if (cd->sw_addr > PHY_MAX_ADDR)
+                       continue;
+
+               for_each_available_child_of_node(child, port) {
+                       port_reg = of_get_property(port, "reg", NULL);
+                       if (!port_reg)
+                               continue;
+
+                       port_index = be32_to_cpup(port_reg);
+
+                       port_name = of_get_property(port, "label", NULL);
+                       if (!port_name)
+                               continue;
+
+                       cd->port_names[port_index] = kstrdup(port_name,
+                                       GFP_KERNEL);
+                       if (!cd->port_names[port_index]) {
+                               ret = -ENOMEM;
+                               goto out_free_chip;
+                       }
+
+                       link = of_parse_phandle(port, "link", 0);
+
+                       if (!strcmp(port_name, "dsa") && link &&
+                                       pd->nr_chips > 1) {
+                               ret = dsa_of_setup_routing_table(pd, cd,
+                                               chip_index, link);
+                               if (ret)
+                                       goto out_free_chip;
+                       }
+
+                       if (port_index == DSA_MAX_PORTS)
+                               break;
+               }
+       }
+
+       return 0;
+
+out_free_chip:
+       dsa_of_free_platform_data(pd);
+out_free:
+       kfree(pd);
+       pdev->dev.platform_data = NULL;
+       return ret;
+}
+
+static void dsa_of_remove(struct platform_device *pdev)
+{
+       struct dsa_platform_data *pd = pdev->dev.platform_data;
+
+       if (!pdev->dev.of_node)
+               return;
+
+       dsa_of_free_platform_data(pd);
+       kfree(pd);
+}
+#else
+static inline int dsa_of_probe(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static inline void dsa_of_remove(struct platform_device *pdev)
+{
+}
+#endif
+
 static int dsa_probe(struct platform_device *pdev)
 {
        static int dsa_version_printed;
        struct dsa_platform_data *pd = pdev->dev.platform_data;
        struct net_device *dev;
        struct dsa_switch_tree *dst;
-       int i;
+       int i, ret;
 
        if (!dsa_version_printed++)
                printk(KERN_NOTICE "Distributed Switch Architecture "
                        "driver version %s\n", dsa_driver_version);
 
+       if (pdev->dev.of_node) {
+               ret = dsa_of_probe(pdev);
+               if (ret)
+                       return ret;
+
+               pd = pdev->dev.platform_data;
+       }
+
        if (pd == NULL || pd->netdev == NULL)
                return -EINVAL;
 
        dev = dev_to_net_device(pd->netdev);
-       if (dev == NULL)
-               return -EINVAL;
+       if (dev == NULL) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        if (dev->dsa_ptr != NULL) {
                dev_put(dev);
-               return -EEXIST;
+               ret = -EEXIST;
+               goto out;
        }
 
        dst = kzalloc(sizeof(*dst), GFP_KERNEL);
        if (dst == NULL) {
                dev_put(dev);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out;
        }
 
        platform_set_drvdata(pdev, dst);
@@ -366,6 +575,11 @@ static int dsa_probe(struct platform_device *pdev)
        }
 
        return 0;
+
+out:
+       dsa_of_remove(pdev);
+
+       return ret;
 }
 
 static int dsa_remove(struct platform_device *pdev)
@@ -385,6 +599,8 @@ static int dsa_remove(struct platform_device *pdev)
                        dsa_switch_destroy(ds);
        }
 
+       dsa_of_remove(pdev);
+
        return 0;
 }
 
@@ -392,6 +608,12 @@ static void dsa_shutdown(struct platform_device *pdev)
 {
 }
 
+static const struct of_device_id dsa_of_match_table[] = {
+       { .compatible = "marvell,dsa", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, dsa_of_match_table);
+
 static struct platform_driver dsa_driver = {
        .probe          = dsa_probe,
        .remove         = dsa_remove,
@@ -399,6 +621,7 @@ static struct platform_driver dsa_driver = {
        .driver = {
                .name   = "dsa",
                .owner  = THIS_MODULE,
+               .of_match_table = dsa_of_match_table,
        },
 };
 
index a36c85eab5b4ebf9992794f3822fd8553f7458c9..5359560926bcf8f17f87c94474fd96cda1f1d6e2 100644 (file)
@@ -195,7 +195,7 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
        if (netdev_uses_trailer_tags(dev))
                return htons(ETH_P_TRAILER);
 
-       if (ntohs(eth->h_proto) >= 1536)
+       if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
                return eth->h_proto;
 
        /*
index 43b95ca611144920333ca6aa12bc70aa14c033b9..55e1fd5b3e56d4ede22c2a05a6f72cc3b4bffa44 100644 (file)
@@ -104,6 +104,7 @@ static const u8 lowpan_llprefix[] = {0xfe, 0x80};
 struct lowpan_dev_info {
        struct net_device       *real_dev; /* real WPAN device ptr */
        struct mutex            dev_list_mtx; /* mutex for list ops */
+       unsigned short          fragment_tag;
 };
 
 struct lowpan_dev_record {
@@ -120,7 +121,6 @@ struct lowpan_fragment {
        struct list_head        list;           /* fragments list */
 };
 
-static unsigned short fragment_tag;
 static LIST_HEAD(lowpan_fragments);
 static DEFINE_SPINLOCK(flist_lock);
 
@@ -284,6 +284,9 @@ lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
        /* checksum is always inline */
        memcpy(*hc06_ptr, &uh->check, 2);
        *hc06_ptr += 2;
+
+       /* skip the UDP header */
+       skb_pull(skb, sizeof(struct udphdr));
 }
 
 static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
@@ -309,9 +312,8 @@ static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val)
 }
 
 static int
-lowpan_uncompress_udp_header(struct sk_buff *skb)
+lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
 {
-       struct udphdr *uh = udp_hdr(skb);
        u8 tmp;
 
        if (!uh)
@@ -358,6 +360,14 @@ lowpan_uncompress_udp_header(struct sk_buff *skb)
                /* copy checksum */
                memcpy(&uh->check, &skb->data[0], 2);
                skb_pull(skb, 2);
+
+               /*
+                * UDP lenght needs to be infered from the lower layers
+                * here, we obtain the hint from the remaining size of the
+                * frame
+                */
+               uh->len = htons(skb->len + sizeof(struct udphdr));
+               pr_debug("uncompressed UDP length: src = %d", uh->len);
        } else {
                pr_debug("ERROR: unsupported NH format\n");
                goto err;
@@ -572,17 +582,31 @@ static int lowpan_header_create(struct sk_buff *skb,
         * this isn't implemented in mainline yet, so currently we assign 0xff
         */
        {
+               mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
+               mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+
                /* prepare wpan address data */
                sa.addr_type = IEEE802154_ADDR_LONG;
-               sa.pan_id = 0xff;
-
-               da.addr_type = IEEE802154_ADDR_LONG;
-               da.pan_id = 0xff;
+               sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
 
-               memcpy(&(da.hwaddr), daddr, 8);
                memcpy(&(sa.hwaddr), saddr, 8);
+               /* intra-PAN communications */
+               da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
 
-               mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
+               /*
+                * if the destination address is the broadcast address, use the
+                * corresponding short address
+                */
+               if (lowpan_is_addr_broadcast(daddr)) {
+                       da.addr_type = IEEE802154_ADDR_SHORT;
+                       da.short_addr = IEEE802154_ADDR_BROADCAST;
+               } else {
+                       da.addr_type = IEEE802154_ADDR_LONG;
+                       memcpy(&(da.hwaddr), daddr, IEEE802154_ADDR_LEN);
+
+                       /* request acknowledgment */
+                       mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
+               }
 
                return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
                                type, (void *)&da, (void *)&sa, skb->len);
@@ -650,7 +674,7 @@ static void lowpan_fragment_timer_expired(unsigned long entry_addr)
 }
 
 static struct lowpan_fragment *
-lowpan_alloc_new_frame(struct sk_buff *skb, u8 len, u16 tag)
+lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag)
 {
        struct lowpan_fragment *frame;
 
@@ -720,7 +744,7 @@ lowpan_process_data(struct sk_buff *skb)
        {
                struct lowpan_fragment *frame;
                /* slen stores the rightmost 8 bits of the 11 bits length */
-               u8 slen, offset;
+               u8 slen, offset = 0;
                u16 len, tag;
                bool found = false;
 
@@ -731,6 +755,18 @@ lowpan_process_data(struct sk_buff *skb)
                /* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */
                len = ((iphc0 & 7) << 8) | slen;
 
+               if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) {
+                       pr_debug("%s received a FRAG1 packet (tag: %d, "
+                                "size of the entire IP packet: %d)",
+                                __func__, tag, len);
+               } else { /* FRAGN */
+                       if (lowpan_fetch_skb_u8(skb, &offset))
+                               goto unlock_and_drop;
+                       pr_debug("%s received a FRAGN packet (tag: %d, "
+                                "size of the entire IP packet: %d, "
+                                "offset: %d)", __func__, tag, len, offset * 8);
+               }
+
                /*
                 * check if frame assembling with the same tag is
                 * already in progress
@@ -745,17 +781,13 @@ lowpan_process_data(struct sk_buff *skb)
 
                /* alloc new frame structure */
                if (!found) {
+                       pr_debug("%s first fragment received for tag %d, "
+                                "begin packet reassembly", __func__, tag);
                        frame = lowpan_alloc_new_frame(skb, len, tag);
                        if (!frame)
                                goto unlock_and_drop;
                }
 
-               if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1)
-                       goto unlock_and_drop;
-
-               if (lowpan_fetch_skb_u8(skb, &offset)) /* fetch offset */
-                       goto unlock_and_drop;
-
                /* if payload fits buffer, copy it */
                if (likely((offset * 8 + skb->len) <= frame->length))
                        skb_copy_to_linear_data_offset(frame->skb, offset * 8,
@@ -773,6 +805,9 @@ lowpan_process_data(struct sk_buff *skb)
                        list_del(&frame->list);
                        spin_unlock_bh(&flist_lock);
 
+                       pr_debug("%s successfully reassembled fragment "
+                                "(tag %d)", __func__, tag);
+
                        dev_kfree_skb(skb);
                        skb = frame->skb;
                        kfree(frame);
@@ -918,10 +953,35 @@ lowpan_process_data(struct sk_buff *skb)
        }
 
        /* UDP data uncompression */
-       if (iphc0 & LOWPAN_IPHC_NH_C)
-               if (lowpan_uncompress_udp_header(skb))
+       if (iphc0 & LOWPAN_IPHC_NH_C) {
+               struct udphdr uh;
+               struct sk_buff *new;
+               if (lowpan_uncompress_udp_header(skb, &uh))
                        goto drop;
 
+               /*
+                * replace the compressed UDP head by the uncompressed UDP
+                * header
+                */
+               new = skb_copy_expand(skb, sizeof(struct udphdr),
+                                     skb_tailroom(skb), GFP_ATOMIC);
+               kfree_skb(skb);
+
+               if (!new)
+                       return -ENOMEM;
+
+               skb = new;
+
+               skb_push(skb, sizeof(struct udphdr));
+               skb_reset_transport_header(skb);
+               skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
+
+               lowpan_raw_dump_table(__func__, "raw UDP header dump",
+                                     (u8 *)&uh, sizeof(uh));
+
+               hdr.nexthdr = UIP_PROTO_UDP;
+       }
+
        /* Not fragmented package */
        hdr.payload_len = htons(skb->len);
 
@@ -969,13 +1029,13 @@ static int lowpan_get_mac_header_length(struct sk_buff *skb)
 
 static int
 lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
-                       int mlen, int plen, int offset)
+                       int mlen, int plen, int offset, int type)
 {
        struct sk_buff *frag;
        int hlen, ret;
 
-       /* if payload length is zero, therefore it's a first fragment */
-       hlen = (plen == 0 ? LOWPAN_FRAG1_HEAD_SIZE :  LOWPAN_FRAGN_HEAD_SIZE);
+       hlen = (type == LOWPAN_DISPATCH_FRAG1) ?
+                       LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE;
 
        lowpan_raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
 
@@ -1003,14 +1063,14 @@ lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
 }
 
 static int
-lowpan_skb_fragmentation(struct sk_buff *skb)
+lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev)
 {
        int  err, header_length, payload_length, tag, offset = 0;
        u8 head[5];
 
        header_length = lowpan_get_mac_header_length(skb);
        payload_length = skb->len - header_length;
-       tag = fragment_tag++;
+       tag = lowpan_dev_info(dev)->fragment_tag++;
 
        /* first fragment header */
        head[0] = LOWPAN_DISPATCH_FRAG1 | ((payload_length >> 8) & 0x7);
@@ -1018,7 +1078,16 @@ lowpan_skb_fragmentation(struct sk_buff *skb)
        head[2] = tag >> 8;
        head[3] = tag & 0xff;
 
-       err = lowpan_fragment_xmit(skb, head, header_length, 0, 0);
+       err = lowpan_fragment_xmit(skb, head, header_length, LOWPAN_FRAG_SIZE,
+                                  0, LOWPAN_DISPATCH_FRAG1);
+
+       if (err) {
+               pr_debug("%s unable to send FRAG1 packet (tag: %d)",
+                        __func__, tag);
+               goto exit;
+       }
+
+       offset = LOWPAN_FRAG_SIZE;
 
        /* next fragment header */
        head[0] &= ~LOWPAN_DISPATCH_FRAG1;
@@ -1033,10 +1102,17 @@ lowpan_skb_fragmentation(struct sk_buff *skb)
                        len = payload_length - offset;
 
                err = lowpan_fragment_xmit(skb, head, header_length,
-                                                       len, offset);
+                                          len, offset, LOWPAN_DISPATCH_FRAGN);
+               if (err) {
+                       pr_debug("%s unable to send a subsequent FRAGN packet "
+                                "(tag: %d, offset: %d", __func__, tag, offset);
+                       goto exit;
+               }
+
                offset += len;
        }
 
+exit:
        return err;
 }
 
@@ -1059,14 +1135,14 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        pr_debug("frame is too big, fragmentation is needed\n");
-       err = lowpan_skb_fragmentation(skb);
+       err = lowpan_skb_fragmentation(skb, dev);
 error:
        dev_kfree_skb(skb);
 out:
-       if (err < 0)
+       if (err)
                pr_debug("ERROR: xmit failed\n");
 
-       return (err < 0 ? NETDEV_TX_BUSY : NETDEV_TX_OK);
+       return (err < 0) ? NET_XMIT_DROP : err;
 }
 
 static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
@@ -1087,6 +1163,12 @@ static u16 lowpan_get_short_addr(const struct net_device *dev)
        return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev);
 }
 
+static u8 lowpan_get_dsn(const struct net_device *dev)
+{
+       struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
+       return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev);
+}
+
 static struct header_ops lowpan_header_ops = {
        .create = lowpan_header_create,
 };
@@ -1100,6 +1182,7 @@ static struct ieee802154_mlme_ops lowpan_mlme = {
        .get_pan_id = lowpan_get_pan_id,
        .get_phy = lowpan_get_phy,
        .get_short_addr = lowpan_get_short_addr,
+       .get_dsn = lowpan_get_dsn,
 };
 
 static void lowpan_setup(struct net_device *dev)
@@ -1203,6 +1286,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
                return -ENODEV;
 
        lowpan_dev_info(dev)->real_dev = real_dev;
+       lowpan_dev_info(dev)->fragment_tag = 0;
        mutex_init(&lowpan_dev_info(dev)->dev_list_mtx);
 
        entry = kzalloc(sizeof(struct lowpan_dev_record), GFP_KERNEL);
index bba5f8336317a178c5c7c073acd3bbfafde0e6b9..4b8f917658b52fa028200d9ec1ab788130ecd143 100644 (file)
  */
 #define lowpan_is_iid_16_bit_compressable(a)   \
        ((((a)->s6_addr16[4]) == 0) &&          \
-        (((a)->s6_addr16[5]) == 0) &&          \
-        (((a)->s6_addr16[6]) == 0) &&          \
-        ((((a)->s6_addr[14]) & 0x80) == 0))
+        (((a)->s6_addr[10]) == 0) &&           \
+        (((a)->s6_addr[11]) == 0xff) &&        \
+        (((a)->s6_addr[12]) == 0xfe) &&        \
+        (((a)->s6_addr[13]) == 0))
 
 /* multicast address */
 #define is_addr_mcast(a) (((a)->s6_addr[0]) == 0xFF)
index e0da175f8e5b7962b7906531ebee6e0650bf7a73..581a59504bd5f49d9a54584f3441c4e323b7bcd5 100644 (file)
@@ -291,6 +291,9 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
        size_t copied = 0;
        int err = -EOPNOTSUPP;
        struct sk_buff *skb;
+       struct sockaddr_ieee802154 *saddr;
+
+       saddr = (struct sockaddr_ieee802154 *)msg->msg_name;
 
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb)
@@ -309,6 +312,13 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
 
        sock_recv_ts_and_drops(msg, sk, skb);
 
+       if (saddr) {
+               saddr->family = AF_IEEE802154;
+               saddr->addr = mac_cb(skb)->sa;
+       }
+       if (addr_len)
+               *addr_len = sizeof(*saddr);
+
        if (flags & MSG_TRUNC)
                copied = skb->len;
 done:
index 97351e1d07a4398931406e3ef64383903d91f613..7e49bbcc6967ebfaac72ee9e6b3e3b9032f1f88a 100644 (file)
@@ -64,8 +64,8 @@ struct sk_buff *ieee802154_nl_create(int flags, u8 req)
 
 int ieee802154_nl_mcast(struct sk_buff *msg, unsigned int group)
 {
-       /* XXX: nlh is right at the start of msg */
-       void *hdr = genlmsg_data(NLMSG_DATA(msg->data));
+       struct nlmsghdr *nlh = nlmsg_hdr(msg);
+       void *hdr = genlmsg_data(nlmsg_data(nlh));
 
        if (genlmsg_end(msg, hdr) < 0)
                goto out;
@@ -97,8 +97,8 @@ struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
 
 int ieee802154_nl_reply(struct sk_buff *msg, struct genl_info *info)
 {
-       /* XXX: nlh is right at the start of msg */
-       void *hdr = genlmsg_data(NLMSG_DATA(msg->data));
+       struct nlmsghdr *nlh = nlmsg_hdr(msg);
+       void *hdr = genlmsg_data(nlmsg_data(nlh));
 
        if (genlmsg_end(msg, hdr) < 0)
                goto out;
index 96bb08abece29408f0d9d65f453ee7f2c0c55c4e..b0bdd8c51e9c83a0ef968fc678588f3807559b13 100644 (file)
@@ -315,7 +315,7 @@ static int ieee802154_associate_req(struct sk_buff *skb,
        struct net_device *dev;
        struct ieee802154_addr addr;
        u8 page;
-       int ret = -EINVAL;
+       int ret = -EOPNOTSUPP;
 
        if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
            !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
@@ -327,6 +327,8 @@ static int ieee802154_associate_req(struct sk_buff *skb,
        dev = ieee802154_nl_get_dev(info);
        if (!dev)
                return -ENODEV;
+       if (!ieee802154_mlme_ops(dev)->assoc_req)
+               goto out;
 
        if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
                addr.addr_type = IEEE802154_ADDR_LONG;
@@ -350,6 +352,7 @@ static int ieee802154_associate_req(struct sk_buff *skb,
                        page,
                        nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
 
+out:
        dev_put(dev);
        return ret;
 }
@@ -359,7 +362,7 @@ static int ieee802154_associate_resp(struct sk_buff *skb,
 {
        struct net_device *dev;
        struct ieee802154_addr addr;
-       int ret = -EINVAL;
+       int ret = -EOPNOTSUPP;
 
        if (!info->attrs[IEEE802154_ATTR_STATUS] ||
            !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
@@ -369,6 +372,8 @@ static int ieee802154_associate_resp(struct sk_buff *skb,
        dev = ieee802154_nl_get_dev(info);
        if (!dev)
                return -ENODEV;
+       if (!ieee802154_mlme_ops(dev)->assoc_resp)
+               goto out;
 
        addr.addr_type = IEEE802154_ADDR_LONG;
        nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
@@ -380,6 +385,7 @@ static int ieee802154_associate_resp(struct sk_buff *skb,
                nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
                nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
 
+out:
        dev_put(dev);
        return ret;
 }
@@ -389,7 +395,7 @@ static int ieee802154_disassociate_req(struct sk_buff *skb,
 {
        struct net_device *dev;
        struct ieee802154_addr addr;
-       int ret = -EINVAL;
+       int ret = -EOPNOTSUPP;
 
        if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
                !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
@@ -399,6 +405,8 @@ static int ieee802154_disassociate_req(struct sk_buff *skb,
        dev = ieee802154_nl_get_dev(info);
        if (!dev)
                return -ENODEV;
+       if (!ieee802154_mlme_ops(dev)->disassoc_req)
+               goto out;
 
        if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
                addr.addr_type = IEEE802154_ADDR_LONG;
@@ -415,6 +423,7 @@ static int ieee802154_disassociate_req(struct sk_buff *skb,
        ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
                        nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
 
+out:
        dev_put(dev);
        return ret;
 }
@@ -432,7 +441,7 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
        u8 channel, bcn_ord, sf_ord;
        u8 page;
        int pan_coord, blx, coord_realign;
-       int ret;
+       int ret = -EOPNOTSUPP;
 
        if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
            !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
@@ -448,6 +457,8 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
        dev = ieee802154_nl_get_dev(info);
        if (!dev)
                return -ENODEV;
+       if (!ieee802154_mlme_ops(dev)->start_req)
+               goto out;
 
        addr.addr_type = IEEE802154_ADDR_SHORT;
        addr.short_addr = nla_get_u16(
@@ -476,6 +487,7 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
        ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
                bcn_ord, sf_ord, pan_coord, blx, coord_realign);
 
+out:
        dev_put(dev);
        return ret;
 }
@@ -483,7 +495,7 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
 static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
 {
        struct net_device *dev;
-       int ret;
+       int ret = -EOPNOTSUPP;
        u8 type;
        u32 channels;
        u8 duration;
@@ -497,6 +509,8 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
        dev = ieee802154_nl_get_dev(info);
        if (!dev)
                return -ENODEV;
+       if (!ieee802154_mlme_ops(dev)->scan_req)
+               goto out;
 
        type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
        channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
@@ -511,6 +525,7 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
        ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
                        duration);
 
+out:
        dev_put(dev);
        return ret;
 }
index 7944df768454caf1cc87905de319a644a6dc3736..8603ca82710415b24e8ff85cbce1e45ba34491d2 100644 (file)
@@ -166,6 +166,7 @@ config IP_PNP_RARP
 config NET_IPIP
        tristate "IP: tunneling"
        select INET_TUNNEL
+       select NET_IP_TUNNEL
        ---help---
          Tunneling means encapsulating data of one protocol type within
          another protocol and sending it over a channel that understands the
@@ -186,9 +187,14 @@ config NET_IPGRE_DEMUX
         This is helper module to demultiplex GRE packets on GRE version field criteria.
         Required by ip_gre and pptp modules.
 
+config NET_IP_TUNNEL
+       tristate
+       default n
+
 config NET_IPGRE
        tristate "IP: GRE tunnels over IP"
        depends on (IPV6 || IPV6=n) && NET_IPGRE_DEMUX
+       select NET_IP_TUNNEL
        help
          Tunneling means encapsulating data of one protocol type within
          another protocol and sending it over a channel that understands the
@@ -313,6 +319,7 @@ config SYN_COOKIES
 config NET_IPVTI
        tristate "Virtual (secure) IP: tunneling"
        select INET_TUNNEL
+       select NET_IP_TUNNEL
        depends on INET_XFRM_MODE_TUNNEL
        ---help---
          Tunneling means encapsulating data of one protocol type within
index 15ca63ec604ee8e01c1f79103c7fe39265a8831c..089cb9f363871327b027d8ce9246c830256cd456 100644 (file)
@@ -13,6 +13,7 @@ obj-y     := route.o inetpeer.o protocol.o \
             fib_frontend.o fib_semantics.o fib_trie.o \
             inet_fragment.o ping.o
 
+obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o
 obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o
index c929d9c1c4b60d719d60066f7ca06e4b7d296f59..93824c57b1082d3fd473ed070249380fc24c45a5 100644 (file)
 #include <net/sock.h>
 #include <net/raw.h>
 #include <net/icmp.h>
-#include <net/ipip.h>
 #include <net/inet_common.h>
 #include <net/xfrm.h>
 #include <net/net_namespace.h>
@@ -1283,9 +1282,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
        int ihl;
        int id;
        unsigned int offset = 0;
-
-       if (!(features & NETIF_F_V4_CSUM))
-               features &= ~NETIF_F_SG;
+       bool tunnel;
 
        if (unlikely(skb_shinfo(skb)->gso_type &
                     ~(SKB_GSO_TCPV4 |
@@ -1293,6 +1290,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_UDP_TUNNEL |
                       0)))
                goto out;
 
@@ -1307,6 +1305,8 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
        if (unlikely(!pskb_may_pull(skb, ihl)))
                goto out;
 
+       tunnel = !!skb->encapsulation;
+
        __skb_pull(skb, ihl);
        skb_reset_transport_header(skb);
        iph = ip_hdr(skb);
@@ -1326,7 +1326,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
        skb = segs;
        do {
                iph = ip_hdr(skb);
-               if (proto == IPPROTO_UDP) {
+               if (!tunnel && proto == IPPROTO_UDP) {
                        iph->id = htons(id);
                        iph->frag_off = htons(offset >> 3);
                        if (skb->next != NULL)
index fea4929f6200f66407e86c487dd7c1c2bb15459d..247ec1951c35bed69d2182c6119085b9a7b2da96 100644 (file)
@@ -654,11 +654,19 @@ struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
        arp_ptr += dev->addr_len;
        memcpy(arp_ptr, &src_ip, 4);
        arp_ptr += 4;
-       if (target_hw != NULL)
-               memcpy(arp_ptr, target_hw, dev->addr_len);
-       else
-               memset(arp_ptr, 0, dev->addr_len);
-       arp_ptr += dev->addr_len;
+
+       switch (dev->type) {
+#if IS_ENABLED(CONFIG_FIREWIRE_NET)
+       case ARPHRD_IEEE1394:
+               break;
+#endif
+       default:
+               if (target_hw != NULL)
+                       memcpy(arp_ptr, target_hw, dev->addr_len);
+               else
+                       memset(arp_ptr, 0, dev->addr_len);
+               arp_ptr += dev->addr_len;
+       }
        memcpy(arp_ptr, &dest_ip, 4);
 
        return skb;
@@ -781,7 +789,14 @@ static int arp_process(struct sk_buff *skb)
        arp_ptr += dev->addr_len;
        memcpy(&sip, arp_ptr, 4);
        arp_ptr += 4;
-       arp_ptr += dev->addr_len;
+       switch (dev_type) {
+#if IS_ENABLED(CONFIG_FIREWIRE_NET)
+       case ARPHRD_IEEE1394:
+               break;
+#endif
+       default:
+               arp_ptr += dev->addr_len;
+       }
        memcpy(&tip, arp_ptr, 4);
 /*
  *     Check for bad requests for 127.x.x.x and requests for multicast
index c6287cd978c2db7b40f3aa3fe06b5d43bced64d5..dfc39d4d48b7471fc83035746026fa14d1dcf497 100644 (file)
@@ -536,7 +536,7 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
        return NULL;
 }
 
-static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[IFA_MAX+1];
@@ -801,7 +801,7 @@ static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
        return NULL;
 }
 
-static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct in_ifaddr *ifa;
@@ -1529,6 +1529,8 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
                idx = 0;
                head = &net->dev_index_head[h];
                rcu_read_lock();
+               cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
+                         net->dev_base_seq;
                hlist_for_each_entry_rcu(dev, head, index_hlist) {
                        if (idx < s_idx)
                                goto cont;
@@ -1549,6 +1551,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
                                        rcu_read_unlock();
                                        goto done;
                                }
+                               nl_dump_check_consistent(cb, nlmsg_hdr(skb));
                        }
 cont:
                        idx++;
@@ -1760,8 +1763,7 @@ static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
 };
 
 static int inet_netconf_get_devconf(struct sk_buff *in_skb,
-                                   struct nlmsghdr *nlh,
-                                   void *arg)
+                                   struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(in_skb->sk);
        struct nlattr *tb[NETCONFA_MAX+1];
@@ -1821,6 +1823,77 @@ errout:
        return err;
 }
 
+static int inet_netconf_dump_devconf(struct sk_buff *skb,
+                                    struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       int h, s_h;
+       int idx, s_idx;
+       struct net_device *dev;
+       struct in_device *in_dev;
+       struct hlist_head *head;
+
+       s_h = cb->args[0];
+       s_idx = idx = cb->args[1];
+
+       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+               idx = 0;
+               head = &net->dev_index_head[h];
+               rcu_read_lock();
+               cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
+                         net->dev_base_seq;
+               hlist_for_each_entry_rcu(dev, head, index_hlist) {
+                       if (idx < s_idx)
+                               goto cont;
+                       in_dev = __in_dev_get_rcu(dev);
+                       if (!in_dev)
+                               goto cont;
+
+                       if (inet_netconf_fill_devconf(skb, dev->ifindex,
+                                                     &in_dev->cnf,
+                                                     NETLINK_CB(cb->skb).portid,
+                                                     cb->nlh->nlmsg_seq,
+                                                     RTM_NEWNETCONF,
+                                                     NLM_F_MULTI,
+                                                     -1) <= 0) {
+                               rcu_read_unlock();
+                               goto done;
+                       }
+                       nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+cont:
+                       idx++;
+               }
+               rcu_read_unlock();
+       }
+       if (h == NETDEV_HASHENTRIES) {
+               if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
+                                             net->ipv4.devconf_all,
+                                             NETLINK_CB(cb->skb).portid,
+                                             cb->nlh->nlmsg_seq,
+                                             RTM_NEWNETCONF, NLM_F_MULTI,
+                                             -1) <= 0)
+                       goto done;
+               else
+                       h++;
+       }
+       if (h == NETDEV_HASHENTRIES + 1) {
+               if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
+                                             net->ipv4.devconf_dflt,
+                                             NETLINK_CB(cb->skb).portid,
+                                             cb->nlh->nlmsg_seq,
+                                             RTM_NEWNETCONF, NLM_F_MULTI,
+                                             -1) <= 0)
+                       goto done;
+               else
+                       h++;
+       }
+done:
+       cb->args[0] = h;
+       cb->args[1] = idx;
+
+       return skb->len;
+}
+
 #ifdef CONFIG_SYSCTL
 
 static void devinet_copy_dflt_conf(struct net *net, int i)
@@ -2225,6 +2298,6 @@ void __init devinet_init(void)
        rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
        rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
        rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
-                     NULL, NULL);
+                     inet_netconf_dump_devconf, NULL);
 }
 
index eb4bb12b3eb41582f8346d82b26a5cc258037037..c7629a209f9d84538b8048de89ce66aa3776a9bb 100644 (file)
@@ -604,7 +604,7 @@ errout:
        return err;
 }
 
-static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct fib_config cfg;
@@ -626,7 +626,7 @@ errout:
        return err;
 }
 
-static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct fib_config cfg;
@@ -957,8 +957,8 @@ static void nl_fib_input(struct sk_buff *skb)
 
        net = sock_net(skb->sk);
        nlh = nlmsg_hdr(skb);
-       if (skb->len < NLMSG_SPACE(0) || skb->len < nlh->nlmsg_len ||
-           nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*frn)))
+       if (skb->len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len ||
+           nlmsg_len(nlh) < sizeof(*frn))
                return;
 
        skb = skb_clone(skb, GFP_KERNEL);
@@ -966,7 +966,7 @@ static void nl_fib_input(struct sk_buff *skb)
                return;
        nlh = nlmsg_hdr(skb);
 
-       frn = (struct fib_result_nl *) NLMSG_DATA(nlh);
+       frn = (struct fib_result_nl *) nlmsg_data(nlh);
        tb = fib_get_table(net, frn->tb_id_in);
 
        nl_fib_lookup(frn, tb);
index 7a4c710c4cddef241766bf5816b20ef768e8a45b..d2d5a99fba0983a4dead0909e30212c84f72154b 100644 (file)
 
 static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
 static DEFINE_SPINLOCK(gre_proto_lock);
-struct gre_base_hdr {
-       __be16 flags;
-       __be16 protocol;
-};
-#define GRE_HEADER_SECTION 4
 
 int gre_add_protocol(const struct gre_protocol *proto, u8 version)
 {
index 786d97aee751e2f2206b9e3b66398bc0995cc645..6acb541c90910204f02449e7500138362da6998a 100644 (file)
@@ -559,7 +559,7 @@ static inline void syn_ack_recalc(struct request_sock *req, const int thresh,
 
 int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req)
 {
-       int err = req->rsk_ops->rtx_syn_ack(parent, req, NULL);
+       int err = req->rsk_ops->rtx_syn_ack(parent, req);
 
        if (!err)
                req->num_retrans++;
index 7afa2c3c788f184457e45899a7065a9786d26df7..5f648751fce2d03418f4a6425c7ba0dbfb3ddfc0 100644 (file)
@@ -158,7 +158,9 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
 
 #define EXPIRES_IN_MS(tmo)  DIV_ROUND_UP((tmo - jiffies) * 1000, HZ)
 
-       if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
+       if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
+           icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+           icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
                r->idiag_timer = 1;
                r->idiag_retrans = icsk->icsk_retransmits;
                r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
@@ -322,7 +324,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s
        }
 
        err = sk_diag_fill(sk, rep, req,
-                          sk_user_ns(NETLINK_CB(in_skb).ssk),
+                          sk_user_ns(NETLINK_CB(in_skb).sk),
                           NETLINK_CB(in_skb).portid,
                           nlh->nlmsg_seq, 0, nlh);
        if (err < 0) {
@@ -628,7 +630,7 @@ static int inet_csk_diag_dump(struct sock *sk,
                return 0;
 
        return inet_csk_diag_fill(sk, skb, r,
-                                 sk_user_ns(NETLINK_CB(cb->skb).ssk),
+                                 sk_user_ns(NETLINK_CB(cb->skb).sk),
                                  NETLINK_CB(cb->skb).portid,
                                  cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
 }
@@ -803,7 +805,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
                        }
 
                        err = inet_diag_fill_req(skb, sk, req,
-                                              sk_user_ns(NETLINK_CB(cb->skb).ssk),
+                                              sk_user_ns(NETLINK_CB(cb->skb).sk),
                                               NETLINK_CB(cb->skb).portid,
                                               cb->nlh->nlmsg_seq, cb->nlh);
                        if (err < 0) {
index f4fd23de9b13103b10351310aea08c47d0429d38..e97d66a1fdde572db3719d22ea2ac1ad4c4b5086 100644 (file)
 
 #include <net/sock.h>
 #include <net/inet_frag.h>
+#include <net/inet_ecn.h>
+
+/* Given the OR values of all fragments, apply RFC 3168 5.3 requirements
+ * Value : 0xff if frame should be dropped.
+ *         0 or INET_ECN_CE value, to be ORed in to final iph->tos field
+ */
+const u8 ip_frag_ecn_table[16] = {
+       /* at least one fragment had CE, and others ECT_0 or ECT_1 */
+       [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0]                      = INET_ECN_CE,
+       [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1]                      = INET_ECN_CE,
+       [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1]   = INET_ECN_CE,
+
+       /* invalid combinations : drop frame */
+       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE] = 0xff,
+       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0] = 0xff,
+       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_1] = 0xff,
+       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
+       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = 0xff,
+       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = 0xff,
+       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
+};
+EXPORT_SYMBOL(ip_frag_ecn_table);
 
 static void inet_frag_secret_rebuild(unsigned long dummy)
 {
@@ -30,20 +52,27 @@ static void inet_frag_secret_rebuild(unsigned long dummy)
        unsigned long now = jiffies;
        int i;
 
+       /* Per bucket lock NOT needed here, due to write lock protection */
        write_lock(&f->lock);
+
        get_random_bytes(&f->rnd, sizeof(u32));
        for (i = 0; i < INETFRAGS_HASHSZ; i++) {
+               struct inet_frag_bucket *hb;
                struct inet_frag_queue *q;
                struct hlist_node *n;
 
-               hlist_for_each_entry_safe(q, n, &f->hash[i], list) {
+               hb = &f->hash[i];
+               hlist_for_each_entry_safe(q, n, &hb->chain, list) {
                        unsigned int hval = f->hashfn(q);
 
                        if (hval != i) {
+                               struct inet_frag_bucket *hb_dest;
+
                                hlist_del(&q->list);
 
                                /* Relink to new hash chain. */
-                               hlist_add_head(&q->list, &f->hash[hval]);
+                               hb_dest = &f->hash[hval];
+                               hlist_add_head(&q->list, &hb_dest->chain);
                        }
                }
        }
@@ -56,9 +85,12 @@ void inet_frags_init(struct inet_frags *f)
 {
        int i;
 
-       for (i = 0; i < INETFRAGS_HASHSZ; i++)
-               INIT_HLIST_HEAD(&f->hash[i]);
+       for (i = 0; i < INETFRAGS_HASHSZ; i++) {
+               struct inet_frag_bucket *hb = &f->hash[i];
 
+               spin_lock_init(&hb->chain_lock);
+               INIT_HLIST_HEAD(&hb->chain);
+       }
        rwlock_init(&f->lock);
 
        f->rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^
@@ -100,10 +132,18 @@ EXPORT_SYMBOL(inet_frags_exit_net);
 
 static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f)
 {
-       write_lock(&f->lock);
+       struct inet_frag_bucket *hb;
+       unsigned int hash;
+
+       read_lock(&f->lock);
+       hash = f->hashfn(fq);
+       hb = &f->hash[hash];
+
+       spin_lock(&hb->chain_lock);
        hlist_del(&fq->list);
-       fq->net->nqueues--;
-       write_unlock(&f->lock);
+       spin_unlock(&hb->chain_lock);
+
+       read_unlock(&f->lock);
        inet_frag_lru_del(fq);
 }
 
@@ -182,6 +222,9 @@ int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force)
                q = list_first_entry(&nf->lru_list,
                                struct inet_frag_queue, lru_list);
                atomic_inc(&q->refcnt);
+               /* Remove q from list to avoid several CPUs grabbing it */
+               list_del_init(&q->lru_list);
+
                spin_unlock(&nf->lru_lock);
 
                spin_lock(&q->lock);
@@ -202,27 +245,32 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf,
                struct inet_frag_queue *qp_in, struct inet_frags *f,
                void *arg)
 {
+       struct inet_frag_bucket *hb;
        struct inet_frag_queue *qp;
 #ifdef CONFIG_SMP
 #endif
        unsigned int hash;
 
-       write_lock(&f->lock);
+       read_lock(&f->lock); /* Protects against hash rebuild */
        /*
         * While we stayed w/o the lock other CPU could update
         * the rnd seed, so we need to re-calculate the hash
         * chain. Fortunatelly the qp_in can be used to get one.
         */
        hash = f->hashfn(qp_in);
+       hb = &f->hash[hash];
+       spin_lock(&hb->chain_lock);
+
 #ifdef CONFIG_SMP
        /* With SMP race we have to recheck hash table, because
         * such entry could be created on other cpu, while we
-        * promoted read lock to write lock.
+        * released the hash bucket lock.
         */
-       hlist_for_each_entry(qp, &f->hash[hash], list) {
+       hlist_for_each_entry(qp, &hb->chain, list) {
                if (qp->net == nf && f->match(qp, arg)) {
                        atomic_inc(&qp->refcnt);
-                       write_unlock(&f->lock);
+                       spin_unlock(&hb->chain_lock);
+                       read_unlock(&f->lock);
                        qp_in->last_in |= INET_FRAG_COMPLETE;
                        inet_frag_put(qp_in, f);
                        return qp;
@@ -234,9 +282,9 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf,
                atomic_inc(&qp->refcnt);
 
        atomic_inc(&qp->refcnt);
-       hlist_add_head(&qp->list, &f->hash[hash]);
-       nf->nqueues++;
-       write_unlock(&f->lock);
+       hlist_add_head(&qp->list, &hb->chain);
+       spin_unlock(&hb->chain_lock);
+       read_unlock(&f->lock);
        inet_frag_lru_add(nf, qp);
        return qp;
 }
@@ -277,17 +325,23 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf,
                struct inet_frags *f, void *key, unsigned int hash)
        __releases(&f->lock)
 {
+       struct inet_frag_bucket *hb;
        struct inet_frag_queue *q;
        int depth = 0;
 
-       hlist_for_each_entry(q, &f->hash[hash], list) {
+       hb = &f->hash[hash];
+
+       spin_lock(&hb->chain_lock);
+       hlist_for_each_entry(q, &hb->chain, list) {
                if (q->net == nf && f->match(q, key)) {
                        atomic_inc(&q->refcnt);
+                       spin_unlock(&hb->chain_lock);
                        read_unlock(&f->lock);
                        return q;
                }
                depth++;
        }
+       spin_unlock(&hb->chain_lock);
        read_unlock(&f->lock);
 
        if (depth <= INETFRAGS_MAXDEPTH)
index cc280a3f4f966bbcc00563af6531c194bbe94bde..1975f52933c59309eac11ece4a6938a1fb3b435d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/if_vlan.h>
 #include <linux/inet_lro.h>
+#include <net/checksum.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jan-Bernd Themann <themann@de.ibm.com>");
@@ -114,11 +115,9 @@ static void lro_update_tcp_ip_header(struct net_lro_desc *lro_desc)
                *(p+2) = lro_desc->tcp_rcv_tsecr;
        }
 
+       csum_replace2(&iph->check, iph->tot_len, htons(lro_desc->ip_tot_len));
        iph->tot_len = htons(lro_desc->ip_tot_len);
 
-       iph->check = 0;
-       iph->check = ip_fast_csum((u8 *)lro_desc->iph, iph->ihl);
-
        tcph->check = 0;
        tcp_hdr_csum = csum_partial(tcph, TCP_HDR_LEN(tcph), 0);
        lro_desc->data_csum = csum_add(lro_desc->data_csum, tcp_hdr_csum);
index 52c273ea05c34c902e07c00609b4f881c392c37b..b66910aaef4d633977d2e983f2c63b419da38f77 100644 (file)
@@ -79,40 +79,11 @@ struct ipq {
        struct inet_peer *peer;
 };
 
-/* RFC 3168 support :
- * We want to check ECN values of all fragments, do detect invalid combinations.
- * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value.
- */
-#define        IPFRAG_ECN_NOT_ECT      0x01 /* one frag had ECN_NOT_ECT */
-#define        IPFRAG_ECN_ECT_1        0x02 /* one frag had ECN_ECT_1 */
-#define        IPFRAG_ECN_ECT_0        0x04 /* one frag had ECN_ECT_0 */
-#define        IPFRAG_ECN_CE           0x08 /* one frag had ECN_CE */
-
 static inline u8 ip4_frag_ecn(u8 tos)
 {
        return 1 << (tos & INET_ECN_MASK);
 }
 
-/* Given the OR values of all fragments, apply RFC 3168 5.3 requirements
- * Value : 0xff if frame should be dropped.
- *         0 or INET_ECN_CE value, to be ORed in to final iph->tos field
- */
-static const u8 ip4_frag_ecn_table[16] = {
-       /* at least one fragment had CE, and others ECT_0 or ECT_1 */
-       [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0]                      = INET_ECN_CE,
-       [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1]                      = INET_ECN_CE,
-       [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1]   = INET_ECN_CE,
-
-       /* invalid combinations : drop frame */
-       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE] = 0xff,
-       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0] = 0xff,
-       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_1] = 0xff,
-       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
-       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = 0xff,
-       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = 0xff,
-       [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
-};
-
 static struct inet_frags ip4_frags;
 
 int ip_frag_nqueues(struct net *net)
@@ -557,7 +528,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
 
        ipq_kill(qp);
 
-       ecn = ip4_frag_ecn_table[qp->ecn];
+       ecn = ip_frag_ecn_table[qp->ecn];
        if (unlikely(ecn == 0xff)) {
                err = -EINVAL;
                goto out_fail;
index 91d66dbde9c05cd05c4e8489a4abd317b378230e..987a4e5e07e2d3d5352de4cd8115909449183015 100644 (file)
@@ -37,7 +37,7 @@
 #include <net/ip.h>
 #include <net/icmp.h>
 #include <net/protocol.h>
-#include <net/ipip.h>
+#include <net/ip_tunnels.h>
 #include <net/arp.h>
 #include <net/checksum.h>
 #include <net/dsfield.h>
    fatal route to network, even if it were you who configured
    fatal static route: you are innocent. :-)
 
-
-
-   3. Really, ipv4/ipip.c, ipv4/ip_gre.c and ipv6/sit.c contain
-   practically identical code. It would be good to glue them
-   together, but it is not very evident, how to make them modular.
-   sit is integral part of IPv6, ipip and gre are naturally modular.
-   We could extract common parts (hash table, ioctl etc)
-   to a separate module (ip_tunnel.c).
-
    Alexey Kuznetsov.
  */
 
@@ -126,400 +117,137 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
 
 static struct rtnl_link_ops ipgre_link_ops __read_mostly;
 static int ipgre_tunnel_init(struct net_device *dev);
-static void ipgre_tunnel_setup(struct net_device *dev);
-static int ipgre_tunnel_bind_dev(struct net_device *dev);
-
-/* Fallback tunnel: no source, no destination, no key, no options */
-
-#define HASH_SIZE  16
 
 static int ipgre_net_id __read_mostly;
-struct ipgre_net {
-       struct ip_tunnel __rcu *tunnels[4][HASH_SIZE];
-
-       struct net_device *fb_tunnel_dev;
-};
-
-/* Tunnel hash table */
-
-/*
-   4 hash tables:
-
-   3: (remote,local)
-   2: (remote,*)
-   1: (*,local)
-   0: (*,*)
+static int gre_tap_net_id __read_mostly;
 
-   We require exact key match i.e. if a key is present in packet
-   it will match only tunnel with the same key; if it is not present,
-   it will match only keyless tunnel.
-
-   All keysless packets, if not matched configured keyless tunnels
-   will match fallback tunnel.
- */
+static __sum16 check_checksum(struct sk_buff *skb)
+{
+       __sum16 csum = 0;
 
-#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
+       switch (skb->ip_summed) {
+       case CHECKSUM_COMPLETE:
+               csum = csum_fold(skb->csum);
 
-#define tunnels_r_l    tunnels[3]
-#define tunnels_r      tunnels[2]
-#define tunnels_l      tunnels[1]
-#define tunnels_wc     tunnels[0]
+               if (!csum)
+                       break;
+               /* Fall through. */
 
-static struct rtnl_link_stats64 *ipgre_get_stats64(struct net_device *dev,
-                                                  struct rtnl_link_stats64 *tot)
-{
-       int i;
-
-       for_each_possible_cpu(i) {
-               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
-               u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
-               unsigned int start;
-
-               do {
-                       start = u64_stats_fetch_begin_bh(&tstats->syncp);
-                       rx_packets = tstats->rx_packets;
-                       tx_packets = tstats->tx_packets;
-                       rx_bytes = tstats->rx_bytes;
-                       tx_bytes = tstats->tx_bytes;
-               } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
-
-               tot->rx_packets += rx_packets;
-               tot->tx_packets += tx_packets;
-               tot->rx_bytes   += rx_bytes;
-               tot->tx_bytes   += tx_bytes;
+       case CHECKSUM_NONE:
+               skb->csum = 0;
+               csum = __skb_checksum_complete(skb);
+               skb->ip_summed = CHECKSUM_COMPLETE;
+               break;
        }
 
-       tot->multicast = dev->stats.multicast;
-       tot->rx_crc_errors = dev->stats.rx_crc_errors;
-       tot->rx_fifo_errors = dev->stats.rx_fifo_errors;
-       tot->rx_length_errors = dev->stats.rx_length_errors;
-       tot->rx_frame_errors = dev->stats.rx_frame_errors;
-       tot->rx_errors = dev->stats.rx_errors;
-
-       tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
-       tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
-       tot->tx_dropped = dev->stats.tx_dropped;
-       tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
-       tot->tx_errors = dev->stats.tx_errors;
-
-       return tot;
+       return csum;
 }
 
-/* Does key in tunnel parameters match packet */
-static bool ipgre_key_match(const struct ip_tunnel_parm *p,
-                           __be16 flags, __be32 key)
+static int ip_gre_calc_hlen(__be16 o_flags)
 {
-       if (p->i_flags & GRE_KEY) {
-               if (flags & GRE_KEY)
-                       return key == p->i_key;
-               else
-                       return false;   /* key expected, none present */
-       } else
-               return !(flags & GRE_KEY);
-}
+       int addend = 4;
 
-/* Given src, dst and key, find appropriate for input tunnel. */
+       if (o_flags&TUNNEL_CSUM)
+               addend += 4;
+       if (o_flags&TUNNEL_KEY)
+               addend += 4;
+       if (o_flags&TUNNEL_SEQ)
+               addend += 4;
+       return addend;
+}
 
-static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
-                                            __be32 remote, __be32 local,
-                                            __be16 flags, __be32 key,
-                                            __be16 gre_proto)
+static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
+                           bool *csum_err, int *hdr_len)
 {
-       struct net *net = dev_net(dev);
-       int link = dev->ifindex;
-       unsigned int h0 = HASH(remote);
-       unsigned int h1 = HASH(key);
-       struct ip_tunnel *t, *cand = NULL;
-       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
-       int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
-                      ARPHRD_ETHER : ARPHRD_IPGRE;
-       int score, cand_score = 4;
-
-       for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) {
-               if (local != t->parms.iph.saddr ||
-                   remote != t->parms.iph.daddr ||
-                   !(t->dev->flags & IFF_UP))
-                       continue;
-
-               if (!ipgre_key_match(&t->parms, flags, key))
-                       continue;
-
-               if (t->dev->type != ARPHRD_IPGRE &&
-                   t->dev->type != dev_type)
-                       continue;
-
-               score = 0;
-               if (t->parms.link != link)
-                       score |= 1;
-               if (t->dev->type != dev_type)
-                       score |= 2;
-               if (score == 0)
-                       return t;
-
-               if (score < cand_score) {
-                       cand = t;
-                       cand_score = score;
-               }
-       }
+       unsigned int ip_hlen = ip_hdrlen(skb);
+       const struct gre_base_hdr *greh;
+       __be32 *options;
 
-       for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) {
-               if (remote != t->parms.iph.daddr ||
-                   !(t->dev->flags & IFF_UP))
-                       continue;
-
-               if (!ipgre_key_match(&t->parms, flags, key))
-                       continue;
-
-               if (t->dev->type != ARPHRD_IPGRE &&
-                   t->dev->type != dev_type)
-                       continue;
-
-               score = 0;
-               if (t->parms.link != link)
-                       score |= 1;
-               if (t->dev->type != dev_type)
-                       score |= 2;
-               if (score == 0)
-                       return t;
-
-               if (score < cand_score) {
-                       cand = t;
-                       cand_score = score;
-               }
-       }
+       if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
+               return -EINVAL;
 
-       for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) {
-               if ((local != t->parms.iph.saddr &&
-                    (local != t->parms.iph.daddr ||
-                     !ipv4_is_multicast(local))) ||
-                   !(t->dev->flags & IFF_UP))
-                       continue;
-
-               if (!ipgre_key_match(&t->parms, flags, key))
-                       continue;
-
-               if (t->dev->type != ARPHRD_IPGRE &&
-                   t->dev->type != dev_type)
-                       continue;
-
-               score = 0;
-               if (t->parms.link != link)
-                       score |= 1;
-               if (t->dev->type != dev_type)
-                       score |= 2;
-               if (score == 0)
-                       return t;
-
-               if (score < cand_score) {
-                       cand = t;
-                       cand_score = score;
-               }
-       }
+       greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
+       if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
+               return -EINVAL;
 
-       for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) {
-               if (t->parms.i_key != key ||
-                   !(t->dev->flags & IFF_UP))
-                       continue;
-
-               if (t->dev->type != ARPHRD_IPGRE &&
-                   t->dev->type != dev_type)
-                       continue;
-
-               score = 0;
-               if (t->parms.link != link)
-                       score |= 1;
-               if (t->dev->type != dev_type)
-                       score |= 2;
-               if (score == 0)
-                       return t;
-
-               if (score < cand_score) {
-                       cand = t;
-                       cand_score = score;
-               }
-       }
+       tpi->flags = gre_flags_to_tnl_flags(greh->flags);
+       *hdr_len = ip_gre_calc_hlen(tpi->flags);
 
-       if (cand != NULL)
-               return cand;
+       if (!pskb_may_pull(skb, *hdr_len))
+               return -EINVAL;
 
-       dev = ign->fb_tunnel_dev;
-       if (dev->flags & IFF_UP)
-               return netdev_priv(dev);
+       greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
 
-       return NULL;
-}
+       tpi->proto = greh->protocol;
 
-static struct ip_tunnel __rcu **__ipgre_bucket(struct ipgre_net *ign,
-               struct ip_tunnel_parm *parms)
-{
-       __be32 remote = parms->iph.daddr;
-       __be32 local = parms->iph.saddr;
-       __be32 key = parms->i_key;
-       unsigned int h = HASH(key);
-       int prio = 0;
-
-       if (local)
-               prio |= 1;
-       if (remote && !ipv4_is_multicast(remote)) {
-               prio |= 2;
-               h ^= HASH(remote);
+       options = (__be32 *)(greh + 1);
+       if (greh->flags & GRE_CSUM) {
+               if (check_checksum(skb)) {
+                       *csum_err = true;
+                       return -EINVAL;
+               }
+               options++;
        }
 
-       return &ign->tunnels[prio][h];
-}
-
-static inline struct ip_tunnel __rcu **ipgre_bucket(struct ipgre_net *ign,
-               struct ip_tunnel *t)
-{
-       return __ipgre_bucket(ign, &t->parms);
-}
-
-static void ipgre_tunnel_link(struct ipgre_net *ign, struct ip_tunnel *t)
-{
-       struct ip_tunnel __rcu **tp = ipgre_bucket(ign, t);
+       if (greh->flags & GRE_KEY) {
+               tpi->key = *options;
+               options++;
+       } else
+               tpi->key = 0;
 
-       rcu_assign_pointer(t->next, rtnl_dereference(*tp));
-       rcu_assign_pointer(*tp, t);
-}
+       if (unlikely(greh->flags & GRE_SEQ)) {
+               tpi->seq = *options;
+               options++;
+       } else
+               tpi->seq = 0;
 
-static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
-{
-       struct ip_tunnel __rcu **tp;
-       struct ip_tunnel *iter;
-
-       for (tp = ipgre_bucket(ign, t);
-            (iter = rtnl_dereference(*tp)) != NULL;
-            tp = &iter->next) {
-               if (t == iter) {
-                       rcu_assign_pointer(*tp, t->next);
-                       break;
+       /* WCCP version 1 and 2 protocol decoding.
+        * - Change protocol to IP
+        * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
+        */
+       if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
+               tpi->proto = htons(ETH_P_IP);
+               if ((*(u8 *)options & 0xF0) != 0x40) {
+                       *hdr_len += 4;
+                       if (!pskb_may_pull(skb, *hdr_len))
+                               return -EINVAL;
                }
        }
-}
-
-static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
-                                          struct ip_tunnel_parm *parms,
-                                          int type)
-{
-       __be32 remote = parms->iph.daddr;
-       __be32 local = parms->iph.saddr;
-       __be32 key = parms->i_key;
-       int link = parms->link;
-       struct ip_tunnel *t;
-       struct ip_tunnel __rcu **tp;
-       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
-
-       for (tp = __ipgre_bucket(ign, parms);
-            (t = rtnl_dereference(*tp)) != NULL;
-            tp = &t->next)
-               if (local == t->parms.iph.saddr &&
-                   remote == t->parms.iph.daddr &&
-                   key == t->parms.i_key &&
-                   link == t->parms.link &&
-                   type == t->dev->type)
-                       break;
-
-       return t;
-}
-
-static struct ip_tunnel *ipgre_tunnel_locate(struct net *net,
-               struct ip_tunnel_parm *parms, int create)
-{
-       struct ip_tunnel *t, *nt;
-       struct net_device *dev;
-       char name[IFNAMSIZ];
-       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
-
-       t = ipgre_tunnel_find(net, parms, ARPHRD_IPGRE);
-       if (t || !create)
-               return t;
-
-       if (parms->name[0])
-               strlcpy(name, parms->name, IFNAMSIZ);
-       else
-               strcpy(name, "gre%d");
-
-       dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
-       if (!dev)
-               return NULL;
-
-       dev_net_set(dev, net);
-
-       nt = netdev_priv(dev);
-       nt->parms = *parms;
-       dev->rtnl_link_ops = &ipgre_link_ops;
-
-       dev->mtu = ipgre_tunnel_bind_dev(dev);
 
-       if (register_netdevice(dev) < 0)
-               goto failed_free;
-
-       /* Can use a lockless transmit, unless we generate output sequences */
-       if (!(nt->parms.o_flags & GRE_SEQ))
-               dev->features |= NETIF_F_LLTX;
-
-       dev_hold(dev);
-       ipgre_tunnel_link(ign, nt);
-       return nt;
-
-failed_free:
-       free_netdev(dev);
-       return NULL;
-}
-
-static void ipgre_tunnel_uninit(struct net_device *dev)
-{
-       struct net *net = dev_net(dev);
-       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
-
-       ipgre_tunnel_unlink(ign, netdev_priv(dev));
-       dev_put(dev);
+       return 0;
 }
 
-
 static void ipgre_err(struct sk_buff *skb, u32 info)
 {
 
-/* All the routers (except for Linux) return only
-   8 bytes of packet payload. It means, that precise relaying of
-   ICMP in the real Internet is absolutely infeasible.
+       /* All the routers (except for Linux) return only
+          8 bytes of packet payload. It means, that precise relaying of
+          ICMP in the real Internet is absolutely infeasible.
 
-   Moreover, Cisco "wise men" put GRE key to the third word
-   in GRE header. It makes impossible maintaining even soft state for keyed
-   GRE tunnels with enabled checksum. Tell them "thank you".
-
-   Well, I wonder, rfc1812 was written by Cisco employee,
-   what the hell these idiots break standards established
-   by themselves???
- */
+          Moreover, Cisco "wise men" put GRE key to the third word
+          in GRE header. It makes impossible maintaining even soft
+          state for keyed GRE tunnels with enabled checksum. Tell
+          them "thank you".
 
+          Well, I wonder, rfc1812 was written by Cisco employee,
+          what the hell these idiots break standards established
+          by themselves???
+          */
+       struct net *net = dev_net(skb->dev);
+       struct ip_tunnel_net *itn;
        const struct iphdr *iph = (const struct iphdr *)skb->data;
-       __be16       *p = (__be16 *)(skb->data+(iph->ihl<<2));
-       int grehlen = (iph->ihl<<2) + 4;
        const int type = icmp_hdr(skb)->type;
        const int code = icmp_hdr(skb)->code;
        struct ip_tunnel *t;
-       __be16 flags;
-       __be32 key = 0;
+       struct tnl_ptk_info tpi;
+       int hdr_len;
+       bool csum_err = false;
 
-       flags = p[0];
-       if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
-               if (flags&(GRE_VERSION|GRE_ROUTING))
+       if (parse_gre_header(skb, &tpi, &csum_err, &hdr_len)) {
+               if (!csum_err)          /* ignore csum errors. */
                        return;
-               if (flags&GRE_KEY) {
-                       grehlen += 4;
-                       if (flags&GRE_CSUM)
-                               grehlen += 4;
-               }
        }
 
-       /* If only 8 bytes returned, keyed message will be dropped here */
-       if (skb_headlen(skb) < grehlen)
-               return;
-
-       if (flags & GRE_KEY)
-               key = *(((__be32 *)p) + (grehlen / 4) - 1);
-
        switch (type) {
        default:
        case ICMP_PARAMETERPROB:
@@ -548,8 +276,13 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
                break;
        }
 
-       t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
-                               flags, key, p[1]);
+       if (tpi.proto == htons(ETH_P_TEB))
+               itn = net_generic(net, gre_tap_net_id);
+       else
+               itn = net_generic(net, ipgre_net_id);
+
+       t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi.flags,
+                            iph->daddr, iph->saddr, tpi.key);
 
        if (t == NULL)
                return;
@@ -578,158 +311,33 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
        t->err_time = jiffies;
 }
 
-static inline u8
-ipgre_ecn_encapsulate(u8 tos, const struct iphdr *old_iph, struct sk_buff *skb)
-{
-       u8 inner = 0;
-       if (skb->protocol == htons(ETH_P_IP))
-               inner = old_iph->tos;
-       else if (skb->protocol == htons(ETH_P_IPV6))
-               inner = ipv6_get_dsfield((const struct ipv6hdr *)old_iph);
-       return INET_ECN_encapsulate(tos, inner);
-}
-
 static int ipgre_rcv(struct sk_buff *skb)
 {
+       struct net *net = dev_net(skb->dev);
+       struct ip_tunnel_net *itn;
        const struct iphdr *iph;
-       u8     *h;
-       __be16    flags;
-       __sum16   csum = 0;
-       __be32 key = 0;
-       u32    seqno = 0;
        struct ip_tunnel *tunnel;
-       int    offset = 4;
-       __be16 gre_proto;
-       int    err;
+       struct tnl_ptk_info tpi;
+       int hdr_len;
+       bool csum_err = false;
 
-       if (!pskb_may_pull(skb, 16))
+       if (parse_gre_header(skb, &tpi, &csum_err, &hdr_len) < 0)
                goto drop;
 
-       iph = ip_hdr(skb);
-       h = skb->data;
-       flags = *(__be16 *)h;
-
-       if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
-               /* - Version must be 0.
-                  - We do not support routing headers.
-                */
-               if (flags&(GRE_VERSION|GRE_ROUTING))
-                       goto drop;
-
-               if (flags&GRE_CSUM) {
-                       switch (skb->ip_summed) {
-                       case CHECKSUM_COMPLETE:
-                               csum = csum_fold(skb->csum);
-                               if (!csum)
-                                       break;
-                               /* fall through */
-                       case CHECKSUM_NONE:
-                               skb->csum = 0;
-                               csum = __skb_checksum_complete(skb);
-                               skb->ip_summed = CHECKSUM_COMPLETE;
-                       }
-                       offset += 4;
-               }
-               if (flags&GRE_KEY) {
-                       key = *(__be32 *)(h + offset);
-                       offset += 4;
-               }
-               if (flags&GRE_SEQ) {
-                       seqno = ntohl(*(__be32 *)(h + offset));
-                       offset += 4;
-               }
-       }
+       if (tpi.proto == htons(ETH_P_TEB))
+               itn = net_generic(net, gre_tap_net_id);
+       else
+               itn = net_generic(net, ipgre_net_id);
 
-       gre_proto = *(__be16 *)(h + 2);
+       iph = ip_hdr(skb);
+       tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi.flags,
+                                 iph->saddr, iph->daddr, tpi.key);
 
-       tunnel = ipgre_tunnel_lookup(skb->dev,
-                                    iph->saddr, iph->daddr, flags, key,
-                                    gre_proto);
        if (tunnel) {
-               struct pcpu_tstats *tstats;
-
-               secpath_reset(skb);
-
-               skb->protocol = gre_proto;
-               /* WCCP version 1 and 2 protocol decoding.
-                * - Change protocol to IP
-                * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
-                */
-               if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) {
-                       skb->protocol = htons(ETH_P_IP);
-                       if ((*(h + offset) & 0xF0) != 0x40)
-                               offset += 4;
-               }
-
-               skb->mac_header = skb->network_header;
-               __pskb_pull(skb, offset);
-               skb_postpull_rcsum(skb, skb_transport_header(skb), offset);
-               skb->pkt_type = PACKET_HOST;
-#ifdef CONFIG_NET_IPGRE_BROADCAST
-               if (ipv4_is_multicast(iph->daddr)) {
-                       /* Looped back packet, drop it! */
-                       if (rt_is_output_route(skb_rtable(skb)))
-                               goto drop;
-                       tunnel->dev->stats.multicast++;
-                       skb->pkt_type = PACKET_BROADCAST;
-               }
-#endif
-
-               if (((flags&GRE_CSUM) && csum) ||
-                   (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
-                       tunnel->dev->stats.rx_crc_errors++;
-                       tunnel->dev->stats.rx_errors++;
-                       goto drop;
-               }
-               if (tunnel->parms.i_flags&GRE_SEQ) {
-                       if (!(flags&GRE_SEQ) ||
-                           (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {
-                               tunnel->dev->stats.rx_fifo_errors++;
-                               tunnel->dev->stats.rx_errors++;
-                               goto drop;
-                       }
-                       tunnel->i_seqno = seqno + 1;
-               }
-
-               /* Warning: All skb pointers will be invalidated! */
-               if (tunnel->dev->type == ARPHRD_ETHER) {
-                       if (!pskb_may_pull(skb, ETH_HLEN)) {
-                               tunnel->dev->stats.rx_length_errors++;
-                               tunnel->dev->stats.rx_errors++;
-                               goto drop;
-                       }
-
-                       iph = ip_hdr(skb);
-                       skb->protocol = eth_type_trans(skb, tunnel->dev);
-                       skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
-               }
-
-               __skb_tunnel_rx(skb, tunnel->dev);
-
-               skb_reset_network_header(skb);
-               err = IP_ECN_decapsulate(iph, skb);
-               if (unlikely(err)) {
-                       if (log_ecn_error)
-                               net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-                                                    &iph->saddr, iph->tos);
-                       if (err > 1) {
-                               ++tunnel->dev->stats.rx_frame_errors;
-                               ++tunnel->dev->stats.rx_errors;
-                               goto drop;
-                       }
-               }
-
-               tstats = this_cpu_ptr(tunnel->dev->tstats);
-               u64_stats_update_begin(&tstats->syncp);
-               tstats->rx_packets++;
-               tstats->rx_bytes += skb->len;
-               u64_stats_update_end(&tstats->syncp);
-
-               gro_cells_receive(&tunnel->gro_cells, skb);
+               ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
                return 0;
        }
        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
-
 drop:
        kfree_skb(skb);
        return 0;
@@ -746,7 +354,7 @@ static struct sk_buff *handle_offloads(struct ip_tunnel *tunnel, struct sk_buff
                skb_shinfo(skb)->gso_type |= SKB_GSO_GRE;
                return skb;
        } else if (skb->ip_summed == CHECKSUM_PARTIAL &&
-                  tunnel->parms.o_flags&GRE_CSUM) {
+                  tunnel->parms.o_flags&TUNNEL_CSUM) {
                err = skb_checksum_help(skb);
                if (unlikely(err))
                        goto error;
@@ -760,494 +368,157 @@ error:
        return ERR_PTR(err);
 }
 
-static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+static struct sk_buff *gre_build_header(struct sk_buff *skb,
+                                       const struct tnl_ptk_info *tpi,
+                                       int hdr_len)
 {
-       struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats);
-       struct ip_tunnel *tunnel = netdev_priv(dev);
-       const struct iphdr  *old_iph;
-       const struct iphdr  *tiph;
-       struct flowi4 fl4;
-       u8     tos;
-       __be16 df;
-       struct rtable *rt;                      /* Route to the other host */
-       struct net_device *tdev;                /* Device to other host */
-       struct iphdr  *iph;                     /* Our new IP header */
-       unsigned int max_headroom;              /* The extra header space needed */
-       int    gre_hlen;
-       __be32 dst;
-       int    mtu;
-       u8     ttl;
-       int    err;
-       int    pkt_len;
-
-       skb = handle_offloads(tunnel, skb);
-       if (IS_ERR(skb)) {
-               dev->stats.tx_dropped++;
-               return NETDEV_TX_OK;
-       }
+       struct gre_base_hdr *greh;
 
-       if (!skb->encapsulation) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb_push(skb, hdr_len);
 
-       old_iph = ip_hdr(skb);
+       greh = (struct gre_base_hdr *)skb->data;
+       greh->flags = tnl_flags_to_gre_flags(tpi->flags);
+       greh->protocol = tpi->proto;
 
-       if (dev->type == ARPHRD_ETHER)
-               IPCB(skb)->flags = 0;
+       if (tpi->flags&(TUNNEL_KEY|TUNNEL_CSUM|TUNNEL_SEQ)) {
+               __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
 
-       if (dev->header_ops && dev->type == ARPHRD_IPGRE) {
-               gre_hlen = 0;
-               tiph = (const struct iphdr *)skb->data;
-       } else {
-               gre_hlen = tunnel->hlen;
-               tiph = &tunnel->parms.iph;
-       }
-
-       if ((dst = tiph->daddr) == 0) {
-               /* NBMA tunnel */
-
-               if (skb_dst(skb) == NULL) {
-                       dev->stats.tx_fifo_errors++;
-                       goto tx_error;
+               if (tpi->flags&TUNNEL_SEQ) {
+                       *ptr = tpi->seq;
+                       ptr--;
                }
-
-               if (skb->protocol == htons(ETH_P_IP)) {
-                       rt = skb_rtable(skb);
-                       dst = rt_nexthop(rt, old_iph->daddr);
+               if (tpi->flags&TUNNEL_KEY) {
+                       *ptr = tpi->key;
+                       ptr--;
                }
-#if IS_ENABLED(CONFIG_IPV6)
-               else if (skb->protocol == htons(ETH_P_IPV6)) {
-                       const struct in6_addr *addr6;
-                       struct neighbour *neigh;
-                       bool do_tx_error_icmp;
-                       int addr_type;
-
-                       neigh = dst_neigh_lookup(skb_dst(skb), &ipv6_hdr(skb)->daddr);
-                       if (neigh == NULL)
-                               goto tx_error;
-
-                       addr6 = (const struct in6_addr *)&neigh->primary_key;
-                       addr_type = ipv6_addr_type(addr6);
-
-                       if (addr_type == IPV6_ADDR_ANY) {
-                               addr6 = &ipv6_hdr(skb)->daddr;
-                               addr_type = ipv6_addr_type(addr6);
-                       }
-
-                       if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
-                               do_tx_error_icmp = true;
-                       else {
-                               do_tx_error_icmp = false;
-                               dst = addr6->s6_addr32[3];
-                       }
-                       neigh_release(neigh);
-                       if (do_tx_error_icmp)
-                               goto tx_error_icmp;
+               if (tpi->flags&TUNNEL_CSUM &&
+                   !(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) {
+                       *(__sum16 *)ptr = 0;
+                       *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
+                                                                skb->len, 0));
                }
-#endif
-               else
-                       goto tx_error;
        }
 
-       ttl = tiph->ttl;
-       tos = tiph->tos;
-       if (tos & 0x1) {
-               tos &= ~0x1;
-               if (skb->protocol == htons(ETH_P_IP))
-                       tos = old_iph->tos;
-               else if (skb->protocol == htons(ETH_P_IPV6))
-                       tos = ipv6_get_dsfield((const struct ipv6hdr *)old_iph);
-       }
+       return skb;
+}
 
-       rt = ip_route_output_gre(dev_net(dev), &fl4, dst, tiph->saddr,
-                                tunnel->parms.o_key, RT_TOS(tos),
-                                tunnel->parms.link);
-       if (IS_ERR(rt)) {
-               dev->stats.tx_carrier_errors++;
-               goto tx_error;
-       }
-       tdev = rt->dst.dev;
+static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
+                      const struct iphdr *tnl_params,
+                      __be16 proto)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct tnl_ptk_info tpi;
 
-       if (tdev == dev) {
-               ip_rt_put(rt);
-               dev->stats.collisions++;
-               goto tx_error;
+       if (likely(!skb->encapsulation)) {
+               skb_reset_inner_headers(skb);
+               skb->encapsulation = 1;
        }
 
-       df = tiph->frag_off;
-       if (df)
-               mtu = dst_mtu(&rt->dst) - dev->hard_header_len - tunnel->hlen;
-       else
-               mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
-
-       if (skb_dst(skb))
-               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
-
-       if (skb->protocol == htons(ETH_P_IP)) {
-               df |= (old_iph->frag_off&htons(IP_DF));
+       tpi.flags = tunnel->parms.o_flags;
+       tpi.proto = proto;
+       tpi.key = tunnel->parms.o_key;
+       if (tunnel->parms.o_flags & TUNNEL_SEQ)
+               tunnel->o_seqno++;
+       tpi.seq = htonl(tunnel->o_seqno);
 
-               if (!skb_is_gso(skb) &&
-                   (old_iph->frag_off&htons(IP_DF)) &&
-                   mtu < ntohs(old_iph->tot_len)) {
-                       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
-                       ip_rt_put(rt);
-                       goto tx_error;
-               }
+       /* Push GRE header. */
+       skb = gre_build_header(skb, &tpi, tunnel->hlen);
+       if (unlikely(!skb)) {
+               dev->stats.tx_dropped++;
+               return;
        }
-#if IS_ENABLED(CONFIG_IPV6)
-       else if (skb->protocol == htons(ETH_P_IPV6)) {
-               struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb);
-
-               if (rt6 && mtu < dst_mtu(skb_dst(skb)) && mtu >= IPV6_MIN_MTU) {
-                       if ((tunnel->parms.iph.daddr &&
-                            !ipv4_is_multicast(tunnel->parms.iph.daddr)) ||
-                           rt6->rt6i_dst.plen == 128) {
-                               rt6->rt6i_flags |= RTF_MODIFIED;
-                               dst_metric_set(skb_dst(skb), RTAX_MTU, mtu);
-                       }
-               }
 
-               if (!skb_is_gso(skb) &&
-                   mtu >= IPV6_MIN_MTU &&
-                   mtu < skb->len - tunnel->hlen + gre_hlen) {
-                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-                       ip_rt_put(rt);
-                       goto tx_error;
-               }
-       }
-#endif
+       ip_tunnel_xmit(skb, dev, tnl_params);
+}
 
-       if (tunnel->err_count > 0) {
-               if (time_before(jiffies,
-                               tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
-                       tunnel->err_count--;
+static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
+                             struct net_device *dev)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       const struct iphdr *tnl_params;
 
-                       dst_link_failure(skb);
-               } else
-                       tunnel->err_count = 0;
-       }
+       skb = handle_offloads(tunnel, skb);
+       if (IS_ERR(skb))
+               goto out;
 
-       max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + rt->dst.header_len;
-
-       if (skb_headroom(skb) < max_headroom || skb_shared(skb)||
-           (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
-               struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
-               if (max_headroom > dev->needed_headroom)
-                       dev->needed_headroom = max_headroom;
-               if (!new_skb) {
-                       ip_rt_put(rt);
-                       dev->stats.tx_dropped++;
-                       dev_kfree_skb(skb);
-                       return NETDEV_TX_OK;
-               }
-               if (skb->sk)
-                       skb_set_owner_w(new_skb, skb->sk);
-               dev_kfree_skb(skb);
-               skb = new_skb;
-               old_iph = ip_hdr(skb);
-               /* Warning : tiph value might point to freed memory */
-       }
+       if (dev->header_ops) {
+               /* Need space for new headers */
+               if (skb_cow_head(skb, dev->needed_headroom -
+                                     (tunnel->hlen + sizeof(struct iphdr))));
+                       goto free_skb;
 
-       skb_push(skb, gre_hlen);
-       skb_reset_network_header(skb);
-       skb_set_transport_header(skb, sizeof(*iph));
-       memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
-       IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
-                             IPSKB_REROUTED);
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
-       /*
-        *      Push down and install the IPIP header.
-        */
+               tnl_params = (const struct iphdr *)skb->data;
 
-       iph                     =       ip_hdr(skb);
-       iph->version            =       4;
-       iph->ihl                =       sizeof(struct iphdr) >> 2;
-       iph->frag_off           =       df;
-       iph->protocol           =       IPPROTO_GRE;
-       iph->tos                =       ipgre_ecn_encapsulate(tos, old_iph, skb);
-       iph->daddr              =       fl4.daddr;
-       iph->saddr              =       fl4.saddr;
-       iph->ttl                =       ttl;
-
-       tunnel_ip_select_ident(skb, old_iph, &rt->dst);
-
-       if (ttl == 0) {
-               if (skb->protocol == htons(ETH_P_IP))
-                       iph->ttl = old_iph->ttl;
-#if IS_ENABLED(CONFIG_IPV6)
-               else if (skb->protocol == htons(ETH_P_IPV6))
-                       iph->ttl = ((const struct ipv6hdr *)old_iph)->hop_limit;
-#endif
-               else
-                       iph->ttl = ip4_dst_hoplimit(&rt->dst);
-       }
-
-       ((__be16 *)(iph + 1))[0] = tunnel->parms.o_flags;
-       ((__be16 *)(iph + 1))[1] = (dev->type == ARPHRD_ETHER) ?
-                                  htons(ETH_P_TEB) : skb->protocol;
-
-       if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
-               __be32 *ptr = (__be32 *)(((u8 *)iph) + tunnel->hlen - 4);
+               /* Pull skb since ip_tunnel_xmit() needs skb->data pointing
+                * to gre header.
+                */
+               skb_pull(skb, tunnel->hlen + sizeof(struct iphdr));
+       } else {
+               if (skb_cow_head(skb, dev->needed_headroom))
+                       goto free_skb;
 
-               if (tunnel->parms.o_flags&GRE_SEQ) {
-                       ++tunnel->o_seqno;
-                       *ptr = htonl(tunnel->o_seqno);
-                       ptr--;
-               }
-               if (tunnel->parms.o_flags&GRE_KEY) {
-                       *ptr = tunnel->parms.o_key;
-                       ptr--;
-               }
-               /* Skip GRE checksum if skb is getting offloaded. */
-               if (!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE) &&
-                   (tunnel->parms.o_flags&GRE_CSUM)) {
-                       int offset = skb_transport_offset(skb);
-
-                       if (skb_has_shared_frag(skb)) {
-                               err = __skb_linearize(skb);
-                               if (err)
-                                       goto tx_error;
-                       }
-
-                       *ptr = 0;
-                       *(__sum16 *)ptr = csum_fold(skb_checksum(skb, offset,
-                                                                skb->len - offset,
-                                                                0));
-               }
+               tnl_params = &tunnel->parms.iph;
        }
 
-       nf_reset(skb);
+       __gre_xmit(skb, dev, tnl_params, skb->protocol);
 
-       pkt_len = skb->len - skb_transport_offset(skb);
-       err = ip_local_out(skb);
-       if (likely(net_xmit_eval(err) == 0)) {
-               u64_stats_update_begin(&tstats->syncp);
-               tstats->tx_bytes += pkt_len;
-               tstats->tx_packets++;
-               u64_stats_update_end(&tstats->syncp);
-       } else {
-               dev->stats.tx_errors++;
-               dev->stats.tx_aborted_errors++;
-       }
        return NETDEV_TX_OK;
 
-#if IS_ENABLED(CONFIG_IPV6)
-tx_error_icmp:
-       dst_link_failure(skb);
-#endif
-tx_error:
-       dev->stats.tx_errors++;
+free_skb:
        dev_kfree_skb(skb);
+out:
+       dev->stats.tx_dropped++;
        return NETDEV_TX_OK;
 }
 
-static int ipgre_tunnel_bind_dev(struct net_device *dev)
+static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
+                               struct net_device *dev)
 {
-       struct net_device *tdev = NULL;
-       struct ip_tunnel *tunnel;
-       const struct iphdr *iph;
-       int hlen = LL_MAX_HEADER;
-       int mtu = ETH_DATA_LEN;
-       int addend = sizeof(struct iphdr) + 4;
-
-       tunnel = netdev_priv(dev);
-       iph = &tunnel->parms.iph;
-
-       /* Guess output device to choose reasonable mtu and needed_headroom */
-
-       if (iph->daddr) {
-               struct flowi4 fl4;
-               struct rtable *rt;
-
-               rt = ip_route_output_gre(dev_net(dev), &fl4,
-                                        iph->daddr, iph->saddr,
-                                        tunnel->parms.o_key,
-                                        RT_TOS(iph->tos),
-                                        tunnel->parms.link);
-               if (!IS_ERR(rt)) {
-                       tdev = rt->dst.dev;
-                       ip_rt_put(rt);
-               }
-
-               if (dev->type != ARPHRD_ETHER)
-                       dev->flags |= IFF_POINTOPOINT;
-       }
+       struct ip_tunnel *tunnel = netdev_priv(dev);
 
-       if (!tdev && tunnel->parms.link)
-               tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link);
+       skb = handle_offloads(tunnel, skb);
+       if (IS_ERR(skb))
+               goto out;
 
-       if (tdev) {
-               hlen = tdev->hard_header_len + tdev->needed_headroom;
-               mtu = tdev->mtu;
-       }
-       dev->iflink = tunnel->parms.link;
-
-       /* Precalculate GRE options length */
-       if (tunnel->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
-               if (tunnel->parms.o_flags&GRE_CSUM)
-                       addend += 4;
-               if (tunnel->parms.o_flags&GRE_KEY)
-                       addend += 4;
-               if (tunnel->parms.o_flags&GRE_SEQ)
-                       addend += 4;
-       }
-       dev->needed_headroom = addend + hlen;
-       mtu -= dev->hard_header_len + addend;
+       if (skb_cow_head(skb, dev->needed_headroom))
+               goto free_skb;
 
-       if (mtu < 68)
-               mtu = 68;
+       __gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_TEB));
 
-       tunnel->hlen = addend;
-       /* TCP offload with GRE SEQ is not supported. */
-       if (!(tunnel->parms.o_flags & GRE_SEQ)) {
-               dev->features           |= NETIF_F_GSO_SOFTWARE;
-               dev->hw_features        |= NETIF_F_GSO_SOFTWARE;
-       }
+       return NETDEV_TX_OK;
 
-       return mtu;
+free_skb:
+       dev_kfree_skb(skb);
+out:
+       dev->stats.tx_dropped++;
+       return NETDEV_TX_OK;
 }
 
-static int
-ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
+static int ipgre_tunnel_ioctl(struct net_device *dev,
+                             struct ifreq *ifr, int cmd)
 {
        int err = 0;
        struct ip_tunnel_parm p;
-       struct ip_tunnel *t;
-       struct net *net = dev_net(dev);
-       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
-
-       switch (cmd) {
-       case SIOCGETTUNNEL:
-               t = NULL;
-               if (dev == ign->fb_tunnel_dev) {
-                       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
-                               err = -EFAULT;
-                               break;
-                       }
-                       t = ipgre_tunnel_locate(net, &p, 0);
-               }
-               if (t == NULL)
-                       t = netdev_priv(dev);
-               memcpy(&p, &t->parms, sizeof(p));
-               if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
-                       err = -EFAULT;
-               break;
-
-       case SIOCADDTUNNEL:
-       case SIOCCHGTUNNEL:
-               err = -EPERM;
-               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
-                       goto done;
-
-               err = -EFAULT;
-               if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
-                       goto done;
-
-               err = -EINVAL;
-               if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE ||
-                   p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) ||
-                   ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)))
-                       goto done;
-               if (p.iph.ttl)
-                       p.iph.frag_off |= htons(IP_DF);
-
-               if (!(p.i_flags&GRE_KEY))
-                       p.i_key = 0;
-               if (!(p.o_flags&GRE_KEY))
-                       p.o_key = 0;
-
-               t = ipgre_tunnel_locate(net, &p, cmd == SIOCADDTUNNEL);
-
-               if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
-                       if (t != NULL) {
-                               if (t->dev != dev) {
-                                       err = -EEXIST;
-                                       break;
-                               }
-                       } else {
-                               unsigned int nflags = 0;
-
-                               t = netdev_priv(dev);
-
-                               if (ipv4_is_multicast(p.iph.daddr))
-                                       nflags = IFF_BROADCAST;
-                               else if (p.iph.daddr)
-                                       nflags = IFF_POINTOPOINT;
-
-                               if ((dev->flags^nflags)&(IFF_POINTOPOINT|IFF_BROADCAST)) {
-                                       err = -EINVAL;
-                                       break;
-                               }
-                               ipgre_tunnel_unlink(ign, t);
-                               synchronize_net();
-                               t->parms.iph.saddr = p.iph.saddr;
-                               t->parms.iph.daddr = p.iph.daddr;
-                               t->parms.i_key = p.i_key;
-                               t->parms.o_key = p.o_key;
-                               memcpy(dev->dev_addr, &p.iph.saddr, 4);
-                               memcpy(dev->broadcast, &p.iph.daddr, 4);
-                               ipgre_tunnel_link(ign, t);
-                               netdev_state_change(dev);
-                       }
-               }
-
-               if (t) {
-                       err = 0;
-                       if (cmd == SIOCCHGTUNNEL) {
-                               t->parms.iph.ttl = p.iph.ttl;
-                               t->parms.iph.tos = p.iph.tos;
-                               t->parms.iph.frag_off = p.iph.frag_off;
-                               if (t->parms.link != p.link) {
-                                       t->parms.link = p.link;
-                                       dev->mtu = ipgre_tunnel_bind_dev(dev);
-                                       netdev_state_change(dev);
-                               }
-                       }
-                       if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
-                               err = -EFAULT;
-               } else
-                       err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
-               break;
-
-       case SIOCDELTUNNEL:
-               err = -EPERM;
-               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
-                       goto done;
-
-               if (dev == ign->fb_tunnel_dev) {
-                       err = -EFAULT;
-                       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
-                               goto done;
-                       err = -ENOENT;
-                       if ((t = ipgre_tunnel_locate(net, &p, 0)) == NULL)
-                               goto done;
-                       err = -EPERM;
-                       if (t == netdev_priv(ign->fb_tunnel_dev))
-                               goto done;
-                       dev = t->dev;
-               }
-               unregister_netdevice(dev);
-               err = 0;
-               break;
 
-       default:
-               err = -EINVAL;
+       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+               return -EFAULT;
+       if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE ||
+           p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) ||
+           ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))) {
+               return -EINVAL;
        }
+       p.i_flags = gre_flags_to_tnl_flags(p.i_flags);
+       p.o_flags = gre_flags_to_tnl_flags(p.o_flags);
 
-done:
-       return err;
-}
+       err = ip_tunnel_ioctl(dev, &p, cmd);
+       if (err)
+               return err;
 
-static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
-{
-       struct ip_tunnel *tunnel = netdev_priv(dev);
-       if (new_mtu < 68 ||
-           new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen)
-               return -EINVAL;
-       dev->mtu = new_mtu;
+       p.i_flags = tnl_flags_to_gre_flags(p.i_flags);
+       p.o_flags = tnl_flags_to_gre_flags(p.o_flags);
+
+       if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+               return -EFAULT;
        return 0;
 }
 
@@ -1277,25 +548,23 @@ static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
    ...
    ftp fec0:6666:6666::193.233.7.65
    ...
-
  */
-
 static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
                        unsigned short type,
                        const void *daddr, const void *saddr, unsigned int len)
 {
        struct ip_tunnel *t = netdev_priv(dev);
-       struct iphdr *iph = (struct iphdr *)skb_push(skb, t->hlen);
-       __be16 *p = (__be16 *)(iph+1);
+       struct iphdr *iph;
+       struct gre_base_hdr *greh;
 
-       memcpy(iph, &t->parms.iph, sizeof(struct iphdr));
-       p[0]            = t->parms.o_flags;
-       p[1]            = htons(type);
+       iph = (struct iphdr *)skb_push(skb, t->hlen + sizeof(*iph));
+       greh = (struct gre_base_hdr *)(iph+1);
+       greh->flags = tnl_flags_to_gre_flags(t->parms.o_flags);
+       greh->protocol = htons(type);
 
-       /*
-        *      Set the source hardware address.
-        */
+       memcpy(iph, &t->parms.iph, sizeof(struct iphdr));
 
+       /* Set the source hardware address. */
        if (saddr)
                memcpy(&iph->saddr, saddr, 4);
        if (daddr)
@@ -1303,7 +572,7 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
        if (iph->daddr)
                return t->hlen;
 
-       return -t->hlen;
+       return -(t->hlen + sizeof(*iph));
 }
 
 static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
@@ -1357,31 +626,21 @@ static int ipgre_close(struct net_device *dev)
        }
        return 0;
 }
-
 #endif
 
 static const struct net_device_ops ipgre_netdev_ops = {
        .ndo_init               = ipgre_tunnel_init,
-       .ndo_uninit             = ipgre_tunnel_uninit,
+       .ndo_uninit             = ip_tunnel_uninit,
 #ifdef CONFIG_NET_IPGRE_BROADCAST
        .ndo_open               = ipgre_open,
        .ndo_stop               = ipgre_close,
 #endif
-       .ndo_start_xmit         = ipgre_tunnel_xmit,
+       .ndo_start_xmit         = ipgre_xmit,
        .ndo_do_ioctl           = ipgre_tunnel_ioctl,
-       .ndo_change_mtu         = ipgre_tunnel_change_mtu,
-       .ndo_get_stats64        = ipgre_get_stats64,
+       .ndo_change_mtu         = ip_tunnel_change_mtu,
+       .ndo_get_stats64        = ip_tunnel_get_stats64,
 };
 
-static void ipgre_dev_free(struct net_device *dev)
-{
-       struct ip_tunnel *tunnel = netdev_priv(dev);
-
-       gro_cells_destroy(&tunnel->gro_cells);
-       free_percpu(dev->tstats);
-       free_netdev(dev);
-}
-
 #define GRE_FEATURES (NETIF_F_SG |             \
                      NETIF_F_FRAGLIST |        \
                      NETIF_F_HIGHDMA |         \
@@ -1390,35 +649,48 @@ static void ipgre_dev_free(struct net_device *dev)
 static void ipgre_tunnel_setup(struct net_device *dev)
 {
        dev->netdev_ops         = &ipgre_netdev_ops;
-       dev->destructor         = ipgre_dev_free;
+       ip_tunnel_setup(dev, ipgre_net_id);
+}
 
-       dev->type               = ARPHRD_IPGRE;
-       dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
+static void __gre_tunnel_init(struct net_device *dev)
+{
+       struct ip_tunnel *tunnel;
+
+       tunnel = netdev_priv(dev);
+       tunnel->hlen = ip_gre_calc_hlen(tunnel->parms.o_flags);
+       tunnel->parms.iph.protocol = IPPROTO_GRE;
+
+       dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
        dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr) - 4;
-       dev->flags              = IFF_NOARP;
-       dev->iflink             = 0;
-       dev->addr_len           = 4;
-       dev->features           |= NETIF_F_NETNS_LOCAL;
-       dev->priv_flags         &= ~IFF_XMIT_DST_RELEASE;
 
-       dev->features           |= GRE_FEATURES;
+       dev->features           |= NETIF_F_NETNS_LOCAL | GRE_FEATURES;
        dev->hw_features        |= GRE_FEATURES;
+
+       if (!(tunnel->parms.o_flags & TUNNEL_SEQ)) {
+               /* TCP offload with GRE SEQ is not supported. */
+               dev->features    |= NETIF_F_GSO_SOFTWARE;
+               dev->hw_features |= NETIF_F_GSO_SOFTWARE;
+               /* Can use a lockless transmit, unless we generate
+                * output sequences
+                */
+               dev->features |= NETIF_F_LLTX;
+       }
 }
 
 static int ipgre_tunnel_init(struct net_device *dev)
 {
-       struct ip_tunnel *tunnel;
-       struct iphdr *iph;
-       int err;
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct iphdr *iph = &tunnel->parms.iph;
 
-       tunnel = netdev_priv(dev);
-       iph = &tunnel->parms.iph;
+       __gre_tunnel_init(dev);
 
-       tunnel->dev = dev;
-       strcpy(tunnel->parms.name, dev->name);
+       memcpy(dev->dev_addr, &iph->saddr, 4);
+       memcpy(dev->broadcast, &iph->daddr, 4);
 
-       memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
-       memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+       dev->type               = ARPHRD_IPGRE;
+       dev->flags              = IFF_NOARP;
+       dev->priv_flags         &= ~IFF_XMIT_DST_RELEASE;
+       dev->addr_len           = 4;
 
        if (iph->daddr) {
 #ifdef CONFIG_NET_IPGRE_BROADCAST
@@ -1432,106 +704,30 @@ static int ipgre_tunnel_init(struct net_device *dev)
        } else
                dev->header_ops = &ipgre_header_ops;
 
-       dev->tstats = alloc_percpu(struct pcpu_tstats);
-       if (!dev->tstats)
-               return -ENOMEM;
-
-       err = gro_cells_init(&tunnel->gro_cells, dev);
-       if (err) {
-               free_percpu(dev->tstats);
-               return err;
-       }
-
-       return 0;
-}
-
-static void ipgre_fb_tunnel_init(struct net_device *dev)
-{
-       struct ip_tunnel *tunnel = netdev_priv(dev);
-       struct iphdr *iph = &tunnel->parms.iph;
-
-       tunnel->dev = dev;
-       strcpy(tunnel->parms.name, dev->name);
-
-       iph->version            = 4;
-       iph->protocol           = IPPROTO_GRE;
-       iph->ihl                = 5;
-       tunnel->hlen            = sizeof(struct iphdr) + 4;
-
-       dev_hold(dev);
+       return ip_tunnel_init(dev);
 }
 
-
 static const struct gre_protocol ipgre_protocol = {
        .handler     = ipgre_rcv,
        .err_handler = ipgre_err,
 };
 
-static void ipgre_destroy_tunnels(struct ipgre_net *ign, struct list_head *head)
-{
-       int prio;
-
-       for (prio = 0; prio < 4; prio++) {
-               int h;
-               for (h = 0; h < HASH_SIZE; h++) {
-                       struct ip_tunnel *t;
-
-                       t = rtnl_dereference(ign->tunnels[prio][h]);
-
-                       while (t != NULL) {
-                               unregister_netdevice_queue(t->dev, head);
-                               t = rtnl_dereference(t->next);
-                       }
-               }
-       }
-}
-
 static int __net_init ipgre_init_net(struct net *net)
 {
-       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
-       int err;
-
-       ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "gre0",
-                                          ipgre_tunnel_setup);
-       if (!ign->fb_tunnel_dev) {
-               err = -ENOMEM;
-               goto err_alloc_dev;
-       }
-       dev_net_set(ign->fb_tunnel_dev, net);
-
-       ipgre_fb_tunnel_init(ign->fb_tunnel_dev);
-       ign->fb_tunnel_dev->rtnl_link_ops = &ipgre_link_ops;
-
-       if ((err = register_netdev(ign->fb_tunnel_dev)))
-               goto err_reg_dev;
-
-       rcu_assign_pointer(ign->tunnels_wc[0],
-                          netdev_priv(ign->fb_tunnel_dev));
-       return 0;
-
-err_reg_dev:
-       ipgre_dev_free(ign->fb_tunnel_dev);
-err_alloc_dev:
-       return err;
+       return ip_tunnel_init_net(net, ipgre_net_id, &ipgre_link_ops, NULL);
 }
 
 static void __net_exit ipgre_exit_net(struct net *net)
 {
-       struct ipgre_net *ign;
-       LIST_HEAD(list);
-
-       ign = net_generic(net, ipgre_net_id);
-       rtnl_lock();
-       ipgre_destroy_tunnels(ign, &list);
-       unregister_netdevice_many(&list);
-       rtnl_unlock();
+       struct ip_tunnel_net *itn = net_generic(net, ipgre_net_id);
+       ip_tunnel_delete_net(itn);
 }
 
 static struct pernet_operations ipgre_net_ops = {
        .init = ipgre_init_net,
        .exit = ipgre_exit_net,
        .id   = &ipgre_net_id,
-       .size = sizeof(struct ipgre_net),
+       .size = sizeof(struct ip_tunnel_net),
 };
 
 static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -1576,8 +772,8 @@ out:
        return ipgre_tunnel_validate(tb, data);
 }
 
-static void ipgre_netlink_parms(struct nlattr *data[],
-                               struct ip_tunnel_parm *parms)
+static void ipgre_netlink_parms(struct nlattr *data[], struct nlattr *tb[],
+                              struct ip_tunnel_parm *parms)
 {
        memset(parms, 0, sizeof(*parms));
 
@@ -1590,10 +786,10 @@ static void ipgre_netlink_parms(struct nlattr *data[],
                parms->link = nla_get_u32(data[IFLA_GRE_LINK]);
 
        if (data[IFLA_GRE_IFLAGS])
-               parms->i_flags = nla_get_be16(data[IFLA_GRE_IFLAGS]);
+               parms->i_flags = gre_flags_to_tnl_flags(nla_get_be16(data[IFLA_GRE_IFLAGS]));
 
        if (data[IFLA_GRE_OFLAGS])
-               parms->o_flags = nla_get_be16(data[IFLA_GRE_OFLAGS]);
+               parms->o_flags = gre_flags_to_tnl_flags(nla_get_be16(data[IFLA_GRE_OFLAGS]));
 
        if (data[IFLA_GRE_IKEY])
                parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]);
@@ -1617,148 +813,46 @@ static void ipgre_netlink_parms(struct nlattr *data[],
                parms->iph.frag_off = htons(IP_DF);
 }
 
-static int ipgre_tap_init(struct net_device *dev)
+static int gre_tap_init(struct net_device *dev)
 {
-       struct ip_tunnel *tunnel;
-
-       tunnel = netdev_priv(dev);
-
-       tunnel->dev = dev;
-       strcpy(tunnel->parms.name, dev->name);
+       __gre_tunnel_init(dev);
 
-       ipgre_tunnel_bind_dev(dev);
-
-       dev->tstats = alloc_percpu(struct pcpu_tstats);
-       if (!dev->tstats)
-               return -ENOMEM;
-
-       return 0;
+       return ip_tunnel_init(dev);
 }
 
-static const struct net_device_ops ipgre_tap_netdev_ops = {
-       .ndo_init               = ipgre_tap_init,
-       .ndo_uninit             = ipgre_tunnel_uninit,
-       .ndo_start_xmit         = ipgre_tunnel_xmit,
+static const struct net_device_ops gre_tap_netdev_ops = {
+       .ndo_init               = gre_tap_init,
+       .ndo_uninit             = ip_tunnel_uninit,
+       .ndo_start_xmit         = gre_tap_xmit,
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
-       .ndo_change_mtu         = ipgre_tunnel_change_mtu,
-       .ndo_get_stats64        = ipgre_get_stats64,
+       .ndo_change_mtu         = ip_tunnel_change_mtu,
+       .ndo_get_stats64        = ip_tunnel_get_stats64,
 };
 
 static void ipgre_tap_setup(struct net_device *dev)
 {
-
        ether_setup(dev);
-
-       dev->netdev_ops         = &ipgre_tap_netdev_ops;
-       dev->destructor         = ipgre_dev_free;
-
-       dev->iflink             = 0;
-       dev->features           |= NETIF_F_NETNS_LOCAL;
-
-       dev->features           |= GRE_FEATURES;
-       dev->hw_features        |= GRE_FEATURES;
+       dev->netdev_ops         = &gre_tap_netdev_ops;
+       ip_tunnel_setup(dev, gre_tap_net_id);
 }
 
-static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[],
-                        struct nlattr *data[])
+static int ipgre_newlink(struct net *src_net, struct net_device *dev,
+                        struct nlattr *tb[], struct nlattr *data[])
 {
-       struct ip_tunnel *nt;
-       struct net *net = dev_net(dev);
-       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
-       int mtu;
-       int err;
-
-       nt = netdev_priv(dev);
-       ipgre_netlink_parms(data, &nt->parms);
-
-       if (ipgre_tunnel_find(net, &nt->parms, dev->type))
-               return -EEXIST;
-
-       if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
-               eth_hw_addr_random(dev);
-
-       mtu = ipgre_tunnel_bind_dev(dev);
-       if (!tb[IFLA_MTU])
-               dev->mtu = mtu;
-
-       /* Can use a lockless transmit, unless we generate output sequences */
-       if (!(nt->parms.o_flags & GRE_SEQ))
-               dev->features |= NETIF_F_LLTX;
-
-       err = register_netdevice(dev);
-       if (err)
-               goto out;
-
-       dev_hold(dev);
-       ipgre_tunnel_link(ign, nt);
+       struct ip_tunnel_parm p;
 
-out:
-       return err;
+       ipgre_netlink_parms(data, tb, &p);
+       return ip_tunnel_newlink(dev, tb, &p);
 }
 
 static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
                            struct nlattr *data[])
 {
-       struct ip_tunnel *t, *nt;
-       struct net *net = dev_net(dev);
-       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
        struct ip_tunnel_parm p;
-       int mtu;
-
-       if (dev == ign->fb_tunnel_dev)
-               return -EINVAL;
-
-       nt = netdev_priv(dev);
-       ipgre_netlink_parms(data, &p);
-
-       t = ipgre_tunnel_locate(net, &p, 0);
-
-       if (t) {
-               if (t->dev != dev)
-                       return -EEXIST;
-       } else {
-               t = nt;
-
-               if (dev->type != ARPHRD_ETHER) {
-                       unsigned int nflags = 0;
-
-                       if (ipv4_is_multicast(p.iph.daddr))
-                               nflags = IFF_BROADCAST;
-                       else if (p.iph.daddr)
-                               nflags = IFF_POINTOPOINT;
-
-                       if ((dev->flags ^ nflags) &
-                           (IFF_POINTOPOINT | IFF_BROADCAST))
-                               return -EINVAL;
-               }
 
-               ipgre_tunnel_unlink(ign, t);
-               t->parms.iph.saddr = p.iph.saddr;
-               t->parms.iph.daddr = p.iph.daddr;
-               t->parms.i_key = p.i_key;
-               if (dev->type != ARPHRD_ETHER) {
-                       memcpy(dev->dev_addr, &p.iph.saddr, 4);
-                       memcpy(dev->broadcast, &p.iph.daddr, 4);
-               }
-               ipgre_tunnel_link(ign, t);
-               netdev_state_change(dev);
-       }
-
-       t->parms.o_key = p.o_key;
-       t->parms.iph.ttl = p.iph.ttl;
-       t->parms.iph.tos = p.iph.tos;
-       t->parms.iph.frag_off = p.iph.frag_off;
-
-       if (t->parms.link != p.link) {
-               t->parms.link = p.link;
-               mtu = ipgre_tunnel_bind_dev(dev);
-               if (!tb[IFLA_MTU])
-                       dev->mtu = mtu;
-               netdev_state_change(dev);
-       }
-
-       return 0;
+       ipgre_netlink_parms(data, tb, &p);
+       return ip_tunnel_changelink(dev, tb, &p);
 }
 
 static size_t ipgre_get_size(const struct net_device *dev)
@@ -1793,8 +887,8 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
        struct ip_tunnel_parm *p = &t->parms;
 
        if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
-           nla_put_be16(skb, IFLA_GRE_IFLAGS, p->i_flags) ||
-           nla_put_be16(skb, IFLA_GRE_OFLAGS, p->o_flags) ||
+           nla_put_be16(skb, IFLA_GRE_IFLAGS, tnl_flags_to_gre_flags(p->i_flags)) ||
+           nla_put_be16(skb, IFLA_GRE_OFLAGS, tnl_flags_to_gre_flags(p->o_flags)) ||
            nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
            nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
            nla_put_be32(skb, IFLA_GRE_LOCAL, p->iph.saddr) ||
@@ -1832,6 +926,7 @@ static struct rtnl_link_ops ipgre_link_ops __read_mostly = {
        .validate       = ipgre_tunnel_validate,
        .newlink        = ipgre_newlink,
        .changelink     = ipgre_changelink,
+       .dellink        = ip_tunnel_dellink,
        .get_size       = ipgre_get_size,
        .fill_info      = ipgre_fill_info,
 };
@@ -1845,13 +940,28 @@ static struct rtnl_link_ops ipgre_tap_ops __read_mostly = {
        .validate       = ipgre_tap_validate,
        .newlink        = ipgre_newlink,
        .changelink     = ipgre_changelink,
+       .dellink        = ip_tunnel_dellink,
        .get_size       = ipgre_get_size,
        .fill_info      = ipgre_fill_info,
 };
 
-/*
- *     And now the modules code and kernel interface.
- */
+static int __net_init ipgre_tap_init_net(struct net *net)
+{
+       return ip_tunnel_init_net(net, gre_tap_net_id, &ipgre_tap_ops, NULL);
+}
+
+static void __net_exit ipgre_tap_exit_net(struct net *net)
+{
+       struct ip_tunnel_net *itn = net_generic(net, gre_tap_net_id);
+       ip_tunnel_delete_net(itn);
+}
+
+static struct pernet_operations ipgre_tap_net_ops = {
+       .init = ipgre_tap_init_net,
+       .exit = ipgre_tap_exit_net,
+       .id   = &gre_tap_net_id,
+       .size = sizeof(struct ip_tunnel_net),
+};
 
 static int __init ipgre_init(void)
 {
@@ -1863,6 +973,10 @@ static int __init ipgre_init(void)
        if (err < 0)
                return err;
 
+       err = register_pernet_device(&ipgre_tap_net_ops);
+       if (err < 0)
+               goto pnet_tap_faied;
+
        err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
        if (err < 0) {
                pr_info("%s: can't add protocol\n", __func__);
@@ -1877,16 +991,17 @@ static int __init ipgre_init(void)
        if (err < 0)
                goto tap_ops_failed;
 
-out:
-       return err;
+       return 0;
 
 tap_ops_failed:
        rtnl_link_unregister(&ipgre_link_ops);
 rtnl_link_failed:
        gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
 add_proto_failed:
+       unregister_pernet_device(&ipgre_tap_net_ops);
+pnet_tap_faied:
        unregister_pernet_device(&ipgre_net_ops);
-       goto out;
+       return err;
 }
 
 static void __exit ipgre_fini(void)
@@ -1895,6 +1010,7 @@ static void __exit ipgre_fini(void)
        rtnl_link_unregister(&ipgre_link_ops);
        if (gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0)
                pr_info("%s: can't remove protocol\n", __func__);
+       unregister_pernet_device(&ipgre_tap_net_ops);
        unregister_pernet_device(&ipgre_net_ops);
 }
 
@@ -1904,3 +1020,4 @@ MODULE_LICENSE("GPL");
 MODULE_ALIAS_RTNL_LINK("gre");
 MODULE_ALIAS_RTNL_LINK("gretap");
 MODULE_ALIAS_NETDEV("gre0");
+MODULE_ALIAS_NETDEV("gretap0");
index 5e12dca7b3dd4914af0f1f0e333c59f6547968f8..147abf5275aa6604df58c3b022eaeceaee10df4f 100644 (file)
@@ -430,8 +430,7 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->tc_index = from->tc_index;
 #endif
        nf_copy(to, from);
-#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
-    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
        to->nf_trace = from->nf_trace;
 #endif
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
new file mode 100644 (file)
index 0000000..e4147ec
--- /dev/null
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 2013 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#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>
+#include <linux/igmp.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/rculist.h>
+
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/ip_tunnels.h>
+#include <net/arp.h>
+#include <net/checksum.h>
+#include <net/dsfield.h>
+#include <net/inet_ecn.h>
+#include <net/xfrm.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/rtnetlink.h>
+
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#endif
+
+static unsigned int ip_tunnel_hash(struct ip_tunnel_net *itn,
+                                  __be32 key, __be32 remote)
+{
+       return hash_32((__force u32)key ^ (__force u32)remote,
+                        IP_TNL_HASH_BITS);
+}
+
+/* Often modified stats are per cpu, other are shared (netdev->stats) */
+struct rtnl_link_stats64 *ip_tunnel_get_stats64(struct net_device *dev,
+                                               struct rtnl_link_stats64 *tot)
+{
+       int i;
+
+       for_each_possible_cpu(i) {
+               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
+               u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
+               unsigned int start;
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&tstats->syncp);
+                       rx_packets = tstats->rx_packets;
+                       tx_packets = tstats->tx_packets;
+                       rx_bytes = tstats->rx_bytes;
+                       tx_bytes = tstats->tx_bytes;
+               } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
+
+               tot->rx_packets += rx_packets;
+               tot->tx_packets += tx_packets;
+               tot->rx_bytes   += rx_bytes;
+               tot->tx_bytes   += tx_bytes;
+       }
+
+       tot->multicast = dev->stats.multicast;
+
+       tot->rx_crc_errors = dev->stats.rx_crc_errors;
+       tot->rx_fifo_errors = dev->stats.rx_fifo_errors;
+       tot->rx_length_errors = dev->stats.rx_length_errors;
+       tot->rx_frame_errors = dev->stats.rx_frame_errors;
+       tot->rx_errors = dev->stats.rx_errors;
+
+       tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
+       tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
+       tot->tx_dropped = dev->stats.tx_dropped;
+       tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
+       tot->tx_errors = dev->stats.tx_errors;
+
+       tot->collisions  = dev->stats.collisions;
+
+       return tot;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_get_stats64);
+
+static bool ip_tunnel_key_match(const struct ip_tunnel_parm *p,
+                               __be16 flags, __be32 key)
+{
+       if (p->i_flags & TUNNEL_KEY) {
+               if (flags & TUNNEL_KEY)
+                       return key == p->i_key;
+               else
+                       /* key expected, none present */
+                       return false;
+       } else
+               return !(flags & TUNNEL_KEY);
+}
+
+/* Fallback tunnel: no source, no destination, no key, no options
+
+   Tunnel hash table:
+   We require exact key match i.e. if a key is present in packet
+   it will match only tunnel with the same key; if it is not present,
+   it will match only keyless tunnel.
+
+   All keysless packets, if not matched configured keyless tunnels
+   will match fallback tunnel.
+   Given src, dst and key, find appropriate for input tunnel.
+*/
+struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
+                                  int link, __be16 flags,
+                                  __be32 remote, __be32 local,
+                                  __be32 key)
+{
+       unsigned int hash;
+       struct ip_tunnel *t, *cand = NULL;
+       struct hlist_head *head;
+
+       hash = ip_tunnel_hash(itn, key, remote);
+       head = &itn->tunnels[hash];
+
+       hlist_for_each_entry_rcu(t, head, hash_node) {
+               if (local != t->parms.iph.saddr ||
+                   remote != t->parms.iph.daddr ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (!ip_tunnel_key_match(&t->parms, flags, key))
+                       continue;
+
+               if (t->parms.link == link)
+                       return t;
+               else
+                       cand = t;
+       }
+
+       hlist_for_each_entry_rcu(t, head, hash_node) {
+               if (remote != t->parms.iph.daddr ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (!ip_tunnel_key_match(&t->parms, flags, key))
+                       continue;
+
+               if (t->parms.link == link)
+                       return t;
+               else if (!cand)
+                       cand = t;
+       }
+
+       hash = ip_tunnel_hash(itn, key, 0);
+       head = &itn->tunnels[hash];
+
+       hlist_for_each_entry_rcu(t, head, hash_node) {
+               if ((local != t->parms.iph.saddr &&
+                    (local != t->parms.iph.daddr ||
+                     !ipv4_is_multicast(local))) ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (!ip_tunnel_key_match(&t->parms, flags, key))
+                       continue;
+
+               if (t->parms.link == link)
+                       return t;
+               else if (!cand)
+                       cand = t;
+       }
+
+       if (flags & TUNNEL_NO_KEY)
+               goto skip_key_lookup;
+
+       hlist_for_each_entry_rcu(t, head, hash_node) {
+               if (t->parms.i_key != key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->parms.link == link)
+                       return t;
+               else if (!cand)
+                       cand = t;
+       }
+
+skip_key_lookup:
+       if (cand)
+               return cand;
+
+       if (itn->fb_tunnel_dev && itn->fb_tunnel_dev->flags & IFF_UP)
+               return netdev_priv(itn->fb_tunnel_dev);
+
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_lookup);
+
+static struct hlist_head *ip_bucket(struct ip_tunnel_net *itn,
+                                   struct ip_tunnel_parm *parms)
+{
+       unsigned int h;
+       __be32 remote;
+
+       if (parms->iph.daddr && !ipv4_is_multicast(parms->iph.daddr))
+               remote = parms->iph.daddr;
+       else
+               remote = 0;
+
+       h = ip_tunnel_hash(itn, parms->i_key, remote);
+       return &itn->tunnels[h];
+}
+
+static void ip_tunnel_add(struct ip_tunnel_net *itn, struct ip_tunnel *t)
+{
+       struct hlist_head *head = ip_bucket(itn, &t->parms);
+
+       hlist_add_head_rcu(&t->hash_node, head);
+}
+
+static void ip_tunnel_del(struct ip_tunnel *t)
+{
+       hlist_del_init_rcu(&t->hash_node);
+}
+
+static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn,
+                                       struct ip_tunnel_parm *parms,
+                                       int type)
+{
+       __be32 remote = parms->iph.daddr;
+       __be32 local = parms->iph.saddr;
+       __be32 key = parms->i_key;
+       int link = parms->link;
+       struct ip_tunnel *t = NULL;
+       struct hlist_head *head = ip_bucket(itn, parms);
+
+       hlist_for_each_entry_rcu(t, head, hash_node) {
+               if (local == t->parms.iph.saddr &&
+                   remote == t->parms.iph.daddr &&
+                   key == t->parms.i_key &&
+                   link == t->parms.link &&
+                   type == t->dev->type)
+                       break;
+       }
+       return t;
+}
+
+static struct net_device *__ip_tunnel_create(struct net *net,
+                                            const struct rtnl_link_ops *ops,
+                                            struct ip_tunnel_parm *parms)
+{
+       int err;
+       struct ip_tunnel *tunnel;
+       struct net_device *dev;
+       char name[IFNAMSIZ];
+
+       if (parms->name[0])
+               strlcpy(name, parms->name, IFNAMSIZ);
+       else {
+               if (strlen(ops->kind) > (IFNAMSIZ - 3)) {
+                       err = -E2BIG;
+                       goto failed;
+               }
+               strlcpy(name, ops->kind, IFNAMSIZ);
+               strncat(name, "%d", 2);
+       }
+
+       ASSERT_RTNL();
+       dev = alloc_netdev(ops->priv_size, name, ops->setup);
+       if (!dev) {
+               err = -ENOMEM;
+               goto failed;
+       }
+       dev_net_set(dev, net);
+
+       dev->rtnl_link_ops = ops;
+
+       tunnel = netdev_priv(dev);
+       tunnel->parms = *parms;
+
+       err = register_netdevice(dev);
+       if (err)
+               goto failed_free;
+
+       return dev;
+
+failed_free:
+       free_netdev(dev);
+failed:
+       return ERR_PTR(err);
+}
+
+static inline struct rtable *ip_route_output_tunnel(struct net *net,
+                                                   struct flowi4 *fl4,
+                                                   int proto,
+                                                   __be32 daddr, __be32 saddr,
+                                                   __be32 key, __u8 tos, int oif)
+{
+       memset(fl4, 0, sizeof(*fl4));
+       fl4->flowi4_oif = oif;
+       fl4->daddr = daddr;
+       fl4->saddr = saddr;
+       fl4->flowi4_tos = tos;
+       fl4->flowi4_proto = proto;
+       fl4->fl4_gre_key = key;
+       return ip_route_output_key(net, fl4);
+}
+
+static int ip_tunnel_bind_dev(struct net_device *dev)
+{
+       struct net_device *tdev = NULL;
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       const struct iphdr *iph;
+       int hlen = LL_MAX_HEADER;
+       int mtu = ETH_DATA_LEN;
+       int t_hlen = tunnel->hlen + sizeof(struct iphdr);
+
+       iph = &tunnel->parms.iph;
+
+       /* Guess output device to choose reasonable mtu and needed_headroom */
+       if (iph->daddr) {
+               struct flowi4 fl4;
+               struct rtable *rt;
+
+               rt = ip_route_output_tunnel(dev_net(dev), &fl4,
+                                           tunnel->parms.iph.protocol,
+                                           iph->daddr, iph->saddr,
+                                           tunnel->parms.o_key,
+                                           RT_TOS(iph->tos),
+                                           tunnel->parms.link);
+               if (!IS_ERR(rt)) {
+                       tdev = rt->dst.dev;
+                       ip_rt_put(rt);
+               }
+               if (dev->type != ARPHRD_ETHER)
+                       dev->flags |= IFF_POINTOPOINT;
+       }
+
+       if (!tdev && tunnel->parms.link)
+               tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link);
+
+       if (tdev) {
+               hlen = tdev->hard_header_len + tdev->needed_headroom;
+               mtu = tdev->mtu;
+       }
+       dev->iflink = tunnel->parms.link;
+
+       dev->needed_headroom = t_hlen + hlen;
+       mtu -= (dev->hard_header_len + t_hlen);
+
+       if (mtu < 68)
+               mtu = 68;
+
+       return mtu;
+}
+
+static struct ip_tunnel *ip_tunnel_create(struct net *net,
+                                         struct ip_tunnel_net *itn,
+                                         struct ip_tunnel_parm *parms)
+{
+       struct ip_tunnel *nt, *fbt;
+       struct net_device *dev;
+
+       BUG_ON(!itn->fb_tunnel_dev);
+       fbt = netdev_priv(itn->fb_tunnel_dev);
+       dev = __ip_tunnel_create(net, itn->fb_tunnel_dev->rtnl_link_ops, parms);
+       if (IS_ERR(dev))
+               return NULL;
+
+       dev->mtu = ip_tunnel_bind_dev(dev);
+
+       nt = netdev_priv(dev);
+       ip_tunnel_add(itn, nt);
+       return nt;
+}
+
+int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
+                 const struct tnl_ptk_info *tpi, bool log_ecn_error)
+{
+       struct pcpu_tstats *tstats;
+       const struct iphdr *iph = ip_hdr(skb);
+       int err;
+
+       secpath_reset(skb);
+
+       skb->protocol = tpi->proto;
+
+       skb->mac_header = skb->network_header;
+       __pskb_pull(skb, tunnel->hlen);
+       skb_postpull_rcsum(skb, skb_transport_header(skb), tunnel->hlen);
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+       if (ipv4_is_multicast(iph->daddr)) {
+               /* Looped back packet, drop it! */
+               if (rt_is_output_route(skb_rtable(skb)))
+                       goto drop;
+               tunnel->dev->stats.multicast++;
+               skb->pkt_type = PACKET_BROADCAST;
+       }
+#endif
+
+       if ((!(tpi->flags&TUNNEL_CSUM) &&  (tunnel->parms.i_flags&TUNNEL_CSUM)) ||
+            ((tpi->flags&TUNNEL_CSUM) && !(tunnel->parms.i_flags&TUNNEL_CSUM))) {
+               tunnel->dev->stats.rx_crc_errors++;
+               tunnel->dev->stats.rx_errors++;
+               goto drop;
+       }
+
+       if (tunnel->parms.i_flags&TUNNEL_SEQ) {
+               if (!(tpi->flags&TUNNEL_SEQ) ||
+                   (tunnel->i_seqno && (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) {
+                       tunnel->dev->stats.rx_fifo_errors++;
+                       tunnel->dev->stats.rx_errors++;
+                       goto drop;
+               }
+               tunnel->i_seqno = ntohl(tpi->seq) + 1;
+       }
+
+       /* Warning: All skb pointers will be invalidated! */
+       if (tunnel->dev->type == ARPHRD_ETHER) {
+               if (!pskb_may_pull(skb, ETH_HLEN)) {
+                       tunnel->dev->stats.rx_length_errors++;
+                       tunnel->dev->stats.rx_errors++;
+                       goto drop;
+               }
+
+               iph = ip_hdr(skb);
+               skb->protocol = eth_type_trans(skb, tunnel->dev);
+               skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
+       }
+
+       skb->pkt_type = PACKET_HOST;
+       __skb_tunnel_rx(skb, tunnel->dev);
+
+       skb_reset_network_header(skb);
+       err = IP_ECN_decapsulate(iph, skb);
+       if (unlikely(err)) {
+               if (log_ecn_error)
+                       net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
+                                       &iph->saddr, iph->tos);
+               if (err > 1) {
+                       ++tunnel->dev->stats.rx_frame_errors;
+                       ++tunnel->dev->stats.rx_errors;
+                       goto drop;
+               }
+       }
+
+       tstats = this_cpu_ptr(tunnel->dev->tstats);
+       u64_stats_update_begin(&tstats->syncp);
+       tstats->rx_packets++;
+       tstats->rx_bytes += skb->len;
+       u64_stats_update_end(&tstats->syncp);
+
+       gro_cells_receive(&tunnel->gro_cells, skb);
+       return 0;
+
+drop:
+       kfree_skb(skb);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_rcv);
+
+void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
+                   const struct iphdr *tnl_params)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       const struct iphdr *inner_iph;
+       struct iphdr *iph;
+       struct flowi4 fl4;
+       u8     tos, ttl;
+       __be16 df;
+       struct rtable *rt;              /* Route to the other host */
+       struct net_device *tdev;        /* Device to other host */
+       unsigned int max_headroom;      /* The extra header space needed */
+       __be32 dst;
+       int mtu;
+
+       inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
+
+       dst = tnl_params->daddr;
+       if (dst == 0) {
+               /* NBMA tunnel */
+
+               if (skb_dst(skb) == NULL) {
+                       dev->stats.tx_fifo_errors++;
+                       goto tx_error;
+               }
+
+               if (skb->protocol == htons(ETH_P_IP)) {
+                       rt = skb_rtable(skb);
+                       dst = rt_nexthop(rt, inner_iph->daddr);
+               }
+#if IS_ENABLED(CONFIG_IPV6)
+               else if (skb->protocol == htons(ETH_P_IPV6)) {
+                       const struct in6_addr *addr6;
+                       struct neighbour *neigh;
+                       bool do_tx_error_icmp;
+                       int addr_type;
+
+                       neigh = dst_neigh_lookup(skb_dst(skb),
+                                                &ipv6_hdr(skb)->daddr);
+                       if (neigh == NULL)
+                               goto tx_error;
+
+                       addr6 = (const struct in6_addr *)&neigh->primary_key;
+                       addr_type = ipv6_addr_type(addr6);
+
+                       if (addr_type == IPV6_ADDR_ANY) {
+                               addr6 = &ipv6_hdr(skb)->daddr;
+                               addr_type = ipv6_addr_type(addr6);
+                       }
+
+                       if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
+                               do_tx_error_icmp = true;
+                       else {
+                               do_tx_error_icmp = false;
+                               dst = addr6->s6_addr32[3];
+                       }
+                       neigh_release(neigh);
+                       if (do_tx_error_icmp)
+                               goto tx_error_icmp;
+               }
+#endif
+               else
+                       goto tx_error;
+       }
+
+       tos = tnl_params->tos;
+       if (tos & 0x1) {
+               tos &= ~0x1;
+               if (skb->protocol == htons(ETH_P_IP))
+                       tos = inner_iph->tos;
+               else if (skb->protocol == htons(ETH_P_IPV6))
+                       tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph);
+       }
+
+       rt = ip_route_output_tunnel(dev_net(dev), &fl4,
+                                   tunnel->parms.iph.protocol,
+                                   dst, tnl_params->saddr,
+                                   tunnel->parms.o_key,
+                                   RT_TOS(tos),
+                                   tunnel->parms.link);
+       if (IS_ERR(rt)) {
+               dev->stats.tx_carrier_errors++;
+               goto tx_error;
+       }
+       tdev = rt->dst.dev;
+
+       if (tdev == dev) {
+               ip_rt_put(rt);
+               dev->stats.collisions++;
+               goto tx_error;
+       }
+
+       df = tnl_params->frag_off;
+
+       if (df)
+               mtu = dst_mtu(&rt->dst) - dev->hard_header_len
+                                       - sizeof(struct iphdr);
+       else
+               mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
+
+       if (skb_dst(skb))
+               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
+
+       if (skb->protocol == htons(ETH_P_IP)) {
+               df |= (inner_iph->frag_off&htons(IP_DF));
+
+               if (!skb_is_gso(skb) &&
+                   (inner_iph->frag_off&htons(IP_DF)) &&
+                    mtu < ntohs(inner_iph->tot_len)) {
+                       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+                       ip_rt_put(rt);
+                       goto tx_error;
+               }
+       }
+#if IS_ENABLED(CONFIG_IPV6)
+       else if (skb->protocol == htons(ETH_P_IPV6)) {
+               struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb);
+
+               if (rt6 && mtu < dst_mtu(skb_dst(skb)) &&
+                   mtu >= IPV6_MIN_MTU) {
+                       if ((tunnel->parms.iph.daddr &&
+                           !ipv4_is_multicast(tunnel->parms.iph.daddr)) ||
+                           rt6->rt6i_dst.plen == 128) {
+                               rt6->rt6i_flags |= RTF_MODIFIED;
+                               dst_metric_set(skb_dst(skb), RTAX_MTU, mtu);
+                       }
+               }
+
+               if (!skb_is_gso(skb) && mtu >= IPV6_MIN_MTU &&
+                   mtu < skb->len) {
+                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+                       ip_rt_put(rt);
+                       goto tx_error;
+               }
+       }
+#endif
+
+       if (tunnel->err_count > 0) {
+               if (time_before(jiffies,
+                               tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
+                       tunnel->err_count--;
+
+                       dst_link_failure(skb);
+               } else
+                       tunnel->err_count = 0;
+       }
+
+       ttl = tnl_params->ttl;
+       if (ttl == 0) {
+               if (skb->protocol == htons(ETH_P_IP))
+                       ttl = inner_iph->ttl;
+#if IS_ENABLED(CONFIG_IPV6)
+               else if (skb->protocol == htons(ETH_P_IPV6))
+                       ttl = ((const struct ipv6hdr *)inner_iph)->hop_limit;
+#endif
+               else
+                       ttl = ip4_dst_hoplimit(&rt->dst);
+       }
+
+       max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr)
+                                              + rt->dst.header_len;
+       if (max_headroom > dev->needed_headroom) {
+               dev->needed_headroom = max_headroom;
+               if (skb_cow_head(skb, dev->needed_headroom)) {
+                       dev->stats.tx_dropped++;
+                       dev_kfree_skb(skb);
+                       return;
+               }
+       }
+
+       skb_dst_drop(skb);
+       skb_dst_set(skb, &rt->dst);
+       memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+
+       /* Push down and install the IP header. */
+       skb_push(skb, sizeof(struct iphdr));
+       skb_reset_network_header(skb);
+
+       iph = ip_hdr(skb);
+       inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
+
+       iph->version    =       4;
+       iph->ihl        =       sizeof(struct iphdr) >> 2;
+       iph->frag_off   =       df;
+       iph->protocol   =       tnl_params->protocol;
+       iph->tos        =       ip_tunnel_ecn_encap(tos, inner_iph, skb);
+       iph->daddr      =       fl4.daddr;
+       iph->saddr      =       fl4.saddr;
+       iph->ttl        =       ttl;
+       tunnel_ip_select_ident(skb, inner_iph, &rt->dst);
+
+       iptunnel_xmit(skb, dev);
+       return;
+
+#if IS_ENABLED(CONFIG_IPV6)
+tx_error_icmp:
+       dst_link_failure(skb);
+#endif
+tx_error:
+       dev->stats.tx_errors++;
+       dev_kfree_skb(skb);
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_xmit);
+
+static void ip_tunnel_update(struct ip_tunnel_net *itn,
+                            struct ip_tunnel *t,
+                            struct net_device *dev,
+                            struct ip_tunnel_parm *p,
+                            bool set_mtu)
+{
+       ip_tunnel_del(t);
+       t->parms.iph.saddr = p->iph.saddr;
+       t->parms.iph.daddr = p->iph.daddr;
+       t->parms.i_key = p->i_key;
+       t->parms.o_key = p->o_key;
+       if (dev->type != ARPHRD_ETHER) {
+               memcpy(dev->dev_addr, &p->iph.saddr, 4);
+               memcpy(dev->broadcast, &p->iph.daddr, 4);
+       }
+       ip_tunnel_add(itn, t);
+
+       t->parms.iph.ttl = p->iph.ttl;
+       t->parms.iph.tos = p->iph.tos;
+       t->parms.iph.frag_off = p->iph.frag_off;
+
+       if (t->parms.link != p->link) {
+               int mtu;
+
+               t->parms.link = p->link;
+               mtu = ip_tunnel_bind_dev(dev);
+               if (set_mtu)
+                       dev->mtu = mtu;
+       }
+       netdev_state_change(dev);
+}
+
+int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
+{
+       int err = 0;
+       struct ip_tunnel *t;
+       struct net *net = dev_net(dev);
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct ip_tunnel_net *itn = net_generic(net, tunnel->ip_tnl_net_id);
+
+       BUG_ON(!itn->fb_tunnel_dev);
+       switch (cmd) {
+       case SIOCGETTUNNEL:
+               t = NULL;
+               if (dev == itn->fb_tunnel_dev)
+                       t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
+               if (t == NULL)
+                       t = netdev_priv(dev);
+               memcpy(p, &t->parms, sizeof(*p));
+               break;
+
+       case SIOCADDTUNNEL:
+       case SIOCCHGTUNNEL:
+               err = -EPERM;
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+                       goto done;
+               if (p->iph.ttl)
+                       p->iph.frag_off |= htons(IP_DF);
+               if (!(p->i_flags&TUNNEL_KEY))
+                       p->i_key = 0;
+               if (!(p->o_flags&TUNNEL_KEY))
+                       p->o_key = 0;
+
+               t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
+
+               if (!t && (cmd == SIOCADDTUNNEL))
+                       t = ip_tunnel_create(net, itn, p);
+
+               if (dev != itn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
+                       if (t != NULL) {
+                               if (t->dev != dev) {
+                                       err = -EEXIST;
+                                       break;
+                               }
+                       } else {
+                               unsigned int nflags = 0;
+
+                               if (ipv4_is_multicast(p->iph.daddr))
+                                       nflags = IFF_BROADCAST;
+                               else if (p->iph.daddr)
+                                       nflags = IFF_POINTOPOINT;
+
+                               if ((dev->flags^nflags)&(IFF_POINTOPOINT|IFF_BROADCAST)) {
+                                       err = -EINVAL;
+                                       break;
+                               }
+
+                               t = netdev_priv(dev);
+                       }
+               }
+
+               if (t) {
+                       err = 0;
+                       ip_tunnel_update(itn, t, dev, p, true);
+               } else
+                       err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+               break;
+
+       case SIOCDELTUNNEL:
+               err = -EPERM;
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+                       goto done;
+
+               if (dev == itn->fb_tunnel_dev) {
+                       err = -ENOENT;
+                       t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
+                       if (t == NULL)
+                               goto done;
+                       err = -EPERM;
+                       if (t == netdev_priv(itn->fb_tunnel_dev))
+                               goto done;
+                       dev = t->dev;
+               }
+               unregister_netdevice(dev);
+               err = 0;
+               break;
+
+       default:
+               err = -EINVAL;
+       }
+
+done:
+       return err;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_ioctl);
+
+int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       int t_hlen = tunnel->hlen + sizeof(struct iphdr);
+
+       if (new_mtu < 68 ||
+           new_mtu > 0xFFF8 - dev->hard_header_len - t_hlen)
+               return -EINVAL;
+       dev->mtu = new_mtu;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_change_mtu);
+
+static void ip_tunnel_dev_free(struct net_device *dev)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+
+       gro_cells_destroy(&tunnel->gro_cells);
+       free_percpu(dev->tstats);
+       free_netdev(dev);
+}
+
+void ip_tunnel_dellink(struct net_device *dev, struct list_head *head)
+{
+       struct net *net = dev_net(dev);
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct ip_tunnel_net *itn;
+
+       itn = net_generic(net, tunnel->ip_tnl_net_id);
+
+       if (itn->fb_tunnel_dev != dev) {
+               ip_tunnel_del(netdev_priv(dev));
+               unregister_netdevice_queue(dev, head);
+       }
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_dellink);
+
+int __net_init ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
+                                 struct rtnl_link_ops *ops, char *devname)
+{
+       struct ip_tunnel_net *itn = net_generic(net, ip_tnl_net_id);
+       struct ip_tunnel_parm parms;
+
+       itn->tunnels = kzalloc(IP_TNL_HASH_SIZE * sizeof(struct hlist_head), GFP_KERNEL);
+       if (!itn->tunnels)
+               return -ENOMEM;
+
+       if (!ops) {
+               itn->fb_tunnel_dev = NULL;
+               return 0;
+       }
+       memset(&parms, 0, sizeof(parms));
+       if (devname)
+               strlcpy(parms.name, devname, IFNAMSIZ);
+
+       rtnl_lock();
+       itn->fb_tunnel_dev = __ip_tunnel_create(net, ops, &parms);
+       rtnl_unlock();
+       if (IS_ERR(itn->fb_tunnel_dev)) {
+               kfree(itn->tunnels);
+               return PTR_ERR(itn->fb_tunnel_dev);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_init_net);
+
+static void ip_tunnel_destroy(struct ip_tunnel_net *itn, struct list_head *head)
+{
+       int h;
+
+       for (h = 0; h < IP_TNL_HASH_SIZE; h++) {
+               struct ip_tunnel *t;
+               struct hlist_node *n;
+               struct hlist_head *thead = &itn->tunnels[h];
+
+               hlist_for_each_entry_safe(t, n, thead, hash_node)
+                       unregister_netdevice_queue(t->dev, head);
+       }
+       if (itn->fb_tunnel_dev)
+               unregister_netdevice_queue(itn->fb_tunnel_dev, head);
+}
+
+void __net_exit ip_tunnel_delete_net(struct ip_tunnel_net *itn)
+{
+       LIST_HEAD(list);
+
+       rtnl_lock();
+       ip_tunnel_destroy(itn, &list);
+       unregister_netdevice_many(&list);
+       rtnl_unlock();
+       kfree(itn->tunnels);
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_delete_net);
+
+int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[],
+                     struct ip_tunnel_parm *p)
+{
+       struct ip_tunnel *nt;
+       struct net *net = dev_net(dev);
+       struct ip_tunnel_net *itn;
+       int mtu;
+       int err;
+
+       nt = netdev_priv(dev);
+       itn = net_generic(net, nt->ip_tnl_net_id);
+
+       if (ip_tunnel_find(itn, p, dev->type))
+               return -EEXIST;
+
+       nt->parms = *p;
+       err = register_netdevice(dev);
+       if (err)
+               goto out;
+
+       if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
+               eth_hw_addr_random(dev);
+
+       mtu = ip_tunnel_bind_dev(dev);
+       if (!tb[IFLA_MTU])
+               dev->mtu = mtu;
+
+       ip_tunnel_add(itn, nt);
+
+out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_newlink);
+
+int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[],
+                        struct ip_tunnel_parm *p)
+{
+       struct ip_tunnel *t, *nt;
+       struct net *net = dev_net(dev);
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct ip_tunnel_net *itn = net_generic(net, tunnel->ip_tnl_net_id);
+
+       if (dev == itn->fb_tunnel_dev)
+               return -EINVAL;
+
+       nt = netdev_priv(dev);
+
+       t = ip_tunnel_find(itn, p, dev->type);
+
+       if (t) {
+               if (t->dev != dev)
+                       return -EEXIST;
+       } else {
+               t = nt;
+
+               if (dev->type != ARPHRD_ETHER) {
+                       unsigned int nflags = 0;
+
+                       if (ipv4_is_multicast(p->iph.daddr))
+                               nflags = IFF_BROADCAST;
+                       else if (p->iph.daddr)
+                               nflags = IFF_POINTOPOINT;
+
+                       if ((dev->flags ^ nflags) &
+                           (IFF_POINTOPOINT | IFF_BROADCAST))
+                               return -EINVAL;
+               }
+       }
+
+       ip_tunnel_update(itn, t, dev, p, !tb[IFLA_MTU]);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_changelink);
+
+int ip_tunnel_init(struct net_device *dev)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct iphdr *iph = &tunnel->parms.iph;
+       int err;
+
+       dev->destructor = ip_tunnel_dev_free;
+       dev->tstats = alloc_percpu(struct pcpu_tstats);
+       if (!dev->tstats)
+               return -ENOMEM;
+
+       err = gro_cells_init(&tunnel->gro_cells, dev);
+       if (err) {
+               free_percpu(dev->tstats);
+               return err;
+       }
+
+       tunnel->dev = dev;
+       strcpy(tunnel->parms.name, dev->name);
+       iph->version            = 4;
+       iph->ihl                = 5;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_init);
+
+void ip_tunnel_uninit(struct net_device *dev)
+{
+       struct net *net = dev_net(dev);
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct ip_tunnel_net *itn;
+
+       itn = net_generic(net, tunnel->ip_tnl_net_id);
+       /* fb_tunnel_dev will be unregisted in net-exit call. */
+       if (itn->fb_tunnel_dev != dev)
+               ip_tunnel_del(netdev_priv(dev));
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_uninit);
+
+/* Do least required initialization, rest of init is done in tunnel_init call */
+void ip_tunnel_setup(struct net_device *dev, int net_id)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       tunnel->ip_tnl_net_id = net_id;
+}
+EXPORT_SYMBOL_GPL(ip_tunnel_setup);
+
+MODULE_LICENSE("GPL");
index c3a4233c0ac290394c819882aeff5ba3800378f4..9d2bdb2c1d3f631ab1ff8c6d9314005be0fa0c99 100644 (file)
@@ -38,7 +38,7 @@
 #include <net/sock.h>
 #include <net/ip.h>
 #include <net/icmp.h>
-#include <net/ipip.h>
+#include <net/ip_tunnels.h>
 #include <net/inet_ecn.h>
 #include <net/xfrm.h>
 #include <net/net_namespace.h>
@@ -82,44 +82,6 @@ static int vti_tunnel_bind_dev(struct net_device *dev);
 } while (0)
 
 
-static struct rtnl_link_stats64 *vti_get_stats64(struct net_device *dev,
-                                                struct rtnl_link_stats64 *tot)
-{
-       int i;
-
-       for_each_possible_cpu(i) {
-               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
-               u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
-               unsigned int start;
-
-               do {
-                       start = u64_stats_fetch_begin_bh(&tstats->syncp);
-                       rx_packets = tstats->rx_packets;
-                       tx_packets = tstats->tx_packets;
-                       rx_bytes = tstats->rx_bytes;
-                       tx_bytes = tstats->tx_bytes;
-               } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
-
-               tot->rx_packets += rx_packets;
-               tot->tx_packets += tx_packets;
-               tot->rx_bytes   += rx_bytes;
-               tot->tx_bytes   += tx_bytes;
-       }
-
-       tot->multicast = dev->stats.multicast;
-       tot->rx_crc_errors = dev->stats.rx_crc_errors;
-       tot->rx_fifo_errors = dev->stats.rx_fifo_errors;
-       tot->rx_length_errors = dev->stats.rx_length_errors;
-       tot->rx_errors = dev->stats.rx_errors;
-       tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
-       tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
-       tot->tx_dropped = dev->stats.tx_dropped;
-       tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
-       tot->tx_errors = dev->stats.tx_errors;
-
-       return tot;
-}
-
 static struct ip_tunnel *vti_tunnel_lookup(struct net *net,
                                           __be32 remote, __be32 local)
 {
@@ -597,7 +559,7 @@ static const struct net_device_ops vti_netdev_ops = {
        .ndo_start_xmit = vti_tunnel_xmit,
        .ndo_do_ioctl   = vti_tunnel_ioctl,
        .ndo_change_mtu = vti_tunnel_change_mtu,
-       .ndo_get_stats64 = vti_get_stats64,
+       .ndo_get_stats64 = ip_tunnel_get_stats64,
 };
 
 static void vti_dev_free(struct net_device *dev)
index f01d1b1aff7f0a08b0020055ae65e26f792a0379..59cb8c7690561f3fc44ecc382557408af53cb5fb 100644 (file)
@@ -75,6 +75,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
        t->props.mode = x->props.mode;
        t->props.saddr.a4 = x->props.saddr.a4;
        t->props.flags = x->props.flags;
+       t->props.extra_flags = x->props.extra_flags;
        memcpy(&t->mark, &x->mark, sizeof(t->mark));
 
        if (xfrm_init_state(t))
index bf6c5cf31aed27ccba4aa25c4d049f0cc037e3cb..efa1138fa523be1ed228b66accbfa9ed75920265 100644 (file)
@@ -206,7 +206,7 @@ static int __init ic_open_devs(void)
        struct ic_device *d, **last;
        struct net_device *dev;
        unsigned short oflags;
-       unsigned long start;
+       unsigned long start, next_msg;
 
        last = &ic_first_dev;
        rtnl_lock();
@@ -263,12 +263,23 @@ static int __init ic_open_devs(void)
 
        /* wait for a carrier on at least one device */
        start = jiffies;
+       next_msg = start + msecs_to_jiffies(CONF_CARRIER_TIMEOUT/12);
        while (jiffies - start < msecs_to_jiffies(CONF_CARRIER_TIMEOUT)) {
+               int wait, elapsed;
+
                for_each_netdev(&init_net, dev)
                        if (ic_is_init_dev(dev) && netif_carrier_ok(dev))
                                goto have_carrier;
 
                msleep(1);
+
+               if time_before(jiffies, next_msg)
+                       continue;
+
+               elapsed = jiffies_to_msecs(jiffies - start);
+               wait = (CONF_CARRIER_TIMEOUT - elapsed + 500)/1000;
+               pr_info("Waiting up to %d more seconds for network.\n", wait);
+               next_msg = jiffies + msecs_to_jiffies(CONF_CARRIER_TIMEOUT/12);
        }
 have_carrier:
        rtnl_unlock();
index 8f024d41eefa9d828af4969b84134e8c94087ffd..77bfcce64fe568b3a162a9d36324061553275a5e 100644 (file)
 #include <net/sock.h>
 #include <net/ip.h>
 #include <net/icmp.h>
-#include <net/ipip.h>
+#include <net/ip_tunnels.h>
 #include <net/inet_ecn.h>
 #include <net/xfrm.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 
-#define HASH_SIZE  16
-#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
-
 static bool log_ecn_error = true;
 module_param(log_ecn_error, bool, 0644);
 MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
 
 static int ipip_net_id __read_mostly;
-struct ipip_net {
-       struct ip_tunnel __rcu *tunnels_r_l[HASH_SIZE];
-       struct ip_tunnel __rcu *tunnels_r[HASH_SIZE];
-       struct ip_tunnel __rcu *tunnels_l[HASH_SIZE];
-       struct ip_tunnel __rcu *tunnels_wc[1];
-       struct ip_tunnel __rcu **tunnels[4];
-
-       struct net_device *fb_tunnel_dev;
-};
 
 static int ipip_tunnel_init(struct net_device *dev);
-static void ipip_tunnel_setup(struct net_device *dev);
-static void ipip_dev_free(struct net_device *dev);
 static struct rtnl_link_ops ipip_link_ops __read_mostly;
 
-static struct rtnl_link_stats64 *ipip_get_stats64(struct net_device *dev,
-                                                 struct rtnl_link_stats64 *tot)
-{
-       int i;
-
-       for_each_possible_cpu(i) {
-               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
-               u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
-               unsigned int start;
-
-               do {
-                       start = u64_stats_fetch_begin_bh(&tstats->syncp);
-                       rx_packets = tstats->rx_packets;
-                       tx_packets = tstats->tx_packets;
-                       rx_bytes = tstats->rx_bytes;
-                       tx_bytes = tstats->tx_bytes;
-               } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
-
-               tot->rx_packets += rx_packets;
-               tot->tx_packets += tx_packets;
-               tot->rx_bytes   += rx_bytes;
-               tot->tx_bytes   += tx_bytes;
-       }
-
-       tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
-       tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
-       tot->tx_dropped = dev->stats.tx_dropped;
-       tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
-       tot->tx_errors = dev->stats.tx_errors;
-       tot->collisions = dev->stats.collisions;
-
-       return tot;
-}
-
-static struct ip_tunnel *ipip_tunnel_lookup(struct net *net,
-               __be32 remote, __be32 local)
-{
-       unsigned int h0 = HASH(remote);
-       unsigned int h1 = HASH(local);
-       struct ip_tunnel *t;
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-
-       for_each_ip_tunnel_rcu(t, ipn->tunnels_r_l[h0 ^ h1])
-               if (local == t->parms.iph.saddr &&
-                   remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
-                       return t;
-
-       for_each_ip_tunnel_rcu(t, ipn->tunnels_r[h0])
-               if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
-                       return t;
-
-       for_each_ip_tunnel_rcu(t, ipn->tunnels_l[h1])
-               if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP))
-                       return t;
-
-       t = rcu_dereference(ipn->tunnels_wc[0]);
-       if (t && (t->dev->flags&IFF_UP))
-               return t;
-       return NULL;
-}
-
-static struct ip_tunnel __rcu **__ipip_bucket(struct ipip_net *ipn,
-               struct ip_tunnel_parm *parms)
-{
-       __be32 remote = parms->iph.daddr;
-       __be32 local = parms->iph.saddr;
-       unsigned int h = 0;
-       int prio = 0;
-
-       if (remote) {
-               prio |= 2;
-               h ^= HASH(remote);
-       }
-       if (local) {
-               prio |= 1;
-               h ^= HASH(local);
-       }
-       return &ipn->tunnels[prio][h];
-}
-
-static inline struct ip_tunnel __rcu **ipip_bucket(struct ipip_net *ipn,
-               struct ip_tunnel *t)
-{
-       return __ipip_bucket(ipn, &t->parms);
-}
-
-static void ipip_tunnel_unlink(struct ipip_net *ipn, struct ip_tunnel *t)
-{
-       struct ip_tunnel __rcu **tp;
-       struct ip_tunnel *iter;
-
-       for (tp = ipip_bucket(ipn, t);
-            (iter = rtnl_dereference(*tp)) != NULL;
-            tp = &iter->next) {
-               if (t == iter) {
-                       rcu_assign_pointer(*tp, t->next);
-                       break;
-               }
-       }
-}
-
-static void ipip_tunnel_link(struct ipip_net *ipn, struct ip_tunnel *t)
-{
-       struct ip_tunnel __rcu **tp = ipip_bucket(ipn, t);
-
-       rcu_assign_pointer(t->next, rtnl_dereference(*tp));
-       rcu_assign_pointer(*tp, t);
-}
-
-static int ipip_tunnel_create(struct net_device *dev)
-{
-       struct ip_tunnel *t = netdev_priv(dev);
-       struct net *net = dev_net(dev);
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-       int err;
-
-       err = ipip_tunnel_init(dev);
-       if (err < 0)
-               goto out;
-
-       err = register_netdevice(dev);
-       if (err < 0)
-               goto out;
-
-       strcpy(t->parms.name, dev->name);
-       dev->rtnl_link_ops = &ipip_link_ops;
-
-       dev_hold(dev);
-       ipip_tunnel_link(ipn, t);
-       return 0;
-
-out:
-       return err;
-}
-
-static struct ip_tunnel *ipip_tunnel_locate(struct net *net,
-               struct ip_tunnel_parm *parms, int create)
-{
-       __be32 remote = parms->iph.daddr;
-       __be32 local = parms->iph.saddr;
-       struct ip_tunnel *t, *nt;
-       struct ip_tunnel __rcu **tp;
-       struct net_device *dev;
-       char name[IFNAMSIZ];
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-
-       for (tp = __ipip_bucket(ipn, parms);
-                (t = rtnl_dereference(*tp)) != NULL;
-                tp = &t->next) {
-               if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr)
-                       return t;
-       }
-       if (!create)
-               return NULL;
-
-       if (parms->name[0])
-               strlcpy(name, parms->name, IFNAMSIZ);
-       else
-               strcpy(name, "tunl%d");
-
-       dev = alloc_netdev(sizeof(*t), name, ipip_tunnel_setup);
-       if (dev == NULL)
-               return NULL;
-
-       dev_net_set(dev, net);
-
-       nt = netdev_priv(dev);
-       nt->parms = *parms;
-
-       if (ipip_tunnel_create(dev) < 0)
-               goto failed_free;
-
-       return nt;
-
-failed_free:
-       ipip_dev_free(dev);
-       return NULL;
-}
-
-/* called with RTNL */
-static void ipip_tunnel_uninit(struct net_device *dev)
-{
-       struct net *net = dev_net(dev);
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-
-       if (dev == ipn->fb_tunnel_dev)
-               RCU_INIT_POINTER(ipn->tunnels_wc[0], NULL);
-       else
-               ipip_tunnel_unlink(ipn, netdev_priv(dev));
-       dev_put(dev);
-}
-
 static int ipip_err(struct sk_buff *skb, u32 info)
 {
 
@@ -339,41 +133,17 @@ static int ipip_err(struct sk_buff *skb, u32 info)
    8 bytes of packet payload. It means, that precise relaying of
    ICMP in the real Internet is absolutely infeasible.
  */
+       struct net *net = dev_net(skb->dev);
+       struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
        const struct iphdr *iph = (const struct iphdr *)skb->data;
-       const int type = icmp_hdr(skb)->type;
-       const int code = icmp_hdr(skb)->code;
        struct ip_tunnel *t;
        int err;
-
-       switch (type) {
-       default:
-       case ICMP_PARAMETERPROB:
-               return 0;
-
-       case ICMP_DEST_UNREACH:
-               switch (code) {
-               case ICMP_SR_FAILED:
-               case ICMP_PORT_UNREACH:
-                       /* Impossible event. */
-                       return 0;
-               default:
-                       /* All others are translated to HOST_UNREACH.
-                          rfc2003 contains "deep thoughts" about NET_UNREACH,
-                          I believe they are just ether pollution. --ANK
-                        */
-                       break;
-               }
-               break;
-       case ICMP_TIME_EXCEEDED:
-               if (code != ICMP_EXC_TTL)
-                       return 0;
-               break;
-       case ICMP_REDIRECT:
-               break;
-       }
+       const int type = icmp_hdr(skb)->type;
+       const int code = icmp_hdr(skb)->code;
 
        err = -ENOENT;
-       t = ipip_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr);
+       t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
+                            iph->daddr, iph->saddr, 0);
        if (t == NULL)
                goto out;
 
@@ -403,53 +173,29 @@ static int ipip_err(struct sk_buff *skb, u32 info)
        else
                t->err_count = 1;
        t->err_time = jiffies;
-out:
 
+out:
        return err;
 }
 
+static const struct tnl_ptk_info tpi = {
+       /* no tunnel info required for ipip. */
+       .proto = htons(ETH_P_IP),
+};
+
 static int ipip_rcv(struct sk_buff *skb)
 {
+       struct net *net = dev_net(skb->dev);
+       struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
        struct ip_tunnel *tunnel;
        const struct iphdr *iph = ip_hdr(skb);
-       int err;
-
-       tunnel = ipip_tunnel_lookup(dev_net(skb->dev), iph->saddr, iph->daddr);
-       if (tunnel != NULL) {
-               struct pcpu_tstats *tstats;
 
+       tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
+                       iph->saddr, iph->daddr, 0);
+       if (tunnel) {
                if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
                        goto drop;
-
-               secpath_reset(skb);
-
-               skb->mac_header = skb->network_header;
-               skb_reset_network_header(skb);
-               skb->protocol = htons(ETH_P_IP);
-               skb->pkt_type = PACKET_HOST;
-
-               __skb_tunnel_rx(skb, tunnel->dev);
-
-               err = IP_ECN_decapsulate(iph, skb);
-               if (unlikely(err)) {
-                       if (log_ecn_error)
-                               net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-                                                    &iph->saddr, iph->tos);
-                       if (err > 1) {
-                               ++tunnel->dev->stats.rx_frame_errors;
-                               ++tunnel->dev->stats.rx_errors;
-                               goto drop;
-                       }
-               }
-
-               tstats = this_cpu_ptr(tunnel->dev->tstats);
-               u64_stats_update_begin(&tstats->syncp);
-               tstats->rx_packets++;
-               tstats->rx_bytes += skb->len;
-               u64_stats_update_end(&tstats->syncp);
-
-               netif_rx(skb);
-               return 0;
+               return ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
        }
 
        return -1;
@@ -463,329 +209,64 @@ drop:
  *     This function assumes it is being called from dev_queue_xmit()
  *     and that skb is filled properly by that function.
  */
-
 static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
        const struct iphdr  *tiph = &tunnel->parms.iph;
-       u8     tos = tunnel->parms.iph.tos;
-       __be16 df = tiph->frag_off;
-       struct rtable *rt;                      /* Route to the other host */
-       struct net_device *tdev;                /* Device to other host */
-       const struct iphdr  *old_iph;
-       struct iphdr  *iph;                     /* Our new IP header */
-       unsigned int max_headroom;              /* The extra header space needed */
-       __be32 dst = tiph->daddr;
-       struct flowi4 fl4;
-       int    mtu;
-
-       if (skb->protocol != htons(ETH_P_IP))
-               goto tx_error;
 
-       if (skb->ip_summed == CHECKSUM_PARTIAL &&
-           skb_checksum_help(skb))
+       if (unlikely(skb->protocol != htons(ETH_P_IP)))
                goto tx_error;
 
-       old_iph = ip_hdr(skb);
-
-       if (tos & 1)
-               tos = old_iph->tos;
-
-       if (!dst) {
-               /* NBMA tunnel */
-               if ((rt = skb_rtable(skb)) == NULL) {
-                       dev->stats.tx_fifo_errors++;
-                       goto tx_error;
-               }
-               dst = rt_nexthop(rt, old_iph->daddr);
+       if (likely(!skb->encapsulation)) {
+               skb_reset_inner_headers(skb);
+               skb->encapsulation = 1;
        }
 
-       rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
-                                  dst, tiph->saddr,
-                                  0, 0,
-                                  IPPROTO_IPIP, RT_TOS(tos),
-                                  tunnel->parms.link);
-       if (IS_ERR(rt)) {
-               dev->stats.tx_carrier_errors++;
-               goto tx_error_icmp;
-       }
-       tdev = rt->dst.dev;
-
-       if (tdev == dev) {
-               ip_rt_put(rt);
-               dev->stats.collisions++;
-               goto tx_error;
-       }
-
-       df |= old_iph->frag_off & htons(IP_DF);
-
-       if (df) {
-               mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr);
-
-               if (mtu < 68) {
-                       dev->stats.collisions++;
-                       ip_rt_put(rt);
-                       goto tx_error;
-               }
-
-               if (skb_dst(skb))
-                       skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
-
-               if ((old_iph->frag_off & htons(IP_DF)) &&
-                   mtu < ntohs(old_iph->tot_len)) {
-                       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
-                                 htonl(mtu));
-                       ip_rt_put(rt);
-                       goto tx_error;
-               }
-       }
-
-       if (tunnel->err_count > 0) {
-               if (time_before(jiffies,
-                               tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
-                       tunnel->err_count--;
-                       dst_link_failure(skb);
-               } else
-                       tunnel->err_count = 0;
-       }
-
-       /*
-        * Okay, now see if we can stuff it in the buffer as-is.
-        */
-       max_headroom = (LL_RESERVED_SPACE(tdev)+sizeof(struct iphdr));
-
-       if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
-           (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
-               struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
-               if (!new_skb) {
-                       ip_rt_put(rt);
-                       dev->stats.tx_dropped++;
-                       dev_kfree_skb(skb);
-                       return NETDEV_TX_OK;
-               }
-               if (skb->sk)
-                       skb_set_owner_w(new_skb, skb->sk);
-               dev_kfree_skb(skb);
-               skb = new_skb;
-               old_iph = ip_hdr(skb);
-       }
-
-       skb->transport_header = skb->network_header;
-       skb_push(skb, sizeof(struct iphdr));
-       skb_reset_network_header(skb);
-       memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
-       IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
-                             IPSKB_REROUTED);
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
-       /*
-        *      Push down and install the IPIP header.
-        */
-
-       iph                     =       ip_hdr(skb);
-       iph->version            =       4;
-       iph->ihl                =       sizeof(struct iphdr)>>2;
-       iph->frag_off           =       df;
-       iph->protocol           =       IPPROTO_IPIP;
-       iph->tos                =       INET_ECN_encapsulate(tos, old_iph->tos);
-       iph->daddr              =       fl4.daddr;
-       iph->saddr              =       fl4.saddr;
-
-       if ((iph->ttl = tiph->ttl) == 0)
-               iph->ttl        =       old_iph->ttl;
-
-       iptunnel_xmit(skb, dev);
+       ip_tunnel_xmit(skb, dev, tiph);
        return NETDEV_TX_OK;
 
-tx_error_icmp:
-       dst_link_failure(skb);
 tx_error:
        dev->stats.tx_errors++;
        dev_kfree_skb(skb);
        return NETDEV_TX_OK;
 }
 
-static void ipip_tunnel_bind_dev(struct net_device *dev)
-{
-       struct net_device *tdev = NULL;
-       struct ip_tunnel *tunnel;
-       const struct iphdr *iph;
-
-       tunnel = netdev_priv(dev);
-       iph = &tunnel->parms.iph;
-
-       if (iph->daddr) {
-               struct rtable *rt;
-               struct flowi4 fl4;
-
-               rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
-                                          iph->daddr, iph->saddr,
-                                          0, 0,
-                                          IPPROTO_IPIP,
-                                          RT_TOS(iph->tos),
-                                          tunnel->parms.link);
-               if (!IS_ERR(rt)) {
-                       tdev = rt->dst.dev;
-                       ip_rt_put(rt);
-               }
-               dev->flags |= IFF_POINTOPOINT;
-       }
-
-       if (!tdev && tunnel->parms.link)
-               tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link);
-
-       if (tdev) {
-               dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr);
-               dev->mtu = tdev->mtu - sizeof(struct iphdr);
-       }
-       dev->iflink = tunnel->parms.link;
-}
-
-static void ipip_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
-{
-       struct net *net = dev_net(t->dev);
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-
-       ipip_tunnel_unlink(ipn, t);
-       synchronize_net();
-       t->parms.iph.saddr = p->iph.saddr;
-       t->parms.iph.daddr = p->iph.daddr;
-       memcpy(t->dev->dev_addr, &p->iph.saddr, 4);
-       memcpy(t->dev->broadcast, &p->iph.daddr, 4);
-       ipip_tunnel_link(ipn, t);
-       t->parms.iph.ttl = p->iph.ttl;
-       t->parms.iph.tos = p->iph.tos;
-       t->parms.iph.frag_off = p->iph.frag_off;
-       if (t->parms.link != p->link) {
-               t->parms.link = p->link;
-               ipip_tunnel_bind_dev(t->dev);
-       }
-       netdev_state_change(t->dev);
-}
-
 static int
-ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
+ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        int err = 0;
        struct ip_tunnel_parm p;
-       struct ip_tunnel *t;
-       struct net *net = dev_net(dev);
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-
-       switch (cmd) {
-       case SIOCGETTUNNEL:
-               t = NULL;
-               if (dev == ipn->fb_tunnel_dev) {
-                       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
-                               err = -EFAULT;
-                               break;
-                       }
-                       t = ipip_tunnel_locate(net, &p, 0);
-               }
-               if (t == NULL)
-                       t = netdev_priv(dev);
-               memcpy(&p, &t->parms, sizeof(p));
-               if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
-                       err = -EFAULT;
-               break;
-
-       case SIOCADDTUNNEL:
-       case SIOCCHGTUNNEL:
-               err = -EPERM;
-               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
-                       goto done;
-
-               err = -EFAULT;
-               if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
-                       goto done;
-
-               err = -EINVAL;
-               if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
-                   p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
-                       goto done;
-               if (p.iph.ttl)
-                       p.iph.frag_off |= htons(IP_DF);
-
-               t = ipip_tunnel_locate(net, &p, cmd == SIOCADDTUNNEL);
-
-               if (dev != ipn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
-                       if (t != NULL) {
-                               if (t->dev != dev) {
-                                       err = -EEXIST;
-                                       break;
-                               }
-                       } else {
-                               if (((dev->flags&IFF_POINTOPOINT) && !p.iph.daddr) ||
-                                   (!(dev->flags&IFF_POINTOPOINT) && p.iph.daddr)) {
-                                       err = -EINVAL;
-                                       break;
-                               }
-                               t = netdev_priv(dev);
-                       }
-
-                       ipip_tunnel_update(t, &p);
-               }
-
-               if (t) {
-                       err = 0;
-                       if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
-                               err = -EFAULT;
-               } else
-                       err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
-               break;
-
-       case SIOCDELTUNNEL:
-               err = -EPERM;
-               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
-                       goto done;
-
-               if (dev == ipn->fb_tunnel_dev) {
-                       err = -EFAULT;
-                       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
-                               goto done;
-                       err = -ENOENT;
-                       if ((t = ipip_tunnel_locate(net, &p, 0)) == NULL)
-                               goto done;
-                       err = -EPERM;
-                       if (t->dev == ipn->fb_tunnel_dev)
-                               goto done;
-                       dev = t->dev;
-               }
-               unregister_netdevice(dev);
-               err = 0;
-               break;
 
-       default:
-               err = -EINVAL;
-       }
-
-done:
-       return err;
-}
+       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+               return -EFAULT;
 
-static int ipip_tunnel_change_mtu(struct net_device *dev, int new_mtu)
-{
-       if (new_mtu < 68 || new_mtu > 0xFFF8 - sizeof(struct iphdr))
+       if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
+                       p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
+               return -EINVAL;
+       if (p.i_key || p.o_key || p.i_flags || p.o_flags)
                return -EINVAL;
-       dev->mtu = new_mtu;
+       if (p.iph.ttl)
+               p.iph.frag_off |= htons(IP_DF);
+
+       err = ip_tunnel_ioctl(dev, &p, cmd);
+       if (err)
+               return err;
+
+       if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+               return -EFAULT;
+
        return 0;
 }
 
 static const struct net_device_ops ipip_netdev_ops = {
-       .ndo_uninit     = ipip_tunnel_uninit,
+       .ndo_init       = ipip_tunnel_init,
+       .ndo_uninit     = ip_tunnel_uninit,
        .ndo_start_xmit = ipip_tunnel_xmit,
        .ndo_do_ioctl   = ipip_tunnel_ioctl,
-       .ndo_change_mtu = ipip_tunnel_change_mtu,
-       .ndo_get_stats64 = ipip_get_stats64,
+       .ndo_change_mtu = ip_tunnel_change_mtu,
+       .ndo_get_stats64 = ip_tunnel_get_stats64,
 };
 
-static void ipip_dev_free(struct net_device *dev)
-{
-       free_percpu(dev->tstats);
-       free_netdev(dev);
-}
-
 #define IPIP_FEATURES (NETIF_F_SG |            \
                       NETIF_F_FRAGLIST |       \
                       NETIF_F_HIGHDMA |        \
@@ -794,11 +275,8 @@ static void ipip_dev_free(struct net_device *dev)
 static void ipip_tunnel_setup(struct net_device *dev)
 {
        dev->netdev_ops         = &ipip_netdev_ops;
-       dev->destructor         = ipip_dev_free;
 
        dev->type               = ARPHRD_TUNNEL;
-       dev->hard_header_len    = LL_MAX_HEADER + sizeof(struct iphdr);
-       dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr);
        dev->flags              = IFF_NOARP;
        dev->iflink             = 0;
        dev->addr_len           = 4;
@@ -808,46 +286,19 @@ static void ipip_tunnel_setup(struct net_device *dev)
 
        dev->features           |= IPIP_FEATURES;
        dev->hw_features        |= IPIP_FEATURES;
+       ip_tunnel_setup(dev, ipip_net_id);
 }
 
 static int ipip_tunnel_init(struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
 
-       tunnel->dev = dev;
-
        memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
        memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
 
-       ipip_tunnel_bind_dev(dev);
-
-       dev->tstats = alloc_percpu(struct pcpu_tstats);
-       if (!dev->tstats)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static int __net_init ipip_fb_tunnel_init(struct net_device *dev)
-{
-       struct ip_tunnel *tunnel = netdev_priv(dev);
-       struct iphdr *iph = &tunnel->parms.iph;
-       struct ipip_net *ipn = net_generic(dev_net(dev), ipip_net_id);
-
-       tunnel->dev = dev;
-       strcpy(tunnel->parms.name, dev->name);
-
-       iph->version            = 4;
-       iph->protocol           = IPPROTO_IPIP;
-       iph->ihl                = 5;
-
-       dev->tstats = alloc_percpu(struct pcpu_tstats);
-       if (!dev->tstats)
-               return -ENOMEM;
-
-       dev_hold(dev);
-       rcu_assign_pointer(ipn->tunnels_wc[0], tunnel);
-       return 0;
+       tunnel->hlen = 0;
+       tunnel->parms.iph.protocol = IPPROTO_IPIP;
+       return ip_tunnel_init(dev);
 }
 
 static void ipip_netlink_parms(struct nlattr *data[],
@@ -887,28 +338,16 @@ static void ipip_netlink_parms(struct nlattr *data[],
 static int ipip_newlink(struct net *src_net, struct net_device *dev,
                        struct nlattr *tb[], struct nlattr *data[])
 {
-       struct net *net = dev_net(dev);
-       struct ip_tunnel *nt;
-
-       nt = netdev_priv(dev);
-       ipip_netlink_parms(data, &nt->parms);
-
-       if (ipip_tunnel_locate(net, &nt->parms, 0))
-               return -EEXIST;
+       struct ip_tunnel_parm p;
 
-       return ipip_tunnel_create(dev);
+       ipip_netlink_parms(data, &p);
+       return ip_tunnel_newlink(dev, tb, &p);
 }
 
 static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
                           struct nlattr *data[])
 {
-       struct ip_tunnel *t;
        struct ip_tunnel_parm p;
-       struct net *net = dev_net(dev);
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-
-       if (dev == ipn->fb_tunnel_dev)
-               return -EINVAL;
 
        ipip_netlink_parms(data, &p);
 
@@ -916,16 +355,7 @@ static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
            (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
                return -EINVAL;
 
-       t = ipip_tunnel_locate(net, &p, 0);
-
-       if (t) {
-               if (t->dev != dev)
-                       return -EEXIST;
-       } else
-               t = netdev_priv(dev);
-
-       ipip_tunnel_update(t, &p);
-       return 0;
+       return ip_tunnel_changelink(dev, tb, &p);
 }
 
 static size_t ipip_get_size(const struct net_device *dev)
@@ -982,6 +412,7 @@ static struct rtnl_link_ops ipip_link_ops __read_mostly = {
        .setup          = ipip_tunnel_setup,
        .newlink        = ipip_newlink,
        .changelink     = ipip_changelink,
+       .dellink        = ip_tunnel_dellink,
        .get_size       = ipip_get_size,
        .fill_info      = ipip_fill_info,
 };
@@ -992,90 +423,29 @@ static struct xfrm_tunnel ipip_handler __read_mostly = {
        .priority       =       1,
 };
 
-static const char banner[] __initconst =
-       KERN_INFO "IPv4 over IPv4 tunneling driver\n";
-
-static void ipip_destroy_tunnels(struct ipip_net *ipn, struct list_head *head)
-{
-       int prio;
-
-       for (prio = 1; prio < 4; prio++) {
-               int h;
-               for (h = 0; h < HASH_SIZE; h++) {
-                       struct ip_tunnel *t;
-
-                       t = rtnl_dereference(ipn->tunnels[prio][h]);
-                       while (t != NULL) {
-                               unregister_netdevice_queue(t->dev, head);
-                               t = rtnl_dereference(t->next);
-                       }
-               }
-       }
-}
-
 static int __net_init ipip_init_net(struct net *net)
 {
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-       struct ip_tunnel *t;
-       int err;
-
-       ipn->tunnels[0] = ipn->tunnels_wc;
-       ipn->tunnels[1] = ipn->tunnels_l;
-       ipn->tunnels[2] = ipn->tunnels_r;
-       ipn->tunnels[3] = ipn->tunnels_r_l;
-
-       ipn->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel),
-                                          "tunl0",
-                                          ipip_tunnel_setup);
-       if (!ipn->fb_tunnel_dev) {
-               err = -ENOMEM;
-               goto err_alloc_dev;
-       }
-       dev_net_set(ipn->fb_tunnel_dev, net);
-
-       err = ipip_fb_tunnel_init(ipn->fb_tunnel_dev);
-       if (err)
-               goto err_reg_dev;
-
-       if ((err = register_netdev(ipn->fb_tunnel_dev)))
-               goto err_reg_dev;
-
-       t = netdev_priv(ipn->fb_tunnel_dev);
-
-       strcpy(t->parms.name, ipn->fb_tunnel_dev->name);
-       return 0;
-
-err_reg_dev:
-       ipip_dev_free(ipn->fb_tunnel_dev);
-err_alloc_dev:
-       /* nothing */
-       return err;
+       return ip_tunnel_init_net(net, ipip_net_id, &ipip_link_ops, "tunl0");
 }
 
 static void __net_exit ipip_exit_net(struct net *net)
 {
-       struct ipip_net *ipn = net_generic(net, ipip_net_id);
-       LIST_HEAD(list);
-
-       rtnl_lock();
-       ipip_destroy_tunnels(ipn, &list);
-       unregister_netdevice_queue(ipn->fb_tunnel_dev, &list);
-       unregister_netdevice_many(&list);
-       rtnl_unlock();
+       struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
+       ip_tunnel_delete_net(itn);
 }
 
 static struct pernet_operations ipip_net_ops = {
        .init = ipip_init_net,
        .exit = ipip_exit_net,
        .id   = &ipip_net_id,
-       .size = sizeof(struct ipip_net),
+       .size = sizeof(struct ip_tunnel_net),
 };
 
 static int __init ipip_init(void)
 {
        int err;
 
-       printk(banner);
+       pr_info("ipip: IPv4 over IPv4 tunneling driver\n");
 
        err = register_pernet_device(&ipip_net_ops);
        if (err < 0)
index 5f95b3aa579ef03f01ca4a1c5900eb16eecff512..9d9610ae78553895e9f6ccb0ea8260fa4a66ac17 100644 (file)
@@ -61,7 +61,7 @@
 #include <linux/netfilter_ipv4.h>
 #include <linux/compat.h>
 #include <linux/export.h>
-#include <net/ipip.h>
+#include <net/ip_tunnels.h>
 #include <net/checksum.h>
 #include <net/netlink.h>
 #include <net/fib_rules.h>
@@ -626,9 +626,9 @@ static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
                if (ip_hdr(skb)->version == 0) {
                        struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
                        nlh->nlmsg_type = NLMSG_ERROR;
-                       nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
+                       nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
                        skb_trim(skb, nlh->nlmsg_len);
-                       e = NLMSG_DATA(nlh);
+                       e = nlmsg_data(nlh);
                        e->error = -ETIMEDOUT;
                        memset(&e->msg, 0, sizeof(e->msg));
 
@@ -910,14 +910,14 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
                if (ip_hdr(skb)->version == 0) {
                        struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
 
-                       if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
+                       if (__ipmr_fill_mroute(mrt, skb, c, nlmsg_data(nlh)) > 0) {
                                nlh->nlmsg_len = skb_tail_pointer(skb) -
                                                 (u8 *)nlh;
                        } else {
                                nlh->nlmsg_type = NLMSG_ERROR;
-                               nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
+                               nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
                                skb_trim(skb, nlh->nlmsg_len);
-                               e = NLMSG_DATA(nlh);
+                               e = nlmsg_data(nlh);
                                e->error = -EMSGSIZE;
                                memset(&e->msg, 0, sizeof(e->msg));
                        }
index 4c0cf63dd92e9ab94bf155c8e0d497c2029cebde..c3e0adea9c277585f01dff2f1740787bb3d19a66 100644 (file)
@@ -1,4 +1,9 @@
-/* IPv4 specific functions of netfilter core */
+/*
+ * IPv4 specific functions of netfilter core
+ *
+ * Rusty Russell (C) 2000 -- This code is GPL.
+ * Patrick McHardy (C) 2006-2012
+ */
 #include <linux/kernel.h>
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv4.h>
@@ -40,14 +45,14 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
        fl4.flowi4_flags = flags;
        rt = ip_route_output_key(net, &fl4);
        if (IS_ERR(rt))
-               return -1;
+               return PTR_ERR(rt);
 
        /* Drop old route. */
        skb_dst_drop(skb);
        skb_dst_set(skb, &rt->dst);
 
        if (skb_dst(skb)->error)
-               return -1;
+               return skb_dst(skb)->error;
 
 #ifdef CONFIG_XFRM
        if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
@@ -56,7 +61,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
                skb_dst_set(skb, NULL);
                dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), skb->sk, 0);
                if (IS_ERR(dst))
-                       return -1;
+                       return PTR_ERR(dst);;
                skb_dst_set(skb, dst);
        }
 #endif
@@ -66,7 +71,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
        if (skb_headroom(skb) < hh_len &&
            pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
                                0, GFP_ATOMIC))
-               return -1;
+               return -ENOMEM;
 
        return 0;
 }
index 0d755c50994b2ca01438158e033c020810a3e8af..e7916c193932222b7538c104dc71eeb8b3f8b5f2 100644 (file)
@@ -71,7 +71,7 @@ config IP_NF_MATCH_ECN
 
 config IP_NF_MATCH_RPFILTER
        tristate '"rpfilter" reverse path filter match support'
-       depends on NETFILTER_ADVANCED
+       depends on NETFILTER_ADVANCED && (IP_NF_MANGLE || IP_NF_RAW)
        ---help---
          This option allows you to match packets whose replies would
          go out via the interface the packet came in.
index 7dc6a97435925d1b56a918906553b423accee999..85a4f21aac1ad117f740d40da7123a663fdfefa8 100644 (file)
@@ -6,6 +6,7 @@
  * Some ARP specific bits are:
  *
  * Copyright (C) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2006-2009 Patrick McHardy <kaber@trash.net>
  *
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
index 79ca5e70d49774e074613fe2c55cee941386542a..eadab1ed6500b955b97640dfef6a9dc43ad74608 100644 (file)
@@ -48,9 +48,7 @@ static int __net_init arptable_filter_net_init(struct net *net)
        net->ipv4.arptable_filter =
                arpt_register_table(net, &packet_filter, repl);
        kfree(repl);
-       if (IS_ERR(net->ipv4.arptable_filter))
-               return PTR_ERR(net->ipv4.arptable_filter);
-       return 0;
+       return PTR_RET(net->ipv4.arptable_filter);
 }
 
 static void __net_exit arptable_filter_net_exit(struct net *net)
index 3efcf87400c3420f7d2d877b7472f2d4aeea00a1..d23118d95ff9291401396953d094391db0e2e44e 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
  * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
+ * Copyright (C) 2006-2010 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -182,8 +183,7 @@ ipt_get_target_c(const struct ipt_entry *e)
        return ipt_get_target((struct ipt_entry *)e);
 }
 
-#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
-    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
 static const char *const hooknames[] = {
        [NF_INET_PRE_ROUTING]           = "PREROUTING",
        [NF_INET_LOCAL_IN]              = "INPUT",
@@ -259,6 +259,7 @@ static void trace_packet(const struct sk_buff *skb,
        const char *hookname, *chainname, *comment;
        const struct ipt_entry *iter;
        unsigned int rulenum = 0;
+       struct net *net = dev_net(in ? in : out);
 
        table_base = private->entries[smp_processor_id()];
        root = get_entry(table_base, private->hook_entry[hook]);
@@ -271,7 +272,7 @@ static void trace_packet(const struct sk_buff *skb,
                    &chainname, &comment, &rulenum) != 0)
                        break;
 
-       nf_log_packet(AF_INET, hook, skb, in, out, &trace_loginfo,
+       nf_log_packet(net, AF_INET, hook, skb, in, out, &trace_loginfo,
                      "TRACE: %s:%s:%s:%u ",
                      tablename, chainname, comment, rulenum);
 }
@@ -361,8 +362,7 @@ ipt_do_table(struct sk_buff *skb,
                t = ipt_get_target(e);
                IP_NF_ASSERT(t->u.kernel.target);
 
-#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
-    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
                /* The packet is traced: log it */
                if (unlikely(skb->nf_trace))
                        trace_packet(skb, hook, in, out,
index 7d168dcbd135c7bf47c7772038454965311e9715..f8a222cb64481c95e2f3b53207e3c6a3d372f4d2 100644 (file)
@@ -4,6 +4,7 @@
  * (C) 2000-2004 by Harald Welte <laforge@netfilter.org>
  * (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2005-2007 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -37,7 +38,7 @@
 #include <linux/skbuff.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <linux/netdevice.h>
 #include <linux/mm.h>
 #include <linux/moduleparam.h>
@@ -45,6 +46,7 @@
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter_ipv4/ipt_ULOG.h>
 #include <net/netfilter/nf_log.h>
+#include <net/netns/generic.h>
 #include <net/sock.h>
 #include <linux/bitops.h>
 #include <asm/unaligned.h>
@@ -78,15 +80,23 @@ typedef struct {
        struct timer_list timer;        /* the timer function */
 } ulog_buff_t;
 
-static ulog_buff_t ulog_buffers[ULOG_MAXNLGROUPS];     /* array of buffers */
+static int ulog_net_id __read_mostly;
+struct ulog_net {
+       unsigned int nlgroup[ULOG_MAXNLGROUPS];
+       ulog_buff_t ulog_buffers[ULOG_MAXNLGROUPS];
+       struct sock *nflognl;
+       spinlock_t lock;
+};
 
-static struct sock *nflognl;           /* our socket */
-static DEFINE_SPINLOCK(ulog_lock);     /* spinlock */
+static struct ulog_net *ulog_pernet(struct net *net)
+{
+       return net_generic(net, ulog_net_id);
+}
 
 /* send one ulog_buff_t to userspace */
-static void ulog_send(unsigned int nlgroupnum)
+static void ulog_send(struct ulog_net *ulog, unsigned int nlgroupnum)
 {
-       ulog_buff_t *ub = &ulog_buffers[nlgroupnum];
+       ulog_buff_t *ub = &ulog->ulog_buffers[nlgroupnum];
 
        pr_debug("ulog_send: timer is deleting\n");
        del_timer(&ub->timer);
@@ -103,7 +113,8 @@ static void ulog_send(unsigned int nlgroupnum)
        NETLINK_CB(ub->skb).dst_group = nlgroupnum + 1;
        pr_debug("throwing %d packets to netlink group %u\n",
                 ub->qlen, nlgroupnum + 1);
-       netlink_broadcast(nflognl, ub->skb, 0, nlgroupnum + 1, GFP_ATOMIC);
+       netlink_broadcast(ulog->nflognl, ub->skb, 0, nlgroupnum + 1,
+                         GFP_ATOMIC);
 
        ub->qlen = 0;
        ub->skb = NULL;
@@ -114,13 +125,16 @@ static void ulog_send(unsigned int nlgroupnum)
 /* timer function to flush queue in flushtimeout time */
 static void ulog_timer(unsigned long data)
 {
+       struct ulog_net *ulog = container_of((void *)data,
+                                            struct ulog_net,
+                                            nlgroup[*(unsigned int *)data]);
        pr_debug("timer function called, calling ulog_send\n");
 
        /* lock to protect against somebody modifying our structure
         * from ipt_ulog_target at the same time */
-       spin_lock_bh(&ulog_lock);
-       ulog_send(data);
-       spin_unlock_bh(&ulog_lock);
+       spin_lock_bh(&ulog->lock);
+       ulog_send(ulog, data);
+       spin_unlock_bh(&ulog->lock);
 }
 
 static struct sk_buff *ulog_alloc_skb(unsigned int size)
@@ -160,6 +174,8 @@ static void ipt_ulog_packet(unsigned int hooknum,
        size_t size, copy_len;
        struct nlmsghdr *nlh;
        struct timeval tv;
+       struct net *net = dev_net(in ? in : out);
+       struct ulog_net *ulog = ulog_pernet(net);
 
        /* ffs == find first bit set, necessary because userspace
         * is already shifting groupnumber, but we need unshifted.
@@ -172,11 +188,11 @@ static void ipt_ulog_packet(unsigned int hooknum,
        else
                copy_len = loginfo->copy_range;
 
-       size = NLMSG_SPACE(sizeof(*pm) + copy_len);
+       size = nlmsg_total_size(sizeof(*pm) + copy_len);
 
-       ub = &ulog_buffers[groupnum];
+       ub = &ulog->ulog_buffers[groupnum];
 
-       spin_lock_bh(&ulog_lock);
+       spin_lock_bh(&ulog->lock);
 
        if (!ub->skb) {
                if (!(ub->skb = ulog_alloc_skb(size)))
@@ -186,7 +202,7 @@ static void ipt_ulog_packet(unsigned int hooknum,
                /* either the queue len is too high or we don't have
                 * enough room in nlskb left. send it to userspace. */
 
-               ulog_send(groupnum);
+               ulog_send(ulog, groupnum);
 
                if (!(ub->skb = ulog_alloc_skb(size)))
                        goto alloc_failure;
@@ -260,16 +276,16 @@ static void ipt_ulog_packet(unsigned int hooknum,
        if (ub->qlen >= loginfo->qthreshold) {
                if (loginfo->qthreshold > 1)
                        nlh->nlmsg_type = NLMSG_DONE;
-               ulog_send(groupnum);
+               ulog_send(ulog, groupnum);
        }
 out_unlock:
-       spin_unlock_bh(&ulog_lock);
+       spin_unlock_bh(&ulog->lock);
 
        return;
 
 alloc_failure:
        pr_debug("Error building netlink message\n");
-       spin_unlock_bh(&ulog_lock);
+       spin_unlock_bh(&ulog->lock);
 }
 
 static unsigned int
@@ -376,54 +392,43 @@ static struct nf_logger ipt_ulog_logger __read_mostly = {
        .me             = THIS_MODULE,
 };
 
-static int __init ulog_tg_init(void)
+static int __net_init ulog_tg_net_init(struct net *net)
 {
-       int ret, i;
+       int i;
+       struct ulog_net *ulog = ulog_pernet(net);
        struct netlink_kernel_cfg cfg = {
                .groups = ULOG_MAXNLGROUPS,
        };
 
-       pr_debug("init module\n");
-
-       if (nlbufsiz > 128*1024) {
-               pr_warning("Netlink buffer has to be <= 128kB\n");
-               return -EINVAL;
-       }
-
+       spin_lock_init(&ulog->lock);
        /* initialize ulog_buffers */
        for (i = 0; i < ULOG_MAXNLGROUPS; i++)
-               setup_timer(&ulog_buffers[i].timer, ulog_timer, i);
+               setup_timer(&ulog->ulog_buffers[i].timer, ulog_timer, i);
 
-       nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, &cfg);
-       if (!nflognl)
+       ulog->nflognl = netlink_kernel_create(net, NETLINK_NFLOG, &cfg);
+       if (!ulog->nflognl)
                return -ENOMEM;
 
-       ret = xt_register_target(&ulog_tg_reg);
-       if (ret < 0) {
-               netlink_kernel_release(nflognl);
-               return ret;
-       }
        if (nflog)
-               nf_log_register(NFPROTO_IPV4, &ipt_ulog_logger);
+               nf_log_set(net, NFPROTO_IPV4, &ipt_ulog_logger);
 
        return 0;
 }
 
-static void __exit ulog_tg_exit(void)
+static void __net_exit ulog_tg_net_exit(struct net *net)
 {
        ulog_buff_t *ub;
        int i;
-
-       pr_debug("cleanup_module\n");
+       struct ulog_net *ulog = ulog_pernet(net);
 
        if (nflog)
-               nf_log_unregister(&ipt_ulog_logger);
-       xt_unregister_target(&ulog_tg_reg);
-       netlink_kernel_release(nflognl);
+               nf_log_unset(net, &ipt_ulog_logger);
+
+       netlink_kernel_release(ulog->nflognl);
 
        /* remove pending timers and free allocated skb's */
        for (i = 0; i < ULOG_MAXNLGROUPS; i++) {
-               ub = &ulog_buffers[i];
+               ub = &ulog->ulog_buffers[i];
                pr_debug("timer is deleting\n");
                del_timer(&ub->timer);
 
@@ -434,5 +439,50 @@ static void __exit ulog_tg_exit(void)
        }
 }
 
+static struct pernet_operations ulog_tg_net_ops = {
+       .init = ulog_tg_net_init,
+       .exit = ulog_tg_net_exit,
+       .id   = &ulog_net_id,
+       .size = sizeof(struct ulog_net),
+};
+
+static int __init ulog_tg_init(void)
+{
+       int ret;
+       pr_debug("init module\n");
+
+       if (nlbufsiz > 128*1024) {
+               pr_warn("Netlink buffer has to be <= 128kB\n");
+               return -EINVAL;
+       }
+
+       ret = register_pernet_subsys(&ulog_tg_net_ops);
+       if (ret)
+               goto out_pernet;
+
+       ret = xt_register_target(&ulog_tg_reg);
+       if (ret < 0)
+               goto out_target;
+
+       if (nflog)
+               nf_log_register(NFPROTO_IPV4, &ipt_ulog_logger);
+
+       return 0;
+
+out_target:
+       unregister_pernet_subsys(&ulog_tg_net_ops);
+out_pernet:
+       return ret;
+}
+
+static void __exit ulog_tg_exit(void)
+{
+       pr_debug("cleanup_module\n");
+       if (nflog)
+               nf_log_unregister(&ipt_ulog_logger);
+       xt_unregister_target(&ulog_tg_reg);
+       unregister_pernet_subsys(&ulog_tg_net_ops);
+}
+
 module_init(ulog_tg_init);
 module_exit(ulog_tg_exit);
index 85d88f20644701f63ad9b8250d9fd391c108ee55..cba5658ec82c671b92e086ba18c4871dda5d385b 100644 (file)
@@ -44,6 +44,7 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
        u_int8_t tos;
        __be32 saddr, daddr;
        u_int32_t mark;
+       int err;
 
        /* root is playing with raw sockets. */
        if (skb->len < sizeof(struct iphdr) ||
@@ -66,9 +67,11 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
                if (iph->saddr != saddr ||
                    iph->daddr != daddr ||
                    skb->mark != mark ||
-                   iph->tos != tos)
-                       if (ip_route_me_harder(skb, RTN_UNSPEC))
-                               ret = NF_DROP;
+                   iph->tos != tos) {
+                       err = ip_route_me_harder(skb, RTN_UNSPEC);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
+               }
        }
 
        return ret;
index eeaff7e4acb5c8da1c042769ac4c805579260bef..6383273d54e17da9080dc5b5c12380058a18a3ee 100644 (file)
@@ -176,6 +176,7 @@ nf_nat_ipv4_out(unsigned int hooknum,
 #ifdef CONFIG_XFRM
        const struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
+       int err;
 #endif
        unsigned int ret;
 
@@ -195,9 +196,11 @@ nf_nat_ipv4_out(unsigned int hooknum,
                     ct->tuplehash[!dir].tuple.dst.u3.ip) ||
                    (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
                     ct->tuplehash[dir].tuple.src.u.all !=
-                    ct->tuplehash[!dir].tuple.dst.u.all))
-                       if (nf_xfrm_me_harder(skb, AF_INET) < 0)
-                               ret = NF_DROP;
+                    ct->tuplehash[!dir].tuple.dst.u.all)) {
+                       err = nf_xfrm_me_harder(skb, AF_INET);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
+               }
        }
 #endif
        return ret;
@@ -213,6 +216,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
        const struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
        unsigned int ret;
+       int err;
 
        /* root is playing with raw sockets. */
        if (skb->len < sizeof(struct iphdr) ||
@@ -226,16 +230,19 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
 
                if (ct->tuplehash[dir].tuple.dst.u3.ip !=
                    ct->tuplehash[!dir].tuple.src.u3.ip) {
-                       if (ip_route_me_harder(skb, RTN_UNSPEC))
-                               ret = NF_DROP;
+                       err = ip_route_me_harder(skb, RTN_UNSPEC);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
                }
 #ifdef CONFIG_XFRM
                else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
                         ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
                         ct->tuplehash[dir].tuple.dst.u.all !=
-                        ct->tuplehash[!dir].tuple.src.u.all)
-                       if (nf_xfrm_me_harder(skb, AF_INET) < 0)
-                               ret = NF_DROP;
+                        ct->tuplehash[!dir].tuple.src.u.all) {
+                       err = nf_xfrm_me_harder(skb, AF_INET);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
+               }
 #endif
        }
        return ret;
index 2820aa18b542d870ad8c5ef1b5da68a30468e870..567d84168bd2e6e878b9a85733a649e3845eabdf 100644 (file)
@@ -1,6 +1,7 @@
 
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
index f2ca1279408109a0eca95a2beaad5ef3199a29ca..4c48e434bb1f7b9c2cd29338dbb9f82930138c93 100644 (file)
@@ -2,6 +2,7 @@
  *
  * (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2006-2010 Patrick McHardy <kaber@trash.net>
  *
  * 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
index 5241d997ab752517ee554a32cf984b1a193d494f..a338dad41b7d8b70d755c3e149db8916615dd89a 100644 (file)
@@ -1,5 +1,6 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2006-2010 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -187,8 +188,8 @@ icmp_error(struct net *net, struct nf_conn *tmpl,
        icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
        if (icmph == NULL) {
                if (LOG_INVALID(net, IPPROTO_ICMP))
-                       nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
-                                     "nf_ct_icmp: short packet ");
+                       nf_log_packet(net, PF_INET, 0, skb, NULL, NULL,
+                                     NULL, "nf_ct_icmp: short packet ");
                return -NF_ACCEPT;
        }
 
@@ -196,7 +197,7 @@ icmp_error(struct net *net, struct nf_conn *tmpl,
        if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
            nf_ip_checksum(skb, hooknum, dataoff, 0)) {
                if (LOG_INVALID(net, IPPROTO_ICMP))
-                       nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, PF_INET, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_icmp: bad HW ICMP checksum ");
                return -NF_ACCEPT;
        }
@@ -209,7 +210,7 @@ icmp_error(struct net *net, struct nf_conn *tmpl,
         */
        if (icmph->type > NR_ICMP_TYPES) {
                if (LOG_INVALID(net, IPPROTO_ICMP))
-                       nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, PF_INET, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_icmp: invalid ICMP type ");
                return -NF_ACCEPT;
        }
index 9c3db10b22d33862fd1ceea6382a85b57cc44e6d..9eea059dd6216225950428819104bfcae77d10ba 100644 (file)
@@ -2,6 +2,7 @@
  * H.323 extension for NAT alteration.
  *
  * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
+ * Copyright (c) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * This source code is licensed under General Public License version 2.
  *
index a06d7d74817d3976d5cb15147036a3270941dc80..657d2307f031e7fb2a2e38f763cf528329931614 100644 (file)
@@ -13,6 +13,8 @@
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  *
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
+ *
  * TODO: - NAT to a unique tuple, not to TCP source port
  *        (needs netfilter tuple reservation)
  */
index ea44f02563b5dc5275bb13484fae50b207079b96..690d890111bbdf0c254360e2dfb14d60067810a1 100644 (file)
@@ -21,6 +21,8 @@
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  *
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
+ *
  */
 
 #include <linux/module.h>
index bac712293fd693a9f3afdd7f7776e845b17a35ea..5f011cc89cd979742844febe4cc6117a9332d5c9 100644 (file)
@@ -38,6 +38,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  * Author: James Morris <jmorris@intercode.com.au>
+ *
+ * Copyright (c) 2006-2010 Patrick McHardy <kaber@trash.net>
  */
 #include <linux/module.h>
 #include <linux/moduleparam.h>
index 2e91006d6076492fb0fe3b0bdf831c42bca56756..7d93d62cd5fdae9f7f8a3f2e5c35f2a203df92e0 100644 (file)
@@ -514,9 +514,8 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        ipc.opt = NULL;
        ipc.oif = sk->sk_bound_dev_if;
        ipc.tx_flags = 0;
-       err = sock_tx_timestamp(sk, &ipc.tx_flags);
-       if (err)
-               return err;
+
+       sock_tx_timestamp(sk, &ipc.tx_flags);
 
        if (msg->msg_controllen) {
                err = ip_cmsg_send(sock_net(sk), msg, &ipc);
index 32030a24e77624c2df45b87d9ae759a9ed8de454..6da51d55d03a167a33703c1ed57ec7183ab607ae 100644 (file)
@@ -224,6 +224,8 @@ static const struct snmp_mib snmp4_net_list[] = {
        SNMP_MIB_ITEM("TCPForwardRetrans", LINUX_MIB_TCPFORWARDRETRANS),
        SNMP_MIB_ITEM("TCPSlowStartRetrans", LINUX_MIB_TCPSLOWSTARTRETRANS),
        SNMP_MIB_ITEM("TCPTimeouts", LINUX_MIB_TCPTIMEOUTS),
+       SNMP_MIB_ITEM("TCPLossProbes", LINUX_MIB_TCPLOSSPROBES),
+       SNMP_MIB_ITEM("TCPLossProbeRecovery", LINUX_MIB_TCPLOSSPROBERECOVERY),
        SNMP_MIB_ITEM("TCPRenoRecoveryFail", LINUX_MIB_TCPRENORECOVERYFAIL),
        SNMP_MIB_ITEM("TCPSackRecoveryFail", LINUX_MIB_TCPSACKRECOVERYFAIL),
        SNMP_MIB_ITEM("TCPSchedulerFailed", LINUX_MIB_TCPSCHEDULERFAILED),
@@ -267,6 +269,7 @@ static const struct snmp_mib snmp4_net_list[] = {
        SNMP_MIB_ITEM("TCPFastOpenPassiveFail", LINUX_MIB_TCPFASTOPENPASSIVEFAIL),
        SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW),
        SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD),
+       SNMP_MIB_ITEM("TCPSpuriousRtxHostQueues", LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES),
        SNMP_MIB_SENTINEL
 };
 
index 6e2851464f8fa46081dcec3b161e22b7a7da6a55..550781a17b34f75f94b919a39120eb119db106a1 100644 (file)
@@ -2311,7 +2311,7 @@ nla_put_failure:
        return -EMSGSIZE;
 }
 
-static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
+static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(in_skb->sk);
        struct rtmsg *rtm;
index 397e0f69435fd1b1b4b3775cadc409d67ea63332..b05c96e7af8b810a62bb07d95436eea07c651008 100644 (file)
@@ -267,7 +267,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
                             struct ip_options *opt)
 {
        struct tcp_options_received tcp_opt;
-       const u8 *hash_location;
        struct inet_request_sock *ireq;
        struct tcp_request_sock *treq;
        struct tcp_sock *tp = tcp_sk(sk);
@@ -294,7 +293,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
 
        /* check for timestamp cookie support */
        memset(&tcp_opt, 0, sizeof(tcp_opt));
-       tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL);
+       tcp_parse_options(skb, &tcp_opt, 0, NULL);
 
        if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok))
                goto out;
index 960fd29d9b8e067e12f89a6dfdcb3fa7873ea468..fa2f63fc453b936b1660b79146b67edf17ae687a 100644 (file)
@@ -28,7 +28,7 @@
 
 static int zero;
 static int one = 1;
-static int two = 2;
+static int four = 4;
 static int tcp_retr1_max = 255;
 static int ip_local_port_range_min[] = { 1, 1 };
 static int ip_local_port_range_max[] = { 65535, 65535 };
@@ -591,13 +591,6 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
-       {
-               .procname       = "tcp_frto_response",
-               .data           = &sysctl_tcp_frto_response,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec
-       },
        {
                .procname       = "tcp_low_latency",
                .data           = &sysctl_tcp_low_latency,
@@ -732,13 +725,6 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
-       {
-               .procname       = "tcp_cookie_size",
-               .data           = &sysctl_tcp_cookie_size,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec
-       },
        {
                .procname       = "tcp_thin_linear_timeouts",
                .data           = &sysctl_tcp_thin_linear_timeouts,
@@ -760,7 +746,7 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
                .extra1         = &zero,
-               .extra2         = &two,
+               .extra2         = &four,
        },
        {
                .procname       = "udp_mem",
index e22020790709dbde58759c771f47a69e883892e4..dcb116dde2168759025d315f4e2d3b77994a276a 100644 (file)
@@ -409,15 +409,6 @@ void tcp_init_sock(struct sock *sk)
 
        icsk->icsk_sync_mss = tcp_sync_mss;
 
-       /* TCP Cookie Transactions */
-       if (sysctl_tcp_cookie_size > 0) {
-               /* Default, cookies without s_data_payload. */
-               tp->cookie_values =
-                       kzalloc(sizeof(*tp->cookie_values),
-                               sk->sk_allocation);
-               if (tp->cookie_values != NULL)
-                       kref_init(&tp->cookie_values->kref);
-       }
        /* Presumed zeroed, in order of appearance:
         *      cookie_in_always, cookie_out_never,
         *      s_data_constant, s_data_in, s_data_out
@@ -2397,92 +2388,6 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
                release_sock(sk);
                return err;
        }
-       case TCP_COOKIE_TRANSACTIONS: {
-               struct tcp_cookie_transactions ctd;
-               struct tcp_cookie_values *cvp = NULL;
-
-               if (sizeof(ctd) > optlen)
-                       return -EINVAL;
-               if (copy_from_user(&ctd, optval, sizeof(ctd)))
-                       return -EFAULT;
-
-               if (ctd.tcpct_used > sizeof(ctd.tcpct_value) ||
-                   ctd.tcpct_s_data_desired > TCP_MSS_DESIRED)
-                       return -EINVAL;
-
-               if (ctd.tcpct_cookie_desired == 0) {
-                       /* default to global value */
-               } else if ((0x1 & ctd.tcpct_cookie_desired) ||
-                          ctd.tcpct_cookie_desired > TCP_COOKIE_MAX ||
-                          ctd.tcpct_cookie_desired < TCP_COOKIE_MIN) {
-                       return -EINVAL;
-               }
-
-               if (TCP_COOKIE_OUT_NEVER & ctd.tcpct_flags) {
-                       /* Supercedes all other values */
-                       lock_sock(sk);
-                       if (tp->cookie_values != NULL) {
-                               kref_put(&tp->cookie_values->kref,
-                                        tcp_cookie_values_release);
-                               tp->cookie_values = NULL;
-                       }
-                       tp->rx_opt.cookie_in_always = 0; /* false */
-                       tp->rx_opt.cookie_out_never = 1; /* true */
-                       release_sock(sk);
-                       return err;
-               }
-
-               /* Allocate ancillary memory before locking.
-                */
-               if (ctd.tcpct_used > 0 ||
-                   (tp->cookie_values == NULL &&
-                    (sysctl_tcp_cookie_size > 0 ||
-                     ctd.tcpct_cookie_desired > 0 ||
-                     ctd.tcpct_s_data_desired > 0))) {
-                       cvp = kzalloc(sizeof(*cvp) + ctd.tcpct_used,
-                                     GFP_KERNEL);
-                       if (cvp == NULL)
-                               return -ENOMEM;
-
-                       kref_init(&cvp->kref);
-               }
-               lock_sock(sk);
-               tp->rx_opt.cookie_in_always =
-                       (TCP_COOKIE_IN_ALWAYS & ctd.tcpct_flags);
-               tp->rx_opt.cookie_out_never = 0; /* false */
-
-               if (tp->cookie_values != NULL) {
-                       if (cvp != NULL) {
-                               /* Changed values are recorded by a changed
-                                * pointer, ensuring the cookie will differ,
-                                * without separately hashing each value later.
-                                */
-                               kref_put(&tp->cookie_values->kref,
-                                        tcp_cookie_values_release);
-                       } else {
-                               cvp = tp->cookie_values;
-                       }
-               }
-
-               if (cvp != NULL) {
-                       cvp->cookie_desired = ctd.tcpct_cookie_desired;
-
-                       if (ctd.tcpct_used > 0) {
-                               memcpy(cvp->s_data_payload, ctd.tcpct_value,
-                                      ctd.tcpct_used);
-                               cvp->s_data_desired = ctd.tcpct_used;
-                               cvp->s_data_constant = 1; /* true */
-                       } else {
-                               /* No constant payload data. */
-                               cvp->s_data_desired = ctd.tcpct_s_data_desired;
-                               cvp->s_data_constant = 0; /* false */
-                       }
-
-                       tp->cookie_values = cvp;
-               }
-               release_sock(sk);
-               return err;
-       }
        default:
                /* fallthru */
                break;
@@ -2902,41 +2807,6 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
                        return -EFAULT;
                return 0;
 
-       case TCP_COOKIE_TRANSACTIONS: {
-               struct tcp_cookie_transactions ctd;
-               struct tcp_cookie_values *cvp = tp->cookie_values;
-
-               if (get_user(len, optlen))
-                       return -EFAULT;
-               if (len < sizeof(ctd))
-                       return -EINVAL;
-
-               memset(&ctd, 0, sizeof(ctd));
-               ctd.tcpct_flags = (tp->rx_opt.cookie_in_always ?
-                                  TCP_COOKIE_IN_ALWAYS : 0)
-                               | (tp->rx_opt.cookie_out_never ?
-                                  TCP_COOKIE_OUT_NEVER : 0);
-
-               if (cvp != NULL) {
-                       ctd.tcpct_flags |= (cvp->s_data_in ?
-                                           TCP_S_DATA_IN : 0)
-                                        | (cvp->s_data_out ?
-                                           TCP_S_DATA_OUT : 0);
-
-                       ctd.tcpct_cookie_desired = cvp->cookie_desired;
-                       ctd.tcpct_s_data_desired = cvp->s_data_desired;
-
-                       memcpy(&ctd.tcpct_value[0], &cvp->cookie_pair[0],
-                              cvp->cookie_pair_size);
-                       ctd.tcpct_used = cvp->cookie_pair_size;
-               }
-
-               if (put_user(sizeof(ctd), optlen))
-                       return -EFAULT;
-               if (copy_to_user(optval, &ctd, sizeof(ctd)))
-                       return -EFAULT;
-               return 0;
-       }
        case TCP_THIN_LINEAR_TIMEOUTS:
                val = tp->thin_lto;
                break;
@@ -3015,6 +2885,8 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
        __be32 delta;
        unsigned int oldlen;
        unsigned int mss;
+       struct sk_buff *gso_skb = skb;
+       __sum16 newcheck;
 
        if (!pskb_may_pull(skb, sizeof(*th)))
                goto out;
@@ -3044,6 +2916,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                               SKB_GSO_TCP_ECN |
                               SKB_GSO_TCPV6 |
                               SKB_GSO_GRE |
+                              SKB_GSO_UDP_TUNNEL |
                               0) ||
                             !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
                        goto out;
@@ -3064,11 +2937,13 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
        th = tcp_hdr(skb);
        seq = ntohl(th->seq);
 
+       newcheck = ~csum_fold((__force __wsum)((__force u32)th->check +
+                                              (__force u32)delta));
+
        do {
                th->fin = th->psh = 0;
+               th->check = newcheck;
 
-               th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
-                                      (__force u32)delta));
                if (skb->ip_summed != CHECKSUM_PARTIAL)
                        th->check =
                             csum_fold(csum_partial(skb_transport_header(skb),
@@ -3082,6 +2957,17 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                th->cwr = 0;
        } while (skb->next);
 
+       /* Following permits TCP Small Queues to work well with GSO :
+        * The callback to TCP stack will be called at the time last frag
+        * is freed at TX completion, and not right now when gso_skb
+        * is freed by GSO engine
+        */
+       if (gso_skb->destructor == tcp_wfree) {
+               swap(gso_skb->sk, skb->sk);
+               swap(gso_skb->destructor, skb->destructor);
+               swap(gso_skb->truesize, skb->truesize);
+       }
+
        delta = htonl(oldlen + (skb->tail - skb->transport_header) +
                      skb->data_len);
        th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
@@ -3408,134 +3294,6 @@ EXPORT_SYMBOL(tcp_md5_hash_key);
 
 #endif
 
-/* Each Responder maintains up to two secret values concurrently for
- * efficient secret rollover.  Each secret value has 4 states:
- *
- * Generating.  (tcp_secret_generating != tcp_secret_primary)
- *    Generates new Responder-Cookies, but not yet used for primary
- *    verification.  This is a short-term state, typically lasting only
- *    one round trip time (RTT).
- *
- * Primary.  (tcp_secret_generating == tcp_secret_primary)
- *    Used both for generation and primary verification.
- *
- * Retiring.  (tcp_secret_retiring != tcp_secret_secondary)
- *    Used for verification, until the first failure that can be
- *    verified by the newer Generating secret.  At that time, this
- *    cookie's state is changed to Secondary, and the Generating
- *    cookie's state is changed to Primary.  This is a short-term state,
- *    typically lasting only one round trip time (RTT).
- *
- * Secondary.  (tcp_secret_retiring == tcp_secret_secondary)
- *    Used for secondary verification, after primary verification
- *    failures.  This state lasts no more than twice the Maximum Segment
- *    Lifetime (2MSL).  Then, the secret is discarded.
- */
-struct tcp_cookie_secret {
-       /* The secret is divided into two parts.  The digest part is the
-        * equivalent of previously hashing a secret and saving the state,
-        * and serves as an initialization vector (IV).  The message part
-        * serves as the trailing secret.
-        */
-       u32                             secrets[COOKIE_WORKSPACE_WORDS];
-       unsigned long                   expires;
-};
-
-#define TCP_SECRET_1MSL (HZ * TCP_PAWS_MSL)
-#define TCP_SECRET_2MSL (HZ * TCP_PAWS_MSL * 2)
-#define TCP_SECRET_LIFE (HZ * 600)
-
-static struct tcp_cookie_secret tcp_secret_one;
-static struct tcp_cookie_secret tcp_secret_two;
-
-/* Essentially a circular list, without dynamic allocation. */
-static struct tcp_cookie_secret *tcp_secret_generating;
-static struct tcp_cookie_secret *tcp_secret_primary;
-static struct tcp_cookie_secret *tcp_secret_retiring;
-static struct tcp_cookie_secret *tcp_secret_secondary;
-
-static DEFINE_SPINLOCK(tcp_secret_locker);
-
-/* Select a pseudo-random word in the cookie workspace.
- */
-static inline u32 tcp_cookie_work(const u32 *ws, const int n)
-{
-       return ws[COOKIE_DIGEST_WORDS + ((COOKIE_MESSAGE_WORDS-1) & ws[n])];
-}
-
-/* Fill bakery[COOKIE_WORKSPACE_WORDS] with generator, updating as needed.
- * Called in softirq context.
- * Returns: 0 for success.
- */
-int tcp_cookie_generator(u32 *bakery)
-{
-       unsigned long jiffy = jiffies;
-
-       if (unlikely(time_after_eq(jiffy, tcp_secret_generating->expires))) {
-               spin_lock_bh(&tcp_secret_locker);
-               if (!time_after_eq(jiffy, tcp_secret_generating->expires)) {
-                       /* refreshed by another */
-                       memcpy(bakery,
-                              &tcp_secret_generating->secrets[0],
-                              COOKIE_WORKSPACE_WORDS);
-               } else {
-                       /* still needs refreshing */
-                       get_random_bytes(bakery, COOKIE_WORKSPACE_WORDS);
-
-                       /* The first time, paranoia assumes that the
-                        * randomization function isn't as strong.  But,
-                        * this secret initialization is delayed until
-                        * the last possible moment (packet arrival).
-                        * Although that time is observable, it is
-                        * unpredictably variable.  Mash in the most
-                        * volatile clock bits available, and expire the
-                        * secret extra quickly.
-                        */
-                       if (unlikely(tcp_secret_primary->expires ==
-                                    tcp_secret_secondary->expires)) {
-                               struct timespec tv;
-
-                               getnstimeofday(&tv);
-                               bakery[COOKIE_DIGEST_WORDS+0] ^=
-                                       (u32)tv.tv_nsec;
-
-                               tcp_secret_secondary->expires = jiffy
-                                       + TCP_SECRET_1MSL
-                                       + (0x0f & tcp_cookie_work(bakery, 0));
-                       } else {
-                               tcp_secret_secondary->expires = jiffy
-                                       + TCP_SECRET_LIFE
-                                       + (0xff & tcp_cookie_work(bakery, 1));
-                               tcp_secret_primary->expires = jiffy
-                                       + TCP_SECRET_2MSL
-                                       + (0x1f & tcp_cookie_work(bakery, 2));
-                       }
-                       memcpy(&tcp_secret_secondary->secrets[0],
-                              bakery, COOKIE_WORKSPACE_WORDS);
-
-                       rcu_assign_pointer(tcp_secret_generating,
-                                          tcp_secret_secondary);
-                       rcu_assign_pointer(tcp_secret_retiring,
-                                          tcp_secret_primary);
-                       /*
-                        * Neither call_rcu() nor synchronize_rcu() needed.
-                        * Retiring data is not freed.  It is replaced after
-                        * further (locked) pointer updates, and a quiet time
-                        * (minimum 1MSL, maximum LIFE - 2MSL).
-                        */
-               }
-               spin_unlock_bh(&tcp_secret_locker);
-       } else {
-               rcu_read_lock_bh();
-               memcpy(bakery,
-                      &rcu_dereference(tcp_secret_generating)->secrets[0],
-                      COOKIE_WORKSPACE_WORDS);
-               rcu_read_unlock_bh();
-       }
-       return 0;
-}
-EXPORT_SYMBOL(tcp_cookie_generator);
-
 void tcp_done(struct sock *sk)
 {
        struct request_sock *req = tcp_sk(sk)->fastopen_rsk;
@@ -3590,7 +3348,6 @@ void __init tcp_init(void)
        unsigned long limit;
        int max_rshare, max_wshare, cnt;
        unsigned int i;
-       unsigned long jiffy = jiffies;
 
        BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb));
 
@@ -3666,13 +3423,5 @@ void __init tcp_init(void)
 
        tcp_register_congestion_control(&tcp_reno);
 
-       memset(&tcp_secret_one.secrets[0], 0, sizeof(tcp_secret_one.secrets));
-       memset(&tcp_secret_two.secrets[0], 0, sizeof(tcp_secret_two.secrets));
-       tcp_secret_one.expires = jiffy; /* past due */
-       tcp_secret_two.expires = jiffy; /* past due */
-       tcp_secret_generating = &tcp_secret_one;
-       tcp_secret_primary = &tcp_secret_one;
-       tcp_secret_retiring = &tcp_secret_two;
-       tcp_secret_secondary = &tcp_secret_two;
        tcp_tasklet_init();
 }
index 13b9c08fc1582531aa34eef92a432dc96f2a1c52..aafd052865ba813f096bb00a16acd612c4d1e6ea 100644 (file)
@@ -93,12 +93,11 @@ int sysctl_tcp_stdurg __read_mostly;
 int sysctl_tcp_rfc1337 __read_mostly;
 int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
 int sysctl_tcp_frto __read_mostly = 2;
-int sysctl_tcp_frto_response __read_mostly;
 
 int sysctl_tcp_thin_dupack __read_mostly;
 
 int sysctl_tcp_moderate_rcvbuf __read_mostly = 1;
-int sysctl_tcp_early_retrans __read_mostly = 2;
+int sysctl_tcp_early_retrans __read_mostly = 3;
 
 #define FLAG_DATA              0x01 /* Incoming frame contained data.          */
 #define FLAG_WIN_UPDATE                0x02 /* Incoming ACK was a window update.       */
@@ -108,10 +107,9 @@ int sysctl_tcp_early_retrans __read_mostly = 2;
 #define FLAG_DATA_SACKED       0x20 /* New SACK.                               */
 #define FLAG_ECE               0x40 /* ECE in this ACK                         */
 #define FLAG_SLOWPATH          0x100 /* Do not skip RFC checks for window update.*/
-#define FLAG_ONLY_ORIG_SACKED  0x200 /* SACKs only non-rexmit sent before RTO */
+#define FLAG_ORIG_SACK_ACKED   0x200 /* Never retransmitted data are (s)acked  */
 #define FLAG_SND_UNA_ADVANCED  0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */
 #define FLAG_DSACKING_ACK      0x800 /* SACK blocks contained D-SACK info */
-#define FLAG_NONHEAD_RETRANS_ACKED     0x1000 /* Non-head rexmitted data was ACKed */
 #define FLAG_SACK_RENEGING     0x2000 /* snd_una advanced to a sacked seq */
 #define FLAG_UPDATE_TS_RECENT  0x4000 /* tcp_replace_ts_recent() */
 
@@ -119,7 +117,6 @@ int sysctl_tcp_early_retrans __read_mostly = 2;
 #define FLAG_NOT_DUP           (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED)
 #define FLAG_CA_ALERT          (FLAG_DATA_SACKED|FLAG_ECE)
 #define FLAG_FORWARD_PROGRESS  (FLAG_ACKED|FLAG_DATA_SACKED)
-#define FLAG_ANY_PROGRESS      (FLAG_FORWARD_PROGRESS|FLAG_SND_UNA_ADVANCED)
 
 #define TCP_REMNANT (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN|TCP_FLAG_PSH)
 #define TCP_HP_BITS (~(TCP_RESERVED_BITS|TCP_FLAG_PSH))
@@ -1160,10 +1157,8 @@ static u8 tcp_sacktag_one(struct sock *sk,
                                           tcp_highest_sack_seq(tp)))
                                        state->reord = min(fack_count,
                                                           state->reord);
-
-                               /* SACK enhanced F-RTO (RFC4138; Appendix B) */
-                               if (!after(end_seq, tp->frto_highmark))
-                                       state->flag |= FLAG_ONLY_ORIG_SACKED;
+                               if (!after(end_seq, tp->high_seq))
+                                       state->flag |= FLAG_ORIG_SACK_ACKED;
                        }
 
                        if (sacked & TCPCB_LOST) {
@@ -1556,7 +1551,6 @@ static int
 tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb,
                        u32 prior_snd_una)
 {
-       const struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        const unsigned char *ptr = (skb_transport_header(ack_skb) +
                                    TCP_SKB_CB(ack_skb)->sacked);
@@ -1729,12 +1723,6 @@ walk:
                                       start_seq, end_seq, dup_sack);
 
 advance_sp:
-               /* SACK enhanced FRTO (RFC4138, Appendix B): Clearing correct
-                * due to in-order walk
-                */
-               if (after(end_seq, tp->frto_highmark))
-                       state.flag &= ~FLAG_ONLY_ORIG_SACKED;
-
                i++;
        }
 
@@ -1751,8 +1739,7 @@ advance_sp:
        tcp_verify_left_out(tp);
 
        if ((state.reord < tp->fackets_out) &&
-           ((icsk->icsk_ca_state != TCP_CA_Loss) || tp->undo_marker) &&
-           (!tp->frto_highmark || after(tp->snd_una, tp->frto_highmark)))
+           ((inet_csk(sk)->icsk_ca_state != TCP_CA_Loss) || tp->undo_marker))
                tcp_update_reordering(sk, tp->fackets_out - state.reord, 0);
 
 out:
@@ -1826,197 +1813,6 @@ static inline void tcp_reset_reno_sack(struct tcp_sock *tp)
        tp->sacked_out = 0;
 }
 
-static int tcp_is_sackfrto(const struct tcp_sock *tp)
-{
-       return (sysctl_tcp_frto == 0x2) && !tcp_is_reno(tp);
-}
-
-/* F-RTO can only be used if TCP has never retransmitted anything other than
- * head (SACK enhanced variant from Appendix B of RFC4138 is more robust here)
- */
-bool tcp_use_frto(struct sock *sk)
-{
-       const struct tcp_sock *tp = tcp_sk(sk);
-       const struct inet_connection_sock *icsk = inet_csk(sk);
-       struct sk_buff *skb;
-
-       if (!sysctl_tcp_frto)
-               return false;
-
-       /* MTU probe and F-RTO won't really play nicely along currently */
-       if (icsk->icsk_mtup.probe_size)
-               return false;
-
-       if (tcp_is_sackfrto(tp))
-               return true;
-
-       /* Avoid expensive walking of rexmit queue if possible */
-       if (tp->retrans_out > 1)
-               return false;
-
-       skb = tcp_write_queue_head(sk);
-       if (tcp_skb_is_last(sk, skb))
-               return true;
-       skb = tcp_write_queue_next(sk, skb);    /* Skips head */
-       tcp_for_write_queue_from(skb, sk) {
-               if (skb == tcp_send_head(sk))
-                       break;
-               if (TCP_SKB_CB(skb)->sacked & TCPCB_RETRANS)
-                       return false;
-               /* Short-circuit when first non-SACKed skb has been checked */
-               if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED))
-                       break;
-       }
-       return true;
-}
-
-/* RTO occurred, but do not yet enter Loss state. Instead, defer RTO
- * recovery a bit and use heuristics in tcp_process_frto() to detect if
- * the RTO was spurious. Only clear SACKED_RETRANS of the head here to
- * keep retrans_out counting accurate (with SACK F-RTO, other than head
- * may still have that bit set); TCPCB_LOST and remaining SACKED_RETRANS
- * bits are handled if the Loss state is really to be entered (in
- * tcp_enter_frto_loss).
- *
- * Do like tcp_enter_loss() would; when RTO expires the second time it
- * does:
- *  "Reduce ssthresh if it has not yet been made inside this window."
- */
-void tcp_enter_frto(struct sock *sk)
-{
-       const struct inet_connection_sock *icsk = inet_csk(sk);
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct sk_buff *skb;
-
-       if ((!tp->frto_counter && icsk->icsk_ca_state <= TCP_CA_Disorder) ||
-           tp->snd_una == tp->high_seq ||
-           ((icsk->icsk_ca_state == TCP_CA_Loss || tp->frto_counter) &&
-            !icsk->icsk_retransmits)) {
-               tp->prior_ssthresh = tcp_current_ssthresh(sk);
-               /* Our state is too optimistic in ssthresh() call because cwnd
-                * is not reduced until tcp_enter_frto_loss() when previous F-RTO
-                * recovery has not yet completed. Pattern would be this: RTO,
-                * Cumulative ACK, RTO (2xRTO for the same segment does not end
-                * up here twice).
-                * RFC4138 should be more specific on what to do, even though
-                * RTO is quite unlikely to occur after the first Cumulative ACK
-                * due to back-off and complexity of triggering events ...
-                */
-               if (tp->frto_counter) {
-                       u32 stored_cwnd;
-                       stored_cwnd = tp->snd_cwnd;
-                       tp->snd_cwnd = 2;
-                       tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
-                       tp->snd_cwnd = stored_cwnd;
-               } else {
-                       tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
-               }
-               /* ... in theory, cong.control module could do "any tricks" in
-                * ssthresh(), which means that ca_state, lost bits and lost_out
-                * counter would have to be faked before the call occurs. We
-                * consider that too expensive, unlikely and hacky, so modules
-                * using these in ssthresh() must deal these incompatibility
-                * issues if they receives CA_EVENT_FRTO and frto_counter != 0
-                */
-               tcp_ca_event(sk, CA_EVENT_FRTO);
-       }
-
-       tp->undo_marker = tp->snd_una;
-       tp->undo_retrans = 0;
-
-       skb = tcp_write_queue_head(sk);
-       if (TCP_SKB_CB(skb)->sacked & TCPCB_RETRANS)
-               tp->undo_marker = 0;
-       if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {
-               TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
-               tp->retrans_out -= tcp_skb_pcount(skb);
-       }
-       tcp_verify_left_out(tp);
-
-       /* Too bad if TCP was application limited */
-       tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp) + 1);
-
-       /* Earlier loss recovery underway (see RFC4138; Appendix B).
-        * The last condition is necessary at least in tp->frto_counter case.
-        */
-       if (tcp_is_sackfrto(tp) && (tp->frto_counter ||
-           ((1 << icsk->icsk_ca_state) & (TCPF_CA_Recovery|TCPF_CA_Loss))) &&
-           after(tp->high_seq, tp->snd_una)) {
-               tp->frto_highmark = tp->high_seq;
-       } else {
-               tp->frto_highmark = tp->snd_nxt;
-       }
-       tcp_set_ca_state(sk, TCP_CA_Disorder);
-       tp->high_seq = tp->snd_nxt;
-       tp->frto_counter = 1;
-}
-
-/* Enter Loss state after F-RTO was applied. Dupack arrived after RTO,
- * which indicates that we should follow the traditional RTO recovery,
- * i.e. mark everything lost and do go-back-N retransmission.
- */
-static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct sk_buff *skb;
-
-       tp->lost_out = 0;
-       tp->retrans_out = 0;
-       if (tcp_is_reno(tp))
-               tcp_reset_reno_sack(tp);
-
-       tcp_for_write_queue(skb, sk) {
-               if (skb == tcp_send_head(sk))
-                       break;
-
-               TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
-               /*
-                * Count the retransmission made on RTO correctly (only when
-                * waiting for the first ACK and did not get it)...
-                */
-               if ((tp->frto_counter == 1) && !(flag & FLAG_DATA_ACKED)) {
-                       /* For some reason this R-bit might get cleared? */
-                       if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS)
-                               tp->retrans_out += tcp_skb_pcount(skb);
-                       /* ...enter this if branch just for the first segment */
-                       flag |= FLAG_DATA_ACKED;
-               } else {
-                       if (TCP_SKB_CB(skb)->sacked & TCPCB_RETRANS)
-                               tp->undo_marker = 0;
-                       TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
-               }
-
-               /* Marking forward transmissions that were made after RTO lost
-                * can cause unnecessary retransmissions in some scenarios,
-                * SACK blocks will mitigate that in some but not in all cases.
-                * We used to not mark them but it was causing break-ups with
-                * receivers that do only in-order receival.
-                *
-                * TODO: we could detect presence of such receiver and select
-                * different behavior per flow.
-                */
-               if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) {
-                       TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
-                       tp->lost_out += tcp_skb_pcount(skb);
-                       tp->retransmit_high = TCP_SKB_CB(skb)->end_seq;
-               }
-       }
-       tcp_verify_left_out(tp);
-
-       tp->snd_cwnd = tcp_packets_in_flight(tp) + allowed_segments;
-       tp->snd_cwnd_cnt = 0;
-       tp->snd_cwnd_stamp = tcp_time_stamp;
-       tp->frto_counter = 0;
-
-       tp->reordering = min_t(unsigned int, tp->reordering,
-                              sysctl_tcp_reordering);
-       tcp_set_ca_state(sk, TCP_CA_Loss);
-       tp->high_seq = tp->snd_nxt;
-       TCP_ECN_queue_cwr(tp);
-
-       tcp_clear_all_retrans_hints(tp);
-}
-
 static void tcp_clear_retrans_partial(struct tcp_sock *tp)
 {
        tp->retrans_out = 0;
@@ -2043,10 +1839,13 @@ void tcp_enter_loss(struct sock *sk, int how)
        const struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *skb;
+       bool new_recovery = false;
 
        /* Reduce ssthresh if it has not yet been made inside this window. */
-       if (icsk->icsk_ca_state <= TCP_CA_Disorder || tp->snd_una == tp->high_seq ||
+       if (icsk->icsk_ca_state <= TCP_CA_Disorder ||
+           !after(tp->high_seq, tp->snd_una) ||
            (icsk->icsk_ca_state == TCP_CA_Loss && !icsk->icsk_retransmits)) {
+               new_recovery = true;
                tp->prior_ssthresh = tcp_current_ssthresh(sk);
                tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
                tcp_ca_event(sk, CA_EVENT_LOSS);
@@ -2088,8 +1887,14 @@ void tcp_enter_loss(struct sock *sk, int how)
        tcp_set_ca_state(sk, TCP_CA_Loss);
        tp->high_seq = tp->snd_nxt;
        TCP_ECN_queue_cwr(tp);
-       /* Abort F-RTO algorithm if one is in progress */
-       tp->frto_counter = 0;
+
+       /* F-RTO RFC5682 sec 3.1 step 1: retransmit SND.UNA if no previous
+        * loss recovery is underway except recurring timeout(s) on
+        * the same SND.UNA (sec 3.2). Disable F-RTO on path MTU probing
+        */
+       tp->frto = sysctl_tcp_frto &&
+                  (new_recovery || icsk->icsk_retransmits) &&
+                  !inet_csk(sk)->icsk_mtup.probe_size;
 }
 
 /* If ACK arrived pointing to a remembered SACK, it means that our
@@ -2148,15 +1953,16 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag)
         * max(RTT/4, 2msec) unless ack has ECE mark, no RTT samples
         * available, or RTO is scheduled to fire first.
         */
-       if (sysctl_tcp_early_retrans < 2 || (flag & FLAG_ECE) || !tp->srtt)
+       if (sysctl_tcp_early_retrans < 2 || sysctl_tcp_early_retrans > 3 ||
+           (flag & FLAG_ECE) || !tp->srtt)
                return false;
 
        delay = max_t(unsigned long, (tp->srtt >> 5), msecs_to_jiffies(2));
        if (!time_after(inet_csk(sk)->icsk_timeout, (jiffies + delay)))
                return false;
 
-       inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, delay, TCP_RTO_MAX);
-       tp->early_retrans_delayed = 1;
+       inet_csk_reset_xmit_timer(sk, ICSK_TIME_EARLY_RETRANS, delay,
+                                 TCP_RTO_MAX);
        return true;
 }
 
@@ -2272,10 +2078,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag)
        struct tcp_sock *tp = tcp_sk(sk);
        __u32 packets_out;
 
-       /* Do not perform any recovery during F-RTO algorithm */
-       if (tp->frto_counter)
-               return false;
-
        /* Trick#1: The loss is proven. */
        if (tp->lost_out)
                return true;
@@ -2319,7 +2121,7 @@ static bool tcp_time_to_recover(struct sock *sk, int flag)
         * interval if appropriate.
         */
        if (tp->do_early_retrans && !tp->retrans_out && tp->sacked_out &&
-           (tp->packets_out == (tp->sacked_out + 1) && tp->packets_out < 4) &&
+           (tp->packets_out >= (tp->sacked_out + 1) && tp->packets_out < 4) &&
            !tcp_may_send_now(sk))
                return !tcp_pause_early_retransmit(sk, flag);
 
@@ -2636,12 +2438,12 @@ static int tcp_try_undo_partial(struct sock *sk, int acked)
        return failed;
 }
 
-/* Undo during loss recovery after partial ACK. */
-static bool tcp_try_undo_loss(struct sock *sk)
+/* Undo during loss recovery after partial ACK or using F-RTO. */
+static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (tcp_may_undo(tp)) {
+       if (frto_undo || tcp_may_undo(tp)) {
                struct sk_buff *skb;
                tcp_for_write_queue(skb, sk) {
                        if (skb == tcp_send_head(sk))
@@ -2655,9 +2457,12 @@ static bool tcp_try_undo_loss(struct sock *sk)
                tp->lost_out = 0;
                tcp_undo_cwr(sk, true);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSSUNDO);
+               if (frto_undo)
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPSPURIOUSRTOS);
                inet_csk(sk)->icsk_retransmits = 0;
                tp->undo_marker = 0;
-               if (tcp_is_sack(tp))
+               if (frto_undo || tcp_is_sack(tp))
                        tcp_set_ca_state(sk, TCP_CA_Open);
                return true;
        }
@@ -2679,6 +2484,7 @@ static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh)
        struct tcp_sock *tp = tcp_sk(sk);
 
        tp->high_seq = tp->snd_nxt;
+       tp->tlp_high_seq = 0;
        tp->snd_cwnd_cnt = 0;
        tp->prior_cwnd = tp->snd_cwnd;
        tp->prr_delivered = 0;
@@ -2756,7 +2562,7 @@ static void tcp_try_to_open(struct sock *sk, int flag, int newly_acked_sacked)
 
        tcp_verify_left_out(tp);
 
-       if (!tp->frto_counter && !tcp_any_retrans_done(sk))
+       if (!tcp_any_retrans_done(sk))
                tp->retrans_stamp = 0;
 
        if (flag & FLAG_ECE)
@@ -2873,6 +2679,58 @@ static void tcp_enter_recovery(struct sock *sk, bool ece_ack)
        tcp_set_ca_state(sk, TCP_CA_Recovery);
 }
 
+/* Process an ACK in CA_Loss state. Move to CA_Open if lost data are
+ * recovered or spurious. Otherwise retransmits more on partial ACKs.
+ */
+static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack)
+{
+       struct inet_connection_sock *icsk = inet_csk(sk);
+       struct tcp_sock *tp = tcp_sk(sk);
+       bool recovered = !before(tp->snd_una, tp->high_seq);
+
+       if (tp->frto) { /* F-RTO RFC5682 sec 3.1 (sack enhanced version). */
+               if (flag & FLAG_ORIG_SACK_ACKED) {
+                       /* Step 3.b. A timeout is spurious if not all data are
+                        * lost, i.e., never-retransmitted data are (s)acked.
+                        */
+                       tcp_try_undo_loss(sk, true);
+                       return;
+               }
+               if (after(tp->snd_nxt, tp->high_seq) &&
+                   (flag & FLAG_DATA_SACKED || is_dupack)) {
+                       tp->frto = 0; /* Loss was real: 2nd part of step 3.a */
+               } else if (flag & FLAG_SND_UNA_ADVANCED && !recovered) {
+                       tp->high_seq = tp->snd_nxt;
+                       __tcp_push_pending_frames(sk, tcp_current_mss(sk),
+                                                 TCP_NAGLE_OFF);
+                       if (after(tp->snd_nxt, tp->high_seq))
+                               return; /* Step 2.b */
+                       tp->frto = 0;
+               }
+       }
+
+       if (recovered) {
+               /* F-RTO RFC5682 sec 3.1 step 2.a and 1st part of step 3.a */
+               icsk->icsk_retransmits = 0;
+               tcp_try_undo_recovery(sk);
+               return;
+       }
+       if (flag & FLAG_DATA_ACKED)
+               icsk->icsk_retransmits = 0;
+       if (tcp_is_reno(tp)) {
+               /* A Reno DUPACK means new data in F-RTO step 2.b above are
+                * delivered. Lower inflight to clock out (re)tranmissions.
+                */
+               if (after(tp->snd_nxt, tp->high_seq) && is_dupack)
+                       tcp_add_reno_sack(sk);
+               else if (flag & FLAG_SND_UNA_ADVANCED)
+                       tcp_reset_reno_sack(tp);
+       }
+       if (tcp_try_undo_loss(sk, false))
+               return;
+       tcp_xmit_retransmit_queue(sk);
+}
+
 /* Process an event, which can update packets-in-flight not trivially.
  * Main goal of this function is to calculate new estimate for left_out,
  * taking into account both packets sitting in receiver's buffer and
@@ -2919,12 +2777,6 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
                tp->retrans_stamp = 0;
        } else if (!before(tp->snd_una, tp->high_seq)) {
                switch (icsk->icsk_ca_state) {
-               case TCP_CA_Loss:
-                       icsk->icsk_retransmits = 0;
-                       if (tcp_try_undo_recovery(sk))
-                               return;
-                       break;
-
                case TCP_CA_CWR:
                        /* CWR is to be held something *above* high_seq
                         * is ACKed for CWR bit to reach receiver. */
@@ -2955,18 +2807,10 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
                newly_acked_sacked = pkts_acked + tp->sacked_out - prior_sacked;
                break;
        case TCP_CA_Loss:
-               if (flag & FLAG_DATA_ACKED)
-                       icsk->icsk_retransmits = 0;
-               if (tcp_is_reno(tp) && flag & FLAG_SND_UNA_ADVANCED)
-                       tcp_reset_reno_sack(tp);
-               if (!tcp_try_undo_loss(sk)) {
-                       tcp_moderate_cwnd(tp);
-                       tcp_xmit_retransmit_queue(sk);
-                       return;
-               }
+               tcp_process_loss(sk, flag, is_dupack);
                if (icsk->icsk_ca_state != TCP_CA_Open)
                        return;
-               /* Loss is undone; fall through to processing in Open state. */
+               /* Fall through to processing in Open state. */
        default:
                if (tcp_is_reno(tp)) {
                        if (flag & FLAG_SND_UNA_ADVANCED)
@@ -3079,6 +2923,7 @@ static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
  */
 void tcp_rearm_rto(struct sock *sk)
 {
+       const struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
 
        /* If the retrans timer is currently being used by Fast Open
@@ -3092,12 +2937,13 @@ void tcp_rearm_rto(struct sock *sk)
        } else {
                u32 rto = inet_csk(sk)->icsk_rto;
                /* Offset the time elapsed after installing regular RTO */
-               if (tp->early_retrans_delayed) {
+               if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+                   icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
                        struct sk_buff *skb = tcp_write_queue_head(sk);
                        const u32 rto_time_stamp = TCP_SKB_CB(skb)->when + rto;
                        s32 delta = (s32)(rto_time_stamp - tcp_time_stamp);
                        /* delta may not be positive if the socket is locked
-                        * when the delayed ER timer fires and is rescheduled.
+                        * when the retrans timer fires and is rescheduled.
                         */
                        if (delta > 0)
                                rto = delta;
@@ -3105,7 +2951,6 @@ void tcp_rearm_rto(struct sock *sk)
                inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, rto,
                                          TCP_RTO_MAX);
        }
-       tp->early_retrans_delayed = 0;
 }
 
 /* This function is called when the delayed ER timer fires. TCP enters
@@ -3193,8 +3038,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
                        flag |= FLAG_RETRANS_DATA_ACKED;
                        ca_seq_rtt = -1;
                        seq_rtt = -1;
-                       if ((flag & FLAG_DATA_ACKED) || (acked_pcount > 1))
-                               flag |= FLAG_NONHEAD_RETRANS_ACKED;
                } else {
                        ca_seq_rtt = now - scb->when;
                        last_ackt = skb->tstamp;
@@ -3203,6 +3046,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
                        }
                        if (!(sacked & TCPCB_SACKED_ACKED))
                                reord = min(pkts_acked, reord);
+                       if (!after(scb->end_seq, tp->high_seq))
+                               flag |= FLAG_ORIG_SACK_ACKED;
                }
 
                if (sacked & TCPCB_SACKED_ACKED)
@@ -3403,150 +3248,6 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32
        return flag;
 }
 
-/* A very conservative spurious RTO response algorithm: reduce cwnd and
- * continue in congestion avoidance.
- */
-static void tcp_conservative_spur_to_response(struct tcp_sock *tp)
-{
-       tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh);
-       tp->snd_cwnd_cnt = 0;
-       TCP_ECN_queue_cwr(tp);
-       tcp_moderate_cwnd(tp);
-}
-
-/* A conservative spurious RTO response algorithm: reduce cwnd using
- * PRR and continue in congestion avoidance.
- */
-static void tcp_cwr_spur_to_response(struct sock *sk)
-{
-       tcp_enter_cwr(sk, 0);
-}
-
-static void tcp_undo_spur_to_response(struct sock *sk, int flag)
-{
-       if (flag & FLAG_ECE)
-               tcp_cwr_spur_to_response(sk);
-       else
-               tcp_undo_cwr(sk, true);
-}
-
-/* F-RTO spurious RTO detection algorithm (RFC4138)
- *
- * F-RTO affects during two new ACKs following RTO (well, almost, see inline
- * comments). State (ACK number) is kept in frto_counter. When ACK advances
- * window (but not to or beyond highest sequence sent before RTO):
- *   On First ACK,  send two new segments out.
- *   On Second ACK, RTO was likely spurious. Do spurious response (response
- *                  algorithm is not part of the F-RTO detection algorithm
- *                  given in RFC4138 but can be selected separately).
- * Otherwise (basically on duplicate ACK), RTO was (likely) caused by a loss
- * and TCP falls back to conventional RTO recovery. F-RTO allows overriding
- * of Nagle, this is done using frto_counter states 2 and 3, when a new data
- * segment of any size sent during F-RTO, state 2 is upgraded to 3.
- *
- * Rationale: if the RTO was spurious, new ACKs should arrive from the
- * original window even after we transmit two new data segments.
- *
- * SACK version:
- *   on first step, wait until first cumulative ACK arrives, then move to
- *   the second step. In second step, the next ACK decides.
- *
- * F-RTO is implemented (mainly) in four functions:
- *   - tcp_use_frto() is used to determine if TCP is can use F-RTO
- *   - tcp_enter_frto() prepares TCP state on RTO if F-RTO is used, it is
- *     called when tcp_use_frto() showed green light
- *   - tcp_process_frto() handles incoming ACKs during F-RTO algorithm
- *   - tcp_enter_frto_loss() is called if there is not enough evidence
- *     to prove that the RTO is indeed spurious. It transfers the control
- *     from F-RTO to the conventional RTO recovery
- */
-static bool tcp_process_frto(struct sock *sk, int flag)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-
-       tcp_verify_left_out(tp);
-
-       /* Duplicate the behavior from Loss state (fastretrans_alert) */
-       if (flag & FLAG_DATA_ACKED)
-               inet_csk(sk)->icsk_retransmits = 0;
-
-       if ((flag & FLAG_NONHEAD_RETRANS_ACKED) ||
-           ((tp->frto_counter >= 2) && (flag & FLAG_RETRANS_DATA_ACKED)))
-               tp->undo_marker = 0;
-
-       if (!before(tp->snd_una, tp->frto_highmark)) {
-               tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3), flag);
-               return true;
-       }
-
-       if (!tcp_is_sackfrto(tp)) {
-               /* RFC4138 shortcoming in step 2; should also have case c):
-                * ACK isn't duplicate nor advances window, e.g., opposite dir
-                * data, winupdate
-                */
-               if (!(flag & FLAG_ANY_PROGRESS) && (flag & FLAG_NOT_DUP))
-                       return true;
-
-               if (!(flag & FLAG_DATA_ACKED)) {
-                       tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 0 : 3),
-                                           flag);
-                       return true;
-               }
-       } else {
-               if (!(flag & FLAG_DATA_ACKED) && (tp->frto_counter == 1)) {
-                       if (!tcp_packets_in_flight(tp)) {
-                               tcp_enter_frto_loss(sk, 2, flag);
-                               return true;
-                       }
-
-                       /* Prevent sending of new data. */
-                       tp->snd_cwnd = min(tp->snd_cwnd,
-                                          tcp_packets_in_flight(tp));
-                       return true;
-               }
-
-               if ((tp->frto_counter >= 2) &&
-                   (!(flag & FLAG_FORWARD_PROGRESS) ||
-                    ((flag & FLAG_DATA_SACKED) &&
-                     !(flag & FLAG_ONLY_ORIG_SACKED)))) {
-                       /* RFC4138 shortcoming (see comment above) */
-                       if (!(flag & FLAG_FORWARD_PROGRESS) &&
-                           (flag & FLAG_NOT_DUP))
-                               return true;
-
-                       tcp_enter_frto_loss(sk, 3, flag);
-                       return true;
-               }
-       }
-
-       if (tp->frto_counter == 1) {
-               /* tcp_may_send_now needs to see updated state */
-               tp->snd_cwnd = tcp_packets_in_flight(tp) + 2;
-               tp->frto_counter = 2;
-
-               if (!tcp_may_send_now(sk))
-                       tcp_enter_frto_loss(sk, 2, flag);
-
-               return true;
-       } else {
-               switch (sysctl_tcp_frto_response) {
-               case 2:
-                       tcp_undo_spur_to_response(sk, flag);
-                       break;
-               case 1:
-                       tcp_conservative_spur_to_response(tp);
-                       break;
-               default:
-                       tcp_cwr_spur_to_response(sk);
-                       break;
-               }
-               tp->frto_counter = 0;
-               tp->undo_marker = 0;
-               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSPURIOUSRTOS);
-       }
-       return false;
-}
-
 /* RFC 5961 7 [ACK Throttling] */
 static void tcp_send_challenge_ack(struct sock *sk)
 {
@@ -3586,6 +3287,38 @@ static void tcp_replace_ts_recent(struct tcp_sock *tp, u32 seq)
        }
 }
 
+/* This routine deals with acks during a TLP episode.
+ * Ref: loss detection algorithm in draft-dukkipati-tcpm-tcp-loss-probe.
+ */
+static void tcp_process_tlp_ack(struct sock *sk, u32 ack, int flag)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       bool is_tlp_dupack = (ack == tp->tlp_high_seq) &&
+                            !(flag & (FLAG_SND_UNA_ADVANCED |
+                                      FLAG_NOT_DUP | FLAG_DATA_SACKED));
+
+       /* Mark the end of TLP episode on receiving TLP dupack or when
+        * ack is after tlp_high_seq.
+        */
+       if (is_tlp_dupack) {
+               tp->tlp_high_seq = 0;
+               return;
+       }
+
+       if (after(ack, tp->tlp_high_seq)) {
+               tp->tlp_high_seq = 0;
+               /* Don't reduce cwnd if DSACK arrives for TLP retrans. */
+               if (!(flag & FLAG_DSACKING_ACK)) {
+                       tcp_init_cwnd_reduction(sk, true);
+                       tcp_set_ca_state(sk, TCP_CA_CWR);
+                       tcp_end_cwnd_reduction(sk);
+                       tcp_set_ca_state(sk, TCP_CA_Open);
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPLOSSPROBERECOVERY);
+               }
+       }
+}
+
 /* This routine deals with incoming acks, but not outgoing ones. */
 static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
 {
@@ -3600,7 +3333,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        int prior_packets;
        int prior_sacked = tp->sacked_out;
        int pkts_acked = 0;
-       bool frto_cwnd = false;
 
        /* If the ack is older than previous acks
         * then we can probably ignore it.
@@ -3620,7 +3352,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        if (after(ack, tp->snd_nxt))
                goto invalid_ack;
 
-       if (tp->early_retrans_delayed)
+       if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+           icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)
                tcp_rearm_rto(sk);
 
        if (after(ack, prior_snd_una))
@@ -3679,30 +3412,29 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
 
        pkts_acked = prior_packets - tp->packets_out;
 
-       if (tp->frto_counter)
-               frto_cwnd = tcp_process_frto(sk, flag);
-       /* Guarantee sacktag reordering detection against wrap-arounds */
-       if (before(tp->frto_highmark, tp->snd_una))
-               tp->frto_highmark = 0;
-
        if (tcp_ack_is_dubious(sk, flag)) {
                /* Advance CWND, if state allows this. */
-               if ((flag & FLAG_DATA_ACKED) && !frto_cwnd &&
-                   tcp_may_raise_cwnd(sk, flag))
+               if ((flag & FLAG_DATA_ACKED) && tcp_may_raise_cwnd(sk, flag))
                        tcp_cong_avoid(sk, ack, prior_in_flight);
                is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
                tcp_fastretrans_alert(sk, pkts_acked, prior_sacked,
                                      is_dupack, flag);
        } else {
-               if ((flag & FLAG_DATA_ACKED) && !frto_cwnd)
+               if (flag & FLAG_DATA_ACKED)
                        tcp_cong_avoid(sk, ack, prior_in_flight);
        }
 
+       if (tp->tlp_high_seq)
+               tcp_process_tlp_ack(sk, ack, flag);
+
        if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) {
                struct dst_entry *dst = __sk_dst_get(sk);
                if (dst)
                        dst_confirm(dst);
        }
+
+       if (icsk->icsk_pending == ICSK_TIME_RETRANS)
+               tcp_schedule_loss_probe(sk);
        return 1;
 
 no_queue:
@@ -3716,6 +3448,9 @@ no_queue:
         */
        if (tcp_send_head(sk))
                tcp_ack_probe(sk);
+
+       if (tp->tlp_high_seq)
+               tcp_process_tlp_ack(sk, ack, flag);
        return 1;
 
 invalid_ack:
@@ -3740,8 +3475,8 @@ old_ack:
  * But, this can also be called on packets in the established flow when
  * the fast version below fails.
  */
-void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *opt_rx,
-                      const u8 **hvpp, int estab,
+void tcp_parse_options(const struct sk_buff *skb,
+                      struct tcp_options_received *opt_rx, int estab,
                       struct tcp_fastopen_cookie *foc)
 {
        const unsigned char *ptr;
@@ -3825,31 +3560,6 @@ void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *o
                                 */
                                break;
 #endif
-                       case TCPOPT_COOKIE:
-                               /* This option is variable length.
-                                */
-                               switch (opsize) {
-                               case TCPOLEN_COOKIE_BASE:
-                                       /* not yet implemented */
-                                       break;
-                               case TCPOLEN_COOKIE_PAIR:
-                                       /* not yet implemented */
-                                       break;
-                               case TCPOLEN_COOKIE_MIN+0:
-                               case TCPOLEN_COOKIE_MIN+2:
-                               case TCPOLEN_COOKIE_MIN+4:
-                               case TCPOLEN_COOKIE_MIN+6:
-                               case TCPOLEN_COOKIE_MAX:
-                                       /* 16-bit multiple */
-                                       opt_rx->cookie_plus = opsize;
-                                       *hvpp = ptr;
-                                       break;
-                               default:
-                                       /* ignore option */
-                                       break;
-                               }
-                               break;
-
                        case TCPOPT_EXP:
                                /* Fast Open option shares code 254 using a
                                 * 16 bits magic number. It's valid only in
@@ -3895,8 +3605,7 @@ static bool tcp_parse_aligned_timestamp(struct tcp_sock *tp, const struct tcphdr
  * If it is wrong it falls back on tcp_parse_options().
  */
 static bool tcp_fast_parse_options(const struct sk_buff *skb,
-                                  const struct tcphdr *th,
-                                  struct tcp_sock *tp, const u8 **hvpp)
+                                  const struct tcphdr *th, struct tcp_sock *tp)
 {
        /* In the spirit of fast parsing, compare doff directly to constant
         * values.  Because equality is used, short doff can be ignored here.
@@ -3910,7 +3619,7 @@ static bool tcp_fast_parse_options(const struct sk_buff *skb,
                        return true;
        }
 
-       tcp_parse_options(skb, &tp->rx_opt, hvpp, 1, NULL);
+       tcp_parse_options(skb, &tp->rx_opt, 1, NULL);
        if (tp->rx_opt.saw_tstamp)
                tp->rx_opt.rcv_tsecr -= tp->tsoffset;
 
@@ -5270,12 +4979,10 @@ out:
 static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
                                  const struct tcphdr *th, int syn_inerr)
 {
-       const u8 *hash_location;
        struct tcp_sock *tp = tcp_sk(sk);
 
        /* RFC1323: H1. Apply PAWS check first. */
-       if (tcp_fast_parse_options(skb, th, tp, &hash_location) &&
-           tp->rx_opt.saw_tstamp &&
+       if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
            tcp_paws_discard(sk, skb)) {
                if (!th->rst) {
                        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
@@ -5624,12 +5331,11 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
 
        if (mss == tp->rx_opt.user_mss) {
                struct tcp_options_received opt;
-               const u8 *hash_location;
 
                /* Get original SYNACK MSS value if user MSS sets mss_clamp */
                tcp_clear_options(&opt);
                opt.user_mss = opt.mss_clamp = 0;
-               tcp_parse_options(synack, &opt, &hash_location, 0, NULL);
+               tcp_parse_options(synack, &opt, 0, NULL);
                mss = opt.mss_clamp;
        }
 
@@ -5660,14 +5366,12 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                                         const struct tcphdr *th, unsigned int len)
 {
-       const u8 *hash_location;
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
-       struct tcp_cookie_values *cvp = tp->cookie_values;
        struct tcp_fastopen_cookie foc = { .len = -1 };
        int saved_clamp = tp->rx_opt.mss_clamp;
 
-       tcp_parse_options(skb, &tp->rx_opt, &hash_location, 0, &foc);
+       tcp_parse_options(skb, &tp->rx_opt, 0, &foc);
        if (tp->rx_opt.saw_tstamp)
                tp->rx_opt.rcv_tsecr -= tp->tsoffset;
 
@@ -5764,30 +5468,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                 * is initialized. */
                tp->copied_seq = tp->rcv_nxt;
 
-               if (cvp != NULL &&
-                   cvp->cookie_pair_size > 0 &&
-                   tp->rx_opt.cookie_plus > 0) {
-                       int cookie_size = tp->rx_opt.cookie_plus
-                                       - TCPOLEN_COOKIE_BASE;
-                       int cookie_pair_size = cookie_size
-                                            + cvp->cookie_desired;
-
-                       /* A cookie extension option was sent and returned.
-                        * Note that each incoming SYNACK replaces the
-                        * Responder cookie.  The initial exchange is most
-                        * fragile, as protection against spoofing relies
-                        * entirely upon the sequence and timestamp (above).
-                        * This replacement strategy allows the correct pair to
-                        * pass through, while any others will be filtered via
-                        * Responder verification later.
-                        */
-                       if (sizeof(cvp->cookie_pair) >= cookie_pair_size) {
-                               memcpy(&cvp->cookie_pair[cvp->cookie_desired],
-                                      hash_location, cookie_size);
-                               cvp->cookie_pair_size = cookie_pair_size;
-                       }
-               }
-
                smp_mb();
 
                tcp_finish_connect(sk, skb);
index d09203c63264d2d8c11f7b40d42200f82934213b..2278669b1d85b4577c680679a0f443c99314a962 100644 (file)
@@ -838,7 +838,6 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
  */
 static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
                              struct request_sock *req,
-                             struct request_values *rvp,
                              u16 queue_mapping,
                              bool nocache)
 {
@@ -851,7 +850,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
        if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
                return -1;
 
-       skb = tcp_make_synack(sk, dst, req, rvp, NULL);
+       skb = tcp_make_synack(sk, dst, req, NULL);
 
        if (skb) {
                __tcp_v4_send_check(skb, ireq->loc_addr, ireq->rmt_addr);
@@ -868,10 +867,9 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
        return err;
 }
 
-static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req,
-                            struct request_values *rvp)
+static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req)
 {
-       int res = tcp_v4_send_synack(sk, NULL, req, rvp, 0, false);
+       int res = tcp_v4_send_synack(sk, NULL, req, 0, false);
 
        if (!res)
                TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
@@ -1371,8 +1369,7 @@ static bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb,
 static int tcp_v4_conn_req_fastopen(struct sock *sk,
                                    struct sk_buff *skb,
                                    struct sk_buff *skb_synack,
-                                   struct request_sock *req,
-                                   struct request_values *rvp)
+                                   struct request_sock *req)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
@@ -1467,9 +1464,7 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk,
 
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 {
-       struct tcp_extend_values tmp_ext;
        struct tcp_options_received tmp_opt;
-       const u8 *hash_location;
        struct request_sock *req;
        struct inet_request_sock *ireq;
        struct tcp_sock *tp = tcp_sk(sk);
@@ -1519,42 +1514,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_clear_options(&tmp_opt);
        tmp_opt.mss_clamp = TCP_MSS_DEFAULT;
        tmp_opt.user_mss  = tp->rx_opt.user_mss;
-       tcp_parse_options(skb, &tmp_opt, &hash_location, 0,
-           want_cookie ? NULL : &foc);
-
-       if (tmp_opt.cookie_plus > 0 &&
-           tmp_opt.saw_tstamp &&
-           !tp->rx_opt.cookie_out_never &&
-           (sysctl_tcp_cookie_size > 0 ||
-            (tp->cookie_values != NULL &&
-             tp->cookie_values->cookie_desired > 0))) {
-               u8 *c;
-               u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS];
-               int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE;
-
-               if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0)
-                       goto drop_and_release;
-
-               /* Secret recipe starts with IP addresses */
-               *mess++ ^= (__force u32)daddr;
-               *mess++ ^= (__force u32)saddr;
-
-               /* plus variable length Initiator Cookie */
-               c = (u8 *)mess;
-               while (l-- > 0)
-                       *c++ ^= *hash_location++;
-
-               want_cookie = false;    /* not our kind of cookie */
-               tmp_ext.cookie_out_never = 0; /* false */
-               tmp_ext.cookie_plus = tmp_opt.cookie_plus;
-       } else if (!tp->rx_opt.cookie_in_always) {
-               /* redundant indications, but ensure initialization. */
-               tmp_ext.cookie_out_never = 1; /* true */
-               tmp_ext.cookie_plus = 0;
-       } else {
-               goto drop_and_release;
-       }
-       tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;
+       tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);
 
        if (want_cookie && !tmp_opt.saw_tstamp)
                tcp_clear_options(&tmp_opt);
@@ -1636,7 +1596,6 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
         * of tcp_v4_send_synack()->tcp_select_initial_window().
         */
        skb_synack = tcp_make_synack(sk, dst, req,
-           (struct request_values *)&tmp_ext,
            fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL);
 
        if (skb_synack) {
@@ -1660,8 +1619,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
                if (fastopen_cookie_present(&foc) && foc.len != 0)
                        NET_INC_STATS_BH(sock_net(sk),
                            LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
-       } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req,
-           (struct request_values *)&tmp_ext))
+       } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req))
                goto drop_and_free;
 
        return 0;
@@ -1950,6 +1908,50 @@ void tcp_v4_early_demux(struct sk_buff *skb)
        }
 }
 
+/* Packet is added to VJ-style prequeue for processing in process
+ * context, if a reader task is waiting. Apparently, this exciting
+ * idea (VJ's mail "Re: query about TCP header on tcp-ip" of 07 Sep 93)
+ * failed somewhere. Latency? Burstiness? Well, at least now we will
+ * see, why it failed. 8)8)                              --ANK
+ *
+ */
+bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (sysctl_tcp_low_latency || !tp->ucopy.task)
+               return false;
+
+       if (skb->len <= tcp_hdrlen(skb) &&
+           skb_queue_len(&tp->ucopy.prequeue) == 0)
+               return false;
+
+       __skb_queue_tail(&tp->ucopy.prequeue, skb);
+       tp->ucopy.memory += skb->truesize;
+       if (tp->ucopy.memory > sk->sk_rcvbuf) {
+               struct sk_buff *skb1;
+
+               BUG_ON(sock_owned_by_user(sk));
+
+               while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
+                       sk_backlog_rcv(sk, skb1);
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPPREQUEUEDROPPED);
+               }
+
+               tp->ucopy.memory = 0;
+       } else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
+               wake_up_interruptible_sync_poll(sk_sleep(sk),
+                                          POLLIN | POLLRDNORM | POLLRDBAND);
+               if (!inet_csk_ack_scheduled(sk))
+                       inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
+                                                 (3 * tcp_rto_min(sk)) / 4,
+                                                 TCP_RTO_MAX);
+       }
+       return true;
+}
+EXPORT_SYMBOL(tcp_prequeue);
+
 /*
  *     From tcp_input.c
  */
@@ -2197,12 +2199,6 @@ void tcp_v4_destroy_sock(struct sock *sk)
        if (inet_csk(sk)->icsk_bind_hash)
                inet_put_port(sk);
 
-       /* TCP Cookie Transactions */
-       if (tp->cookie_values != NULL) {
-               kref_put(&tp->cookie_values->kref,
-                        tcp_cookie_values_release);
-               tp->cookie_values = NULL;
-       }
        BUG_ON(tp->fastopen_rsk != NULL);
 
        /* If socket is aborted during connect operation */
@@ -2659,7 +2655,9 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
        __u16 srcp = ntohs(inet->inet_sport);
        int rx_queue;
 
-       if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
+       if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
+           icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+           icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
                timer_active    = 1;
                timer_expires   = icsk->icsk_timeout;
        } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
index b6f3583ddfe83eae73370c9070c567e452518f57..da14436c1735677f7207030cfb8d76ae15a1157f 100644 (file)
@@ -64,7 +64,6 @@ void tcp_destroy_cgroup(struct mem_cgroup *memcg)
 {
        struct cg_proto *cg_proto;
        struct tcp_memcontrol *tcp;
-       u64 val;
 
        cg_proto = tcp_prot.proto_cgroup(memcg);
        if (!cg_proto)
@@ -72,8 +71,6 @@ void tcp_destroy_cgroup(struct mem_cgroup *memcg)
 
        tcp = tcp_from_cgproto(cg_proto);
        percpu_counter_destroy(&tcp->tcp_sockets_allocated);
-
-       val = res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT);
 }
 EXPORT_SYMBOL(tcp_destroy_cgroup);
 
index b83a49cc38169a654a14b94581f72eae09466c52..05eaf890461359131e8cd8a20d6adea0c7be740f 100644 (file)
@@ -93,13 +93,12 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
                           const struct tcphdr *th)
 {
        struct tcp_options_received tmp_opt;
-       const u8 *hash_location;
        struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
        bool paws_reject = false;
 
        tmp_opt.saw_tstamp = 0;
        if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) {
-               tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL);
+               tcp_parse_options(skb, &tmp_opt, 0, NULL);
 
                if (tmp_opt.saw_tstamp) {
                        tmp_opt.rcv_tsecr       -= tcptw->tw_ts_offset;
@@ -388,32 +387,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                struct tcp_request_sock *treq = tcp_rsk(req);
                struct inet_connection_sock *newicsk = inet_csk(newsk);
                struct tcp_sock *newtp = tcp_sk(newsk);
-               struct tcp_sock *oldtp = tcp_sk(sk);
-               struct tcp_cookie_values *oldcvp = oldtp->cookie_values;
-
-               /* TCP Cookie Transactions require space for the cookie pair,
-                * as it differs for each connection.  There is no need to
-                * copy any s_data_payload stored at the original socket.
-                * Failure will prevent resuming the connection.
-                *
-                * Presumed copied, in order of appearance:
-                *      cookie_in_always, cookie_out_never
-                */
-               if (oldcvp != NULL) {
-                       struct tcp_cookie_values *newcvp =
-                               kzalloc(sizeof(*newtp->cookie_values),
-                                       GFP_ATOMIC);
-
-                       if (newcvp != NULL) {
-                               kref_init(&newcvp->kref);
-                               newcvp->cookie_desired =
-                                               oldcvp->cookie_desired;
-                               newtp->cookie_values = newcvp;
-                       } else {
-                               /* Not Yet Implemented */
-                               newtp->cookie_values = NULL;
-                       }
-               }
 
                /* Now setup tcp_sock */
                newtp->pred_flags = 0;
@@ -422,8 +395,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                newtp->rcv_nxt = treq->rcv_isn + 1;
 
                newtp->snd_sml = newtp->snd_una =
-               newtp->snd_nxt = newtp->snd_up =
-                       treq->snt_isn + 1 + tcp_s_data_size(oldtp);
+               newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1;
 
                tcp_prequeue_init(newtp);
                INIT_LIST_HEAD(&newtp->tsq_node);
@@ -440,6 +412,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                newtp->fackets_out = 0;
                newtp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
                tcp_enable_early_retrans(newtp);
+               newtp->tlp_high_seq = 0;
 
                /* So many TCP implementations out there (incorrectly) count the
                 * initial SYN frame in their delayed-ACK and congestion control
@@ -449,9 +422,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                newtp->snd_cwnd = TCP_INIT_CWND;
                newtp->snd_cwnd_cnt = 0;
 
-               newtp->frto_counter = 0;
-               newtp->frto_highmark = 0;
-
                if (newicsk->icsk_ca_ops != &tcp_init_congestion_ops &&
                    !try_module_get(newicsk->icsk_ca_ops->owner))
                        newicsk->icsk_ca_ops = &tcp_init_congestion_ops;
@@ -459,8 +429,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                tcp_set_ca_state(newsk, TCP_CA_Open);
                tcp_init_xmit_timers(newsk);
                skb_queue_head_init(&newtp->out_of_order_queue);
-               newtp->write_seq = newtp->pushed_seq =
-                       treq->snt_isn + 1 + tcp_s_data_size(oldtp);
+               newtp->write_seq = newtp->pushed_seq = treq->snt_isn + 1;
 
                newtp->rx_opt.saw_tstamp = 0;
 
@@ -537,7 +506,6 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
                           bool fastopen)
 {
        struct tcp_options_received tmp_opt;
-       const u8 *hash_location;
        struct sock *child;
        const struct tcphdr *th = tcp_hdr(skb);
        __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
@@ -547,7 +515,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
 
        tmp_opt.saw_tstamp = 0;
        if (th->doff > (sizeof(struct tcphdr)>>2)) {
-               tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL);
+               tcp_parse_options(skb, &tmp_opt, 0, NULL);
 
                if (tmp_opt.saw_tstamp) {
                        tmp_opt.ts_recent = req->ts_recent;
@@ -647,7 +615,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
         */
        if ((flg & TCP_FLAG_ACK) && !fastopen &&
            (TCP_SKB_CB(skb)->ack_seq !=
-            tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk))))
+            tcp_rsk(req)->snt_isn + 1))
                return sk;
 
        /* Also, it would be not so bad idea to check rcv_tsecr, which
index 509912a5ff98e73edfe9d60295de196ed16874a2..b735c23a961df203fd16a03c757badecd0a6dbfc 100644 (file)
@@ -65,27 +65,22 @@ int sysctl_tcp_base_mss __read_mostly = TCP_BASE_MSS;
 /* By default, RFC2861 behavior.  */
 int sysctl_tcp_slow_start_after_idle __read_mostly = 1;
 
-int sysctl_tcp_cookie_size __read_mostly = 0; /* TCP_COOKIE_MAX */
-EXPORT_SYMBOL_GPL(sysctl_tcp_cookie_size);
-
 static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                           int push_one, gfp_t gfp);
 
 /* Account for new data that has been sent to the network. */
 static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb)
 {
+       struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        unsigned int prior_packets = tp->packets_out;
 
        tcp_advance_send_head(sk, skb);
        tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
 
-       /* Don't override Nagle indefinitely with F-RTO */
-       if (tp->frto_counter == 2)
-               tp->frto_counter = 3;
-
        tp->packets_out += tcp_skb_pcount(skb);
-       if (!prior_packets || tp->early_retrans_delayed)
+       if (!prior_packets || icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+           icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)
                tcp_rearm_rto(sk);
 }
 
@@ -384,7 +379,6 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp)
 #define OPTION_TS              (1 << 1)
 #define OPTION_MD5             (1 << 2)
 #define OPTION_WSCALE          (1 << 3)
-#define OPTION_COOKIE_EXTENSION        (1 << 4)
 #define OPTION_FAST_OPEN_COOKIE        (1 << 8)
 
 struct tcp_out_options {
@@ -398,36 +392,6 @@ struct tcp_out_options {
        struct tcp_fastopen_cookie *fastopen_cookie;    /* Fast open cookie */
 };
 
-/* The sysctl int routines are generic, so check consistency here.
- */
-static u8 tcp_cookie_size_check(u8 desired)
-{
-       int cookie_size;
-
-       if (desired > 0)
-               /* previously specified */
-               return desired;
-
-       cookie_size = ACCESS_ONCE(sysctl_tcp_cookie_size);
-       if (cookie_size <= 0)
-               /* no default specified */
-               return 0;
-
-       if (cookie_size <= TCP_COOKIE_MIN)
-               /* value too small, specify minimum */
-               return TCP_COOKIE_MIN;
-
-       if (cookie_size >= TCP_COOKIE_MAX)
-               /* value too large, specify maximum */
-               return TCP_COOKIE_MAX;
-
-       if (cookie_size & 1)
-               /* 8-bit multiple, illegal, fix it */
-               cookie_size++;
-
-       return (u8)cookie_size;
-}
-
 /* Write previously computed TCP options to the packet.
  *
  * Beware: Something in the Internet is very sensitive to the ordering of
@@ -446,27 +410,9 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
 {
        u16 options = opts->options;    /* mungable copy */
 
-       /* Having both authentication and cookies for security is redundant,
-        * and there's certainly not enough room.  Instead, the cookie-less
-        * extension variant is proposed.
-        *
-        * Consider the pessimal case with authentication.  The options
-        * could look like:
-        *   COOKIE|MD5(20) + MSS(4) + SACK|TS(12) + WSCALE(4) == 40
-        */
        if (unlikely(OPTION_MD5 & options)) {
-               if (unlikely(OPTION_COOKIE_EXTENSION & options)) {
-                       *ptr++ = htonl((TCPOPT_COOKIE << 24) |
-                                      (TCPOLEN_COOKIE_BASE << 16) |
-                                      (TCPOPT_MD5SIG << 8) |
-                                      TCPOLEN_MD5SIG);
-               } else {
-                       *ptr++ = htonl((TCPOPT_NOP << 24) |
-                                      (TCPOPT_NOP << 16) |
-                                      (TCPOPT_MD5SIG << 8) |
-                                      TCPOLEN_MD5SIG);
-               }
-               options &= ~OPTION_COOKIE_EXTENSION;
+               *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+                              (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG);
                /* overload cookie hash location */
                opts->hash_location = (__u8 *)ptr;
                ptr += 4;
@@ -495,44 +441,6 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
                *ptr++ = htonl(opts->tsecr);
        }
 
-       /* Specification requires after timestamp, so do it now.
-        *
-        * Consider the pessimal case without authentication.  The options
-        * could look like:
-        *   MSS(4) + SACK|TS(12) + COOKIE(20) + WSCALE(4) == 40
-        */
-       if (unlikely(OPTION_COOKIE_EXTENSION & options)) {
-               __u8 *cookie_copy = opts->hash_location;
-               u8 cookie_size = opts->hash_size;
-
-               /* 8-bit multiple handled in tcp_cookie_size_check() above,
-                * and elsewhere.
-                */
-               if (0x2 & cookie_size) {
-                       __u8 *p = (__u8 *)ptr;
-
-                       /* 16-bit multiple */
-                       *p++ = TCPOPT_COOKIE;
-                       *p++ = TCPOLEN_COOKIE_BASE + cookie_size;
-                       *p++ = *cookie_copy++;
-                       *p++ = *cookie_copy++;
-                       ptr++;
-                       cookie_size -= 2;
-               } else {
-                       /* 32-bit multiple */
-                       *ptr++ = htonl(((TCPOPT_NOP << 24) |
-                                       (TCPOPT_NOP << 16) |
-                                       (TCPOPT_COOKIE << 8) |
-                                       TCPOLEN_COOKIE_BASE) +
-                                      cookie_size);
-               }
-
-               if (cookie_size > 0) {
-                       memcpy(ptr, cookie_copy, cookie_size);
-                       ptr += (cookie_size / 4);
-               }
-       }
-
        if (unlikely(OPTION_SACK_ADVERTISE & options)) {
                *ptr++ = htonl((TCPOPT_NOP << 24) |
                               (TCPOPT_NOP << 16) |
@@ -591,11 +499,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
                                struct tcp_md5sig_key **md5)
 {
        struct tcp_sock *tp = tcp_sk(sk);
-       struct tcp_cookie_values *cvp = tp->cookie_values;
        unsigned int remaining = MAX_TCP_OPTION_SPACE;
-       u8 cookie_size = (!tp->rx_opt.cookie_out_never && cvp != NULL) ?
-                        tcp_cookie_size_check(cvp->cookie_desired) :
-                        0;
        struct tcp_fastopen_request *fastopen = tp->fastopen_req;
 
 #ifdef CONFIG_TCP_MD5SIG
@@ -647,52 +551,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
                        tp->syn_fastopen = 1;
                }
        }
-       /* Note that timestamps are required by the specification.
-        *
-        * Odd numbers of bytes are prohibited by the specification, ensuring
-        * that the cookie is 16-bit aligned, and the resulting cookie pair is
-        * 32-bit aligned.
-        */
-       if (*md5 == NULL &&
-           (OPTION_TS & opts->options) &&
-           cookie_size > 0) {
-               int need = TCPOLEN_COOKIE_BASE + cookie_size;
-
-               if (0x2 & need) {
-                       /* 32-bit multiple */
-                       need += 2; /* NOPs */
-
-                       if (need > remaining) {
-                               /* try shrinking cookie to fit */
-                               cookie_size -= 2;
-                               need -= 4;
-                       }
-               }
-               while (need > remaining && TCP_COOKIE_MIN <= cookie_size) {
-                       cookie_size -= 4;
-                       need -= 4;
-               }
-               if (TCP_COOKIE_MIN <= cookie_size) {
-                       opts->options |= OPTION_COOKIE_EXTENSION;
-                       opts->hash_location = (__u8 *)&cvp->cookie_pair[0];
-                       opts->hash_size = cookie_size;
-
-                       /* Remember for future incarnations. */
-                       cvp->cookie_desired = cookie_size;
-
-                       if (cvp->cookie_desired != cvp->cookie_pair_size) {
-                               /* Currently use random bytes as a nonce,
-                                * assuming these are completely unpredictable
-                                * by hostile users of the same system.
-                                */
-                               get_random_bytes(&cvp->cookie_pair[0],
-                                                cookie_size);
-                               cvp->cookie_pair_size = cookie_size;
-                       }
 
-                       remaining -= need;
-               }
-       }
        return MAX_TCP_OPTION_SPACE - remaining;
 }
 
@@ -702,14 +561,10 @@ static unsigned int tcp_synack_options(struct sock *sk,
                                   unsigned int mss, struct sk_buff *skb,
                                   struct tcp_out_options *opts,
                                   struct tcp_md5sig_key **md5,
-                                  struct tcp_extend_values *xvp,
                                   struct tcp_fastopen_cookie *foc)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        unsigned int remaining = MAX_TCP_OPTION_SPACE;
-       u8 cookie_plus = (xvp != NULL && !xvp->cookie_out_never) ?
-                        xvp->cookie_plus :
-                        0;
 
 #ifdef CONFIG_TCP_MD5SIG
        *md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);
@@ -757,28 +612,7 @@ static unsigned int tcp_synack_options(struct sock *sk,
                        remaining -= need;
                }
        }
-       /* Similar rationale to tcp_syn_options() applies here, too.
-        * If the <SYN> options fit, the same options should fit now!
-        */
-       if (*md5 == NULL &&
-           ireq->tstamp_ok &&
-           cookie_plus > TCPOLEN_COOKIE_BASE) {
-               int need = cookie_plus; /* has TCPOLEN_COOKIE_BASE */
-
-               if (0x2 & need) {
-                       /* 32-bit multiple */
-                       need += 2; /* NOPs */
-               }
-               if (need <= remaining) {
-                       opts->options |= OPTION_COOKIE_EXTENSION;
-                       opts->hash_size = cookie_plus - TCPOLEN_COOKIE_BASE;
-                       remaining -= need;
-               } else {
-                       /* There's no error return, so flag it. */
-                       xvp->cookie_out_never = 1; /* true */
-                       opts->hash_size = 0;
-               }
-       }
+
        return MAX_TCP_OPTION_SPACE - remaining;
 }
 
@@ -953,7 +787,7 @@ void __init tcp_tasklet_init(void)
  * We cant xmit new skbs from this context, as we might already
  * hold qdisc lock.
  */
-static void tcp_wfree(struct sk_buff *skb)
+void tcp_wfree(struct sk_buff *skb)
 {
        struct sock *sk = skb->sk;
        struct tcp_sock *tp = tcp_sk(sk);
@@ -1012,6 +846,13 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
                __net_timestamp(skb);
 
        if (likely(clone_it)) {
+               const struct sk_buff *fclone = skb + 1;
+
+               if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
+                            fclone->fclone == SKB_FCLONE_CLONE))
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);
+
                if (unlikely(skb_cloned(skb)))
                        skb = pskb_copy(skb, gfp_mask);
                else
@@ -1632,11 +1473,8 @@ static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buf
        if (nonagle & TCP_NAGLE_PUSH)
                return true;
 
-       /* Don't use the nagle rule for urgent data (or for the final FIN).
-        * Nagle can be ignored during F-RTO too (see RFC4138).
-        */
-       if (tcp_urg_mode(tp) || (tp->frto_counter == 2) ||
-           (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN))
+       /* Don't use the nagle rule for urgent data (or for the final FIN). */
+       if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN))
                return true;
 
        if (!tcp_nagle_check(tp, skb, cur_mss, nonagle))
@@ -1961,6 +1799,9 @@ static int tcp_mtu_probe(struct sock *sk)
  * snd_up-64k-mss .. snd_up cannot be large. However, taking into
  * account rare use of URG, this is not a big flaw.
  *
+ * Send at most one packet when push_one > 0. Temporarily ignore
+ * cwnd limit to force at most one packet out when push_one == 2.
+
  * Returns true, if no segments are in flight and we have queued segments,
  * but cannot send anything now because of SWS or another problem.
  */
@@ -1996,8 +1837,13 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                        goto repair; /* Skip network transmission */
 
                cwnd_quota = tcp_cwnd_test(tp, skb);
-               if (!cwnd_quota)
-                       break;
+               if (!cwnd_quota) {
+                       if (push_one == 2)
+                               /* Force out a loss probe pkt. */
+                               cwnd_quota = 1;
+                       else
+                               break;
+               }
 
                if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now)))
                        break;
@@ -2051,10 +1897,129 @@ repair:
        if (likely(sent_pkts)) {
                if (tcp_in_cwnd_reduction(sk))
                        tp->prr_out += sent_pkts;
+
+               /* Send one loss probe per tail loss episode. */
+               if (push_one != 2)
+                       tcp_schedule_loss_probe(sk);
                tcp_cwnd_validate(sk);
                return false;
        }
-       return !tp->packets_out && tcp_send_head(sk);
+       return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk));
+}
+
+bool tcp_schedule_loss_probe(struct sock *sk)
+{
+       struct inet_connection_sock *icsk = inet_csk(sk);
+       struct tcp_sock *tp = tcp_sk(sk);
+       u32 timeout, tlp_time_stamp, rto_time_stamp;
+       u32 rtt = tp->srtt >> 3;
+
+       if (WARN_ON(icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS))
+               return false;
+       /* No consecutive loss probes. */
+       if (WARN_ON(icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)) {
+               tcp_rearm_rto(sk);
+               return false;
+       }
+       /* Don't do any loss probe on a Fast Open connection before 3WHS
+        * finishes.
+        */
+       if (sk->sk_state == TCP_SYN_RECV)
+               return false;
+
+       /* TLP is only scheduled when next timer event is RTO. */
+       if (icsk->icsk_pending != ICSK_TIME_RETRANS)
+               return false;
+
+       /* Schedule a loss probe in 2*RTT for SACK capable connections
+        * in Open state, that are either limited by cwnd or application.
+        */
+       if (sysctl_tcp_early_retrans < 3 || !rtt || !tp->packets_out ||
+           !tcp_is_sack(tp) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open)
+               return false;
+
+       if ((tp->snd_cwnd > tcp_packets_in_flight(tp)) &&
+            tcp_send_head(sk))
+               return false;
+
+       /* Probe timeout is at least 1.5*rtt + TCP_DELACK_MAX to account
+        * for delayed ack when there's one outstanding packet.
+        */
+       timeout = rtt << 1;
+       if (tp->packets_out == 1)
+               timeout = max_t(u32, timeout,
+                               (rtt + (rtt >> 1) + TCP_DELACK_MAX));
+       timeout = max_t(u32, timeout, msecs_to_jiffies(10));
+
+       /* If RTO is shorter, just schedule TLP in its place. */
+       tlp_time_stamp = tcp_time_stamp + timeout;
+       rto_time_stamp = (u32)inet_csk(sk)->icsk_timeout;
+       if ((s32)(tlp_time_stamp - rto_time_stamp) > 0) {
+               s32 delta = rto_time_stamp - tcp_time_stamp;
+               if (delta > 0)
+                       timeout = delta;
+       }
+
+       inet_csk_reset_xmit_timer(sk, ICSK_TIME_LOSS_PROBE, timeout,
+                                 TCP_RTO_MAX);
+       return true;
+}
+
+/* When probe timeout (PTO) fires, send a new segment if one exists, else
+ * retransmit the last segment.
+ */
+void tcp_send_loss_probe(struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       struct sk_buff *skb;
+       int pcount;
+       int mss = tcp_current_mss(sk);
+       int err = -1;
+
+       if (tcp_send_head(sk) != NULL) {
+               err = tcp_write_xmit(sk, mss, TCP_NAGLE_OFF, 2, GFP_ATOMIC);
+               goto rearm_timer;
+       }
+
+       /* At most one outstanding TLP retransmission. */
+       if (tp->tlp_high_seq)
+               goto rearm_timer;
+
+       /* Retransmit last segment. */
+       skb = tcp_write_queue_tail(sk);
+       if (WARN_ON(!skb))
+               goto rearm_timer;
+
+       pcount = tcp_skb_pcount(skb);
+       if (WARN_ON(!pcount))
+               goto rearm_timer;
+
+       if ((pcount > 1) && (skb->len > (pcount - 1) * mss)) {
+               if (unlikely(tcp_fragment(sk, skb, (pcount - 1) * mss, mss)))
+                       goto rearm_timer;
+               skb = tcp_write_queue_tail(sk);
+       }
+
+       if (WARN_ON(!skb || !tcp_skb_pcount(skb)))
+               goto rearm_timer;
+
+       /* Probe with zero data doesn't trigger fast recovery. */
+       if (skb->len > 0)
+               err = __tcp_retransmit_skb(sk, skb);
+
+       /* Record snd_nxt for loss detection. */
+       if (likely(!err))
+               tp->tlp_high_seq = tp->snd_nxt;
+
+rearm_timer:
+       inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
+                                 inet_csk(sk)->icsk_rto,
+                                 TCP_RTO_MAX);
+
+       if (likely(!err))
+               NET_INC_STATS_BH(sock_net(sk),
+                                LINUX_MIB_TCPLOSSPROBES);
+       return;
 }
 
 /* Push out any pending frames which were held back due to
@@ -2679,32 +2644,24 @@ int tcp_send_synack(struct sock *sk)
  * sk: listener socket
  * dst: dst entry attached to the SYNACK
  * req: request_sock pointer
- * rvp: request_values pointer
  *
  * Allocate one skb and build a SYNACK packet.
  * @dst is consumed : Caller should not use it again.
  */
 struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
                                struct request_sock *req,
-                               struct request_values *rvp,
                                struct tcp_fastopen_cookie *foc)
 {
        struct tcp_out_options opts;
-       struct tcp_extend_values *xvp = tcp_xv(rvp);
        struct inet_request_sock *ireq = inet_rsk(req);
        struct tcp_sock *tp = tcp_sk(sk);
-       const struct tcp_cookie_values *cvp = tp->cookie_values;
        struct tcphdr *th;
        struct sk_buff *skb;
        struct tcp_md5sig_key *md5;
        int tcp_header_size;
        int mss;
-       int s_data_desired = 0;
 
-       if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired)
-               s_data_desired = cvp->s_data_desired;
-       skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired,
-                       sk_gfp_atomic(sk, GFP_ATOMIC));
+       skb = alloc_skb(MAX_TCP_HEADER + 15, sk_gfp_atomic(sk, GFP_ATOMIC));
        if (unlikely(!skb)) {
                dst_release(dst);
                return NULL;
@@ -2747,9 +2704,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        else
 #endif
        TCP_SKB_CB(skb)->when = tcp_time_stamp;
-       tcp_header_size = tcp_synack_options(sk, req, mss,
-                                            skb, &opts, &md5, xvp, foc)
-                       + sizeof(*th);
+       tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, &md5,
+                                            foc) + sizeof(*th);
 
        skb_push(skb, tcp_header_size);
        skb_reset_transport_header(skb);
@@ -2767,40 +2723,6 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
                             TCPHDR_SYN | TCPHDR_ACK);
 
-       if (OPTION_COOKIE_EXTENSION & opts.options) {
-               if (s_data_desired) {
-                       u8 *buf = skb_put(skb, s_data_desired);
-
-                       /* copy data directly from the listening socket. */
-                       memcpy(buf, cvp->s_data_payload, s_data_desired);
-                       TCP_SKB_CB(skb)->end_seq += s_data_desired;
-               }
-
-               if (opts.hash_size > 0) {
-                       __u32 workspace[SHA_WORKSPACE_WORDS];
-                       u32 *mess = &xvp->cookie_bakery[COOKIE_DIGEST_WORDS];
-                       u32 *tail = &mess[COOKIE_MESSAGE_WORDS-1];
-
-                       /* Secret recipe depends on the Timestamp, (future)
-                        * Sequence and Acknowledgment Numbers, Initiator
-                        * Cookie, and others handled by IP variant caller.
-                        */
-                       *tail-- ^= opts.tsval;
-                       *tail-- ^= tcp_rsk(req)->rcv_isn + 1;
-                       *tail-- ^= TCP_SKB_CB(skb)->seq + 1;
-
-                       /* recommended */
-                       *tail-- ^= (((__force u32)th->dest << 16) | (__force u32)th->source);
-                       *tail-- ^= (u32)(unsigned long)cvp; /* per sockopt */
-
-                       sha_transform((__u32 *)&xvp->cookie_bakery[0],
-                                     (char *)mess,
-                                     &workspace[0]);
-                       opts.hash_location =
-                               (__u8 *)&xvp->cookie_bakery[0];
-               }
-       }
-
        th->seq = htonl(TCP_SKB_CB(skb)->seq);
        /* XXX data is queued and acked as is. No buffer/window check */
        th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt);
index b78aac30c4986dc78fcd8dae03a527326baa32a3..4b85e6f636c9e66dbb1cc0f4acd197c33cb6cfb6 100644 (file)
@@ -342,10 +342,6 @@ void tcp_retransmit_timer(struct sock *sk)
        struct tcp_sock *tp = tcp_sk(sk);
        struct inet_connection_sock *icsk = inet_csk(sk);
 
-       if (tp->early_retrans_delayed) {
-               tcp_resume_early_retransmit(sk);
-               return;
-       }
        if (tp->fastopen_rsk) {
                WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV &&
                             sk->sk_state != TCP_FIN_WAIT1);
@@ -360,6 +356,8 @@ void tcp_retransmit_timer(struct sock *sk)
 
        WARN_ON(tcp_write_queue_empty(sk));
 
+       tp->tlp_high_seq = 0;
+
        if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
            !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {
                /* Receiver dastardly shrinks window. Our retransmits
@@ -418,11 +416,7 @@ void tcp_retransmit_timer(struct sock *sk)
                NET_INC_STATS_BH(sock_net(sk), mib_idx);
        }
 
-       if (tcp_use_frto(sk)) {
-               tcp_enter_frto(sk);
-       } else {
-               tcp_enter_loss(sk, 0);
-       }
+       tcp_enter_loss(sk, 0);
 
        if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) {
                /* Retransmission failed because of local congestion,
@@ -495,13 +489,20 @@ void tcp_write_timer_handler(struct sock *sk)
        }
 
        event = icsk->icsk_pending;
-       icsk->icsk_pending = 0;
 
        switch (event) {
+       case ICSK_TIME_EARLY_RETRANS:
+               tcp_resume_early_retransmit(sk);
+               break;
+       case ICSK_TIME_LOSS_PROBE:
+               tcp_send_loss_probe(sk);
+               break;
        case ICSK_TIME_RETRANS:
+               icsk->icsk_pending = 0;
                tcp_retransmit_timer(sk);
                break;
        case ICSK_TIME_PROBE0:
+               icsk->icsk_pending = 0;
                tcp_probe_timer(sk);
                break;
        }
index 1b91bf48e277bcc982e8e34e4eca3d67db26f16f..76a1e23259e1fa713bb447486e4e94848e419d41 100644 (file)
@@ -236,7 +236,7 @@ static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event)
                tp->snd_cwnd = tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk);
                break;
 
-       case CA_EVENT_FRTO:
+       case CA_EVENT_LOSS:
                tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk);
                /* Update RTT_min when next ack arrives */
                w->reset_rtt_min = 1;
index 0a073a263720c4eab823db2c09b1b18d7b510bcd..2722db024a0bf772a433cdb192a40e82904afc42 100644 (file)
@@ -902,9 +902,9 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        ipc.addr = inet->inet_saddr;
 
        ipc.oif = sk->sk_bound_dev_if;
-       err = sock_tx_timestamp(sk, &ipc.tx_flags);
-       if (err)
-               return err;
+
+       sock_tx_timestamp(sk, &ipc.tx_flags);
+
        if (msg->msg_controllen) {
                err = ip_cmsg_send(sock_net(sk), msg, &ipc);
                if (err)
@@ -2279,31 +2279,88 @@ void __init udp_init(void)
 
 int udp4_ufo_send_check(struct sk_buff *skb)
 {
-       const struct iphdr *iph;
-       struct udphdr *uh;
-
-       if (!pskb_may_pull(skb, sizeof(*uh)))
+       if (!pskb_may_pull(skb, sizeof(struct udphdr)))
                return -EINVAL;
 
-       iph = ip_hdr(skb);
-       uh = udp_hdr(skb);
+       if (likely(!skb->encapsulation)) {
+               const struct iphdr *iph;
+               struct udphdr *uh;
+
+               iph = ip_hdr(skb);
+               uh = udp_hdr(skb);
 
-       uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
-                                      IPPROTO_UDP, 0);
-       skb->csum_start = skb_transport_header(skb) - skb->head;
-       skb->csum_offset = offsetof(struct udphdr, check);
-       skb->ip_summed = CHECKSUM_PARTIAL;
+               uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
+                               IPPROTO_UDP, 0);
+               skb->csum_start = skb_transport_header(skb) - skb->head;
+               skb->csum_offset = offsetof(struct udphdr, check);
+               skb->ip_summed = CHECKSUM_PARTIAL;
+       }
        return 0;
 }
 
+static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
+               netdev_features_t features)
+{
+       struct sk_buff *segs = ERR_PTR(-EINVAL);
+       int mac_len = skb->mac_len;
+       int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
+       int outer_hlen;
+       netdev_features_t enc_features;
+
+       if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
+               goto out;
+
+       skb->encapsulation = 0;
+       __skb_pull(skb, tnl_hlen);
+       skb_reset_mac_header(skb);
+       skb_set_network_header(skb, skb_inner_network_offset(skb));
+       skb->mac_len = skb_inner_network_offset(skb);
+
+       /* segment inner packet. */
+       enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
+       segs = skb_mac_gso_segment(skb, enc_features);
+       if (!segs || IS_ERR(segs))
+               goto out;
+
+       outer_hlen = skb_tnl_header_len(skb);
+       skb = segs;
+       do {
+               struct udphdr *uh;
+               int udp_offset = outer_hlen - tnl_hlen;
+
+               skb->mac_len = mac_len;
+
+               skb_push(skb, outer_hlen);
+               skb_reset_mac_header(skb);
+               skb_set_network_header(skb, mac_len);
+               skb_set_transport_header(skb, udp_offset);
+               uh = udp_hdr(skb);
+               uh->len = htons(skb->len - udp_offset);
+
+               /* csum segment if tunnel sets skb with csum. */
+               if (unlikely(uh->check)) {
+                       struct iphdr *iph = ip_hdr(skb);
+
+                       uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+                                                      skb->len - udp_offset,
+                                                      IPPROTO_UDP, 0);
+                       uh->check = csum_fold(skb_checksum(skb, udp_offset,
+                                                          skb->len - udp_offset, 0));
+                       if (uh->check == 0)
+                               uh->check = CSUM_MANGLED_0;
+
+               }
+               skb->ip_summed = CHECKSUM_NONE;
+       } while ((skb = skb->next));
+out:
+       return segs;
+}
+
 struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
        netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        unsigned int mss;
-       int offset;
-       __wsum csum;
-
        mss = skb_shinfo(skb)->gso_size;
        if (unlikely(skb->len <= mss))
                goto out;
@@ -2313,6 +2370,7 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
                int type = skb_shinfo(skb)->gso_type;
 
                if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
+                                     SKB_GSO_UDP_TUNNEL |
                                      SKB_GSO_GRE) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
@@ -2323,20 +2381,27 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
                goto out;
        }
 
-       /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
-        * do checksum of UDP packets sent as multiple IP fragments.
-        */
-       offset = skb_checksum_start_offset(skb);
-       csum = skb_checksum(skb, offset, skb->len - offset, 0);
-       offset += skb->csum_offset;
-       *(__sum16 *)(skb->data + offset) = csum_fold(csum);
-       skb->ip_summed = CHECKSUM_NONE;
-
        /* Fragment the skb. IP headers of the fragments are updated in
         * inet_gso_segment()
         */
-       segs = skb_segment(skb, features);
+       if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
+               segs = skb_udp_tunnel_segment(skb, features);
+       else {
+               int offset;
+               __wsum csum;
+
+               /* Do software UFO. Complete and fill in the UDP checksum as
+                * HW cannot do checksum of UDP packets sent as multiple
+                * IP fragments.
+                */
+               offset = skb_checksum_start_offset(skb);
+               csum = skb_checksum(skb, offset, skb->len - offset, 0);
+               offset += skb->csum_offset;
+               *(__sum16 *)(skb->data + offset) = csum_fold(csum);
+               skb->ip_summed = CHECKSUM_NONE;
+
+               segs = skb_segment(skb, features);
+       }
 out:
        return segs;
 }
-
index 505b30ad9182dc83e42e106b4bbaa507dd84f591..7927db0a927951a20b4502d38fca0bba9e94862c 100644 (file)
@@ -25,7 +25,7 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb,
                return 0;
 
        return inet_sk_diag_fill(sk, NULL, skb, req,
-                       sk_user_ns(NETLINK_CB(cb->skb).ssk),
+                       sk_user_ns(NETLINK_CB(cb->skb).sk),
                        NETLINK_CB(cb->skb).portid,
                        cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
 }
@@ -64,14 +64,14 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
                goto out;
 
        err = -ENOMEM;
-       rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) +
-                                    sizeof(struct inet_diag_meminfo) +
-                                    64)), GFP_KERNEL);
+       rep = nlmsg_new(sizeof(struct inet_diag_msg) +
+                       sizeof(struct inet_diag_meminfo) + 64,
+                       GFP_KERNEL);
        if (!rep)
                goto out;
 
        err = inet_sk_diag_fill(sk, NULL, rep, req,
-                          sk_user_ns(NETLINK_CB(in_skb).ssk),
+                          sk_user_ns(NETLINK_CB(in_skb).sk),
                           NETLINK_CB(in_skb).portid,
                           nlh->nlmsg_seq, 0, nlh);
        if (err < 0) {
index fe5189e2e1147d338d96347666d7a98a4ac7b018..eb1dd4d643f2f92d5a2385d0839d06a3424c545c 100644 (file)
@@ -103,8 +103,12 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
 
        top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family);
 
-       /* DS disclosed */
-       top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos,
+       /* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */
+       if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
+               top_iph->tos = 0;
+       else
+               top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
+       top_iph->tos = INET_ECN_encapsulate(top_iph->tos,
                                            XFRM_MODE_SKB_CB(skb)->tos);
 
        flags = x->props.flags;
index ed0b9e2e797a047983a54ff34f73109ae33b20c5..11b13ea69db4e6c35c8b992de9055bf6a312d802 100644 (file)
@@ -156,6 +156,7 @@ config INET6_XFRM_MODE_ROUTEOPTIMIZATION
 config IPV6_SIT
        tristate "IPv6: IPv6-in-IPv4 tunnel (SIT driver)"
        select INET_TUNNEL
+       select NET_IP_TUNNEL
        select IPV6_NDISC_NODETYPE
        default y
        ---help---
@@ -201,6 +202,7 @@ config IPV6_TUNNEL
 config IPV6_GRE
        tristate "IPv6: GRE tunnel"
        select IPV6_TUNNEL
+       select NET_IP_TUNNEL
        ---help---
          Tunneling means encapsulating data of one protocol type within
          another protocol and sending it over a channel that understands the
index dae802c0af7c002df341d3dfe4ed94db6584118e..d1ab6ab29a55c11e0570bb9854c524d9f21369fc 100644 (file)
@@ -70,6 +70,7 @@
 #include <net/snmp.h>
 
 #include <net/af_ieee802154.h>
+#include <net/firewire.h>
 #include <net/ipv6.h>
 #include <net/protocol.h>
 #include <net/ndisc.h>
@@ -419,6 +420,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
                ipv6_regen_rndid((unsigned long) ndev);
        }
 #endif
+       ndev->token = in6addr_any;
 
        if (netif_running(dev) && addrconf_qdisc_ok(dev))
                ndev->if_flags |= IF_READY;
@@ -542,8 +544,7 @@ static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
 };
 
 static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
-                                    struct nlmsghdr *nlh,
-                                    void *arg)
+                                    struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(in_skb->sk);
        struct nlattr *tb[NETCONFA_MAX+1];
@@ -603,6 +604,77 @@ errout:
        return err;
 }
 
+static int inet6_netconf_dump_devconf(struct sk_buff *skb,
+                                     struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       int h, s_h;
+       int idx, s_idx;
+       struct net_device *dev;
+       struct inet6_dev *idev;
+       struct hlist_head *head;
+
+       s_h = cb->args[0];
+       s_idx = idx = cb->args[1];
+
+       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+               idx = 0;
+               head = &net->dev_index_head[h];
+               rcu_read_lock();
+               cb->seq = atomic_read(&net->ipv6.dev_addr_genid) ^
+                         net->dev_base_seq;
+               hlist_for_each_entry_rcu(dev, head, index_hlist) {
+                       if (idx < s_idx)
+                               goto cont;
+                       idev = __in6_dev_get(dev);
+                       if (!idev)
+                               goto cont;
+
+                       if (inet6_netconf_fill_devconf(skb, dev->ifindex,
+                                                      &idev->cnf,
+                                                      NETLINK_CB(cb->skb).portid,
+                                                      cb->nlh->nlmsg_seq,
+                                                      RTM_NEWNETCONF,
+                                                      NLM_F_MULTI,
+                                                      -1) <= 0) {
+                               rcu_read_unlock();
+                               goto done;
+                       }
+                       nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+cont:
+                       idx++;
+               }
+               rcu_read_unlock();
+       }
+       if (h == NETDEV_HASHENTRIES) {
+               if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
+                                              net->ipv6.devconf_all,
+                                              NETLINK_CB(cb->skb).portid,
+                                              cb->nlh->nlmsg_seq,
+                                              RTM_NEWNETCONF, NLM_F_MULTI,
+                                              -1) <= 0)
+                       goto done;
+               else
+                       h++;
+       }
+       if (h == NETDEV_HASHENTRIES + 1) {
+               if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
+                                              net->ipv6.devconf_dflt,
+                                              NETLINK_CB(cb->skb).portid,
+                                              cb->nlh->nlmsg_seq,
+                                              RTM_NEWNETCONF, NLM_F_MULTI,
+                                              -1) <= 0)
+                       goto done;
+               else
+                       h++;
+       }
+done:
+       cb->args[0] = h;
+       cb->args[1] = idx;
+
+       return skb->len;
+}
+
 #ifdef CONFIG_SYSCTL
 static void dev_forward_change(struct inet6_dev *idev)
 {
@@ -804,6 +876,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
        ifa->prefix_len = pfxlen;
        ifa->flags = flags | IFA_F_TENTATIVE;
        ifa->cstamp = ifa->tstamp = jiffies;
+       ifa->tokenized = false;
 
        ifa->rt = rt;
 
@@ -1666,6 +1739,20 @@ static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev)
        return 0;
 }
 
+static int addrconf_ifid_ieee1394(u8 *eui, struct net_device *dev)
+{
+       union fwnet_hwaddr *ha;
+
+       if (dev->addr_len != FWNET_ALEN)
+               return -1;
+
+       ha = (union fwnet_hwaddr *)dev->dev_addr;
+
+       memcpy(eui, &ha->uc.uniq_id, sizeof(ha->uc.uniq_id));
+       eui[0] ^= 2;
+       return 0;
+}
+
 static int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev)
 {
        /* XXX: inherit EUI-64 from other interface -- yoshfuji */
@@ -1730,6 +1817,8 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
                return addrconf_ifid_gre(eui, dev);
        case ARPHRD_IEEE802154:
                return addrconf_ifid_eui64(eui, dev);
+       case ARPHRD_IEEE1394:
+               return addrconf_ifid_ieee1394(eui, dev);
        }
        return -1;
 }
@@ -2044,11 +2133,19 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
                struct inet6_ifaddr *ifp;
                struct in6_addr addr;
                int create = 0, update_lft = 0;
+               bool tokenized = false;
 
                if (pinfo->prefix_len == 64) {
                        memcpy(&addr, &pinfo->prefix, 8);
-                       if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
-                           ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
+
+                       if (!ipv6_addr_any(&in6_dev->token)) {
+                               read_lock_bh(&in6_dev->lock);
+                               memcpy(addr.s6_addr + 8,
+                                      in6_dev->token.s6_addr + 8, 8);
+                               read_unlock_bh(&in6_dev->lock);
+                               tokenized = true;
+                       } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
+                                  ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
                                in6_dev_put(in6_dev);
                                return;
                        }
@@ -2089,6 +2186,7 @@ ok:
 
                        update_lft = create = 1;
                        ifp->cstamp = jiffies;
+                       ifp->tokenized = tokenized;
                        addrconf_dad_start(ifp);
                }
 
@@ -2598,7 +2696,8 @@ 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_IEEE802154) &&
+           (dev->type != ARPHRD_IEEE1394)) {
                /* Alas, we support only Ethernet autoconfiguration. */
                return;
        }
@@ -3535,7 +3634,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
 };
 
 static int
-inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifaddrmsg *ifm;
@@ -3601,7 +3700,7 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
 }
 
 static int
-inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifaddrmsg *ifm;
@@ -3832,6 +3931,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
                                                NLM_F_MULTI);
                        if (err <= 0)
                                break;
+                       nl_dump_check_consistent(cb, nlmsg_hdr(skb));
                }
                break;
        }
@@ -3889,6 +3989,7 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
        s_ip_idx = ip_idx = cb->args[2];
 
        rcu_read_lock();
+       cb->seq = atomic_read(&net->ipv6.dev_addr_genid) ^ net->dev_base_seq;
        for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
                idx = 0;
                head = &net->dev_index_head[h];
@@ -3940,8 +4041,7 @@ static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb)
        return inet6_dump_addr(skb, cb, type);
 }
 
-static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh,
-                            void *arg)
+static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(in_skb->sk);
        struct ifaddrmsg *ifm;
@@ -4074,7 +4174,8 @@ static inline size_t inet6_ifla6_size(void)
             + nla_total_size(sizeof(struct ifla_cacheinfo))
             + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */
             + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */
-            + nla_total_size(ICMP6_MIB_MAX * 8); /* IFLA_INET6_ICMP6STATS */
+            + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */
+            + nla_total_size(sizeof(struct in6_addr)); /* IFLA_INET6_TOKEN */
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -4161,6 +4262,13 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)
                goto nla_put_failure;
        snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla));
 
+       nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr));
+       if (nla == NULL)
+               goto nla_put_failure;
+       read_lock_bh(&idev->lock);
+       memcpy(nla_data(nla), idev->token.s6_addr, nla_len(nla));
+       read_unlock_bh(&idev->lock);
+
        return 0;
 
 nla_put_failure:
@@ -4188,6 +4296,80 @@ static int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
        return 0;
 }
 
+static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
+{
+       struct inet6_ifaddr *ifp;
+       struct net_device *dev = idev->dev;
+       bool update_rs = false;
+
+       if (token == NULL)
+               return -EINVAL;
+       if (ipv6_addr_any(token))
+               return -EINVAL;
+       if (dev->flags & (IFF_LOOPBACK | IFF_NOARP))
+               return -EINVAL;
+       if (!ipv6_accept_ra(idev))
+               return -EINVAL;
+       if (idev->cnf.rtr_solicits <= 0)
+               return -EINVAL;
+
+       write_lock_bh(&idev->lock);
+
+       BUILD_BUG_ON(sizeof(token->s6_addr) != 16);
+       memcpy(idev->token.s6_addr + 8, token->s6_addr + 8, 8);
+
+       write_unlock_bh(&idev->lock);
+
+       if (!idev->dead && (idev->if_flags & IF_READY)) {
+               struct in6_addr ll_addr;
+
+               ipv6_get_lladdr(dev, &ll_addr, IFA_F_TENTATIVE |
+                               IFA_F_OPTIMISTIC);
+
+               /* If we're not ready, then normal ifup will take care
+                * of this. Otherwise, we need to request our rs here.
+                */
+               ndisc_send_rs(dev, &ll_addr, &in6addr_linklocal_allrouters);
+               update_rs = true;
+       }
+
+       write_lock_bh(&idev->lock);
+
+       if (update_rs)
+               idev->if_flags |= IF_RS_SENT;
+
+       /* Well, that's kinda nasty ... */
+       list_for_each_entry(ifp, &idev->addr_list, if_list) {
+               spin_lock(&ifp->lock);
+               if (ifp->tokenized) {
+                       ifp->valid_lft = 0;
+                       ifp->prefered_lft = 0;
+               }
+               spin_unlock(&ifp->lock);
+       }
+
+       write_unlock_bh(&idev->lock);
+       return 0;
+}
+
+static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+       int err = -EINVAL;
+       struct inet6_dev *idev = __in6_dev_get(dev);
+       struct nlattr *tb[IFLA_INET6_MAX + 1];
+
+       if (!idev)
+               return -EAFNOSUPPORT;
+
+       if (nla_parse_nested(tb, IFLA_INET6_MAX, nla, NULL) < 0)
+               BUG();
+
+       if (tb[IFLA_INET6_TOKEN])
+               err = inet6_set_iftoken(idev, nla_data(tb[IFLA_INET6_TOKEN]));
+
+       return err;
+}
+
 static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
                             u32 portid, u32 seq, int event, unsigned int flags)
 {
@@ -4366,6 +4548,8 @@ errout:
 
 static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 {
+       struct net *net = dev_net(ifp->idev->dev);
+
        inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
 
        switch (event) {
@@ -4391,6 +4575,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
                        dst_free(&ifp->rt->dst);
                break;
        }
+       atomic_inc(&net->ipv6.dev_addr_genid);
 }
 
 static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
@@ -4871,6 +5056,7 @@ static struct rtnl_af_ops inet6_ops = {
        .family           = AF_INET6,
        .fill_link_af     = inet6_fill_link_af,
        .get_link_af_size = inet6_get_link_af_size,
+       .set_link_af      = inet6_set_link_af,
 };
 
 /*
@@ -4943,7 +5129,7 @@ int __init addrconf_init(void)
        __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL,
                        inet6_dump_ifacaddr, NULL);
        __rtnl_register(PF_INET6, RTM_GETNETCONF, inet6_netconf_get_devconf,
-                       NULL, NULL);
+                       inet6_netconf_dump_devconf, NULL);
 
        ipv6_addr_label_rtnl_register();
 
index aad64352cb60699d20c3d071fa81f3cc1bf8ee30..f083a583a05cfac719a1df3fd4dc0e58e30f431f 100644 (file)
@@ -414,8 +414,7 @@ static const struct nla_policy ifal_policy[IFAL_MAX+1] = {
        [IFAL_LABEL]            = { .len = sizeof(u32), },
 };
 
-static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh,
-                            void *arg)
+static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifaddrlblmsg *ifal;
@@ -436,10 +435,7 @@ static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh,
 
        if (!tb[IFAL_ADDRESS])
                return -EINVAL;
-
        pfx = nla_data(tb[IFAL_ADDRESS]);
-       if (!pfx)
-               return -EINVAL;
 
        if (!tb[IFAL_LABEL])
                return -EINVAL;
@@ -533,8 +529,7 @@ static inline int ip6addrlbl_msgsize(void)
                + nla_total_size(4);    /* IFAL_LABEL */
 }
 
-static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh,
-                         void *arg)
+static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh)
 {
        struct net *net = sock_net(in_skb->sk);
        struct ifaddrlblmsg *ifal;
@@ -561,10 +556,7 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh,
 
        if (!tb[IFAL_ADDRESS])
                return -EINVAL;
-
        addr = nla_data(tb[IFAL_ADDRESS]);
-       if (!addr)
-               return -EINVAL;
 
        rcu_read_lock();
        p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index);
index 6b793bfc0e10b4fc42a5d7887e613d00e545ee70..ab5c7ad482cded367c1180a3a3761615bc2a4d54 100644 (file)
@@ -49,7 +49,6 @@
 #include <net/udp.h>
 #include <net/udplite.h>
 #include <net/tcp.h>
-#include <net/ipip.h>
 #include <net/protocol.h>
 #include <net/inet_common.h>
 #include <net/route.h>
@@ -323,7 +322,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                        struct net_device *dev = NULL;
 
                        rcu_read_lock();
-                       if (addr_type & IPV6_ADDR_LINKLOCAL) {
+                       if (__ipv6_addr_needs_scope_id(addr_type)) {
                                if (addr_len >= sizeof(struct sockaddr_in6) &&
                                    addr->sin6_scope_id) {
                                        /* Override any existing binding, if another one
@@ -471,8 +470,8 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
 
                sin->sin6_port = inet->inet_sport;
        }
-       if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
-               sin->sin6_scope_id = sk->sk_bound_dev_if;
+       sin->sin6_scope_id = ipv6_iface_scope_id(&sin->sin6_addr,
+                                                sk->sk_bound_dev_if);
        *uaddr_len = sizeof(*sin);
        return 0;
 }
index f5a54782a3408422ab4aa5da733bdbffb8755905..4b56cbbc789062d89c5a708386a03e5a8ded8bc6 100644 (file)
@@ -124,7 +124,7 @@ ipv4_connected:
                goto out;
        }
 
-       if (addr_type&IPV6_ADDR_LINKLOCAL) {
+       if (__ipv6_addr_needs_scope_id(addr_type)) {
                if (addr_len >= sizeof(struct sockaddr_in6) &&
                    usin->sin6_scope_id) {
                        if (sk->sk_bound_dev_if &&
@@ -355,18 +355,19 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
                sin->sin6_family = AF_INET6;
                sin->sin6_flowinfo = 0;
                sin->sin6_port = serr->port;
-               sin->sin6_scope_id = 0;
                if (skb->protocol == htons(ETH_P_IPV6)) {
                        const struct ipv6hdr *ip6h = container_of((struct in6_addr *)(nh + serr->addr_offset),
                                                                  struct ipv6hdr, daddr);
                        sin->sin6_addr = ip6h->daddr;
                        if (np->sndflow)
                                sin->sin6_flowinfo = ip6_flowinfo(ip6h);
-                       if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
-                               sin->sin6_scope_id = IP6CB(skb)->iif;
+                       sin->sin6_scope_id =
+                               ipv6_iface_scope_id(&sin->sin6_addr,
+                                                   IP6CB(skb)->iif);
                } else {
                        ipv6_addr_set_v4mapped(*(__be32 *)(nh + serr->addr_offset),
                                               &sin->sin6_addr);
+                       sin->sin6_scope_id = 0;
                }
        }
 
@@ -376,18 +377,19 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
        if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
                sin->sin6_family = AF_INET6;
                sin->sin6_flowinfo = 0;
-               sin->sin6_scope_id = 0;
                if (skb->protocol == htons(ETH_P_IPV6)) {
                        sin->sin6_addr = ipv6_hdr(skb)->saddr;
                        if (np->rxopt.all)
                                ip6_datagram_recv_ctl(sk, msg, skb);
-                       if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
-                               sin->sin6_scope_id = IP6CB(skb)->iif;
+                       sin->sin6_scope_id =
+                               ipv6_iface_scope_id(&sin->sin6_addr,
+                                                   IP6CB(skb)->iif);
                } else {
                        struct inet_sock *inet = inet_sk(sk);
 
                        ipv6_addr_set_v4mapped(ip_hdr(skb)->saddr,
                                               &sin->sin6_addr);
+                       sin->sin6_scope_id = 0;
                        if (inet->cmsg_flags)
                                ip_cmsg_recv(msg, skb);
                }
@@ -592,7 +594,9 @@ int ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg,
                        sin6.sin6_addr = ipv6_hdr(skb)->daddr;
                        sin6.sin6_port = ports[1];
                        sin6.sin6_flowinfo = 0;
-                       sin6.sin6_scope_id = 0;
+                       sin6.sin6_scope_id =
+                               ipv6_iface_scope_id(&ipv6_hdr(skb)->daddr,
+                                                   opt->iif);
 
                        put_cmsg(msg, SOL_IPV6, IPV6_ORIGDSTADDR, sizeof(sin6), &sin6);
                }
index fff5bdd8b6800d56679957a62e201e84fd846d09..71b900c3f4ffc5a7634deef14716ba30187c2b36 100644 (file)
@@ -434,7 +434,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
         *      Source addr check
         */
 
-       if (addr_type & IPV6_ADDR_LINKLOCAL)
+       if (__ipv6_addr_needs_scope_id(addr_type))
                iif = skb->dev->ifindex;
 
        /*
index 9bfab19ff3c02b301be08555b2c35a7982b2d1b6..e4311cbc8b4ecbf70ea1fb2e2f2415342be382cc 100644 (file)
@@ -54,6 +54,10 @@ int inet6_csk_bind_conflict(const struct sock *sk,
                                if (ipv6_rcv_saddr_equal(sk, sk2))
                                        break;
                        }
+                       if (!relax && reuse && sk2->sk_reuse &&
+                           sk2->sk_state != TCP_LISTEN &&
+                           ipv6_rcv_saddr_equal(sk, sk2))
+                               break;
                }
        }
 
@@ -169,10 +173,8 @@ void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
        sin6->sin6_port = inet_sk(sk)->inet_dport;
        /* We do not store received flowlabel for TCP */
        sin6->sin6_flowinfo = 0;
-       sin6->sin6_scope_id = 0;
-       if (sk->sk_bound_dev_if &&
-           ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
-               sin6->sin6_scope_id = sk->sk_bound_dev_if;
+       sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
+                                                 sk->sk_bound_dev_if);
 }
 
 EXPORT_SYMBOL_GPL(inet6_csk_addr2sockaddr);
index b973ed3d06cf6b3302190b59e69d46c30b1d2ae3..46e88433ec7d229550eecadc29f8eacc139fe5b2 100644 (file)
@@ -144,7 +144,9 @@ static void ip6_fl_gc(unsigned long dummy)
        spin_lock(&ip6_fl_lock);
 
        for (i=0; i<=FL_HASH_MASK; i++) {
-               struct ip6_flowlabel *fl, **flp;
+               struct ip6_flowlabel *fl;
+               struct ip6_flowlabel __rcu **flp;
+
                flp = &fl_ht[i];
                while ((fl = rcu_dereference_protected(*flp,
                                                       lockdep_is_held(&ip6_fl_lock))) != NULL) {
@@ -179,7 +181,9 @@ static void __net_exit ip6_fl_purge(struct net *net)
 
        spin_lock(&ip6_fl_lock);
        for (i = 0; i <= FL_HASH_MASK; i++) {
-               struct ip6_flowlabel *fl, **flp;
+               struct ip6_flowlabel *fl;
+               struct ip6_flowlabel __rcu **flp;
+
                flp = &fl_ht[i];
                while ((fl = rcu_dereference_protected(*flp,
                                                       lockdep_is_held(&ip6_fl_lock))) != NULL) {
@@ -506,7 +510,8 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct in6_flowlabel_req freq;
        struct ipv6_fl_socklist *sfl1=NULL;
-       struct ipv6_fl_socklist *sfl, **sflp;
+       struct ipv6_fl_socklist *sfl;
+       struct ipv6_fl_socklist __rcu **sflp;
        struct ip6_flowlabel *fl, *fl1 = NULL;
 
 
index e4efffe2522e0173a6ec96100f6cac3d34a61d1e..d3ddd840035450570d634fb22002496a447d5d34 100644 (file)
@@ -38,6 +38,7 @@
 
 #include <net/sock.h>
 #include <net/ip.h>
+#include <net/ip_tunnels.h>
 #include <net/icmp.h>
 #include <net/protocol.h>
 #include <net/addrconf.h>
@@ -110,46 +111,6 @@ static u32 HASH_ADDR(const struct in6_addr *addr)
 #define tunnels_l      tunnels[1]
 #define tunnels_wc     tunnels[0]
 
-static struct rtnl_link_stats64 *ip6gre_get_stats64(struct net_device *dev,
-               struct rtnl_link_stats64 *tot)
-{
-       int i;
-
-       for_each_possible_cpu(i) {
-               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
-               u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
-               unsigned int start;
-
-               do {
-                       start = u64_stats_fetch_begin_bh(&tstats->syncp);
-                       rx_packets = tstats->rx_packets;
-                       tx_packets = tstats->tx_packets;
-                       rx_bytes = tstats->rx_bytes;
-                       tx_bytes = tstats->tx_bytes;
-               } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
-
-               tot->rx_packets += rx_packets;
-               tot->tx_packets += tx_packets;
-               tot->rx_bytes   += rx_bytes;
-               tot->tx_bytes   += tx_bytes;
-       }
-
-       tot->multicast = dev->stats.multicast;
-       tot->rx_crc_errors = dev->stats.rx_crc_errors;
-       tot->rx_fifo_errors = dev->stats.rx_fifo_errors;
-       tot->rx_length_errors = dev->stats.rx_length_errors;
-       tot->rx_frame_errors = dev->stats.rx_frame_errors;
-       tot->rx_errors = dev->stats.rx_errors;
-
-       tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
-       tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
-       tot->tx_dropped = dev->stats.tx_dropped;
-       tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
-       tot->tx_errors = dev->stats.tx_errors;
-
-       return tot;
-}
-
 /* Given src, dst and key, find appropriate for input tunnel. */
 
 static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
@@ -667,7 +628,6 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
        struct net_device_stats *stats = &tunnel->dev->stats;
        int err = -1;
        u8 proto;
-       int pkt_len;
        struct sk_buff *new_skb;
 
        if (dev->type == ARPHRD_ETHER)
@@ -801,23 +761,9 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
                }
        }
 
-       nf_reset(skb);
-       pkt_len = skb->len;
-       err = ip6_local_out(skb);
-
-       if (net_xmit_eval(err) == 0) {
-               struct pcpu_tstats *tstats = this_cpu_ptr(tunnel->dev->tstats);
-
-               tstats->tx_bytes += pkt_len;
-               tstats->tx_packets++;
-       } else {
-               stats->tx_errors++;
-               stats->tx_aborted_errors++;
-       }
-
+       ip6tunnel_xmit(skb, dev);
        if (ndst)
                ip6_tnl_dst_store(tunnel, ndst);
-
        return 0;
 tx_err_link_failure:
        stats->tx_carrier_errors++;
@@ -1271,7 +1217,7 @@ static const struct net_device_ops ip6gre_netdev_ops = {
        .ndo_start_xmit         = ip6gre_tunnel_xmit,
        .ndo_do_ioctl           = ip6gre_tunnel_ioctl,
        .ndo_change_mtu         = ip6gre_tunnel_change_mtu,
-       .ndo_get_stats64        = ip6gre_get_stats64,
+       .ndo_get_stats64        = ip_tunnel_get_stats64,
 };
 
 static void ip6gre_dev_free(struct net_device *dev)
@@ -1520,7 +1466,7 @@ static const struct net_device_ops ip6gre_tap_netdev_ops = {
        .ndo_set_mac_address = eth_mac_addr,
        .ndo_validate_addr = eth_validate_addr,
        .ndo_change_mtu = ip6gre_tunnel_change_mtu,
-       .ndo_get_stats64 = ip6gre_get_stats64,
+       .ndo_get_stats64 = ip_tunnel_get_stats64,
 };
 
 static void ip6gre_tap_setup(struct net_device *dev)
index 8234c1dcdf7286f23c32bc150279cffaeb818ef4..71b766ee821d64fd10e99482b962ca6cea07cdad 100644 (file)
@@ -92,14 +92,12 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
        u8 *prevhdr;
        int offset = 0;
 
-       if (!(features & NETIF_F_V6_CSUM))
-               features &= ~NETIF_F_SG;
-
        if (unlikely(skb_shinfo(skb)->gso_type &
                     ~(SKB_GSO_UDP |
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_UDP_TUNNEL |
                       SKB_GSO_TCPV6 |
                       0)))
                goto out;
index 155eccfa77604fcd63bbeeff1db23ed184082dd6..d2eedf192330caf5a963c27ddaceffae5e4886b8 100644 (file)
@@ -1224,11 +1224,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        }
 
        /* For UDP, check if TX timestamp is enabled */
-       if (sk->sk_type == SOCK_DGRAM) {
-               err = sock_tx_timestamp(sk, &tx_flags);
-               if (err)
-                       goto error;
-       }
+       if (sk->sk_type == SOCK_DGRAM)
+               sock_tx_timestamp(sk, &tx_flags);
 
        /*
         * Let's try using as much space as possible.
index fff83cbc197f1cc22149698d0b8f54fea0d107e8..1e55866cead7425eefdff36c1ddca1aab9504286 100644 (file)
@@ -47,6 +47,7 @@
 
 #include <net/icmp.h>
 #include <net/ip.h>
+#include <net/ip_tunnels.h>
 #include <net/ipv6.h>
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
@@ -955,7 +956,6 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
        unsigned int max_headroom = sizeof(struct ipv6hdr);
        u8 proto;
        int err = -1;
-       int pkt_len;
 
        if (!fl6->flowi6_mark)
                dst = ip6_tnl_dst_check(t);
@@ -1035,19 +1035,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
        ipv6h->nexthdr = proto;
        ipv6h->saddr = fl6->saddr;
        ipv6h->daddr = fl6->daddr;
-       nf_reset(skb);
-       pkt_len = skb->len;
-       err = ip6_local_out(skb);
-
-       if (net_xmit_eval(err) == 0) {
-               struct pcpu_tstats *tstats = this_cpu_ptr(t->dev->tstats);
-
-               tstats->tx_bytes += pkt_len;
-               tstats->tx_packets++;
-       } else {
-               stats->tx_errors++;
-               stats->tx_aborted_errors++;
-       }
+       ip6tunnel_xmit(skb, dev);
        if (ndst)
                ip6_tnl_dst_store(t, ndst);
        return 0;
index 96bfb4e4b820e29e84dcaf49d3104a17c8800788..241fb8ad9fcf28e2982f32e23ff9b7403ceef24a 100644 (file)
@@ -842,9 +842,9 @@ static void ip6mr_destroy_unres(struct mr6_table *mrt, struct mfc6_cache *c)
                if (ipv6_hdr(skb)->version == 0) {
                        struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct ipv6hdr));
                        nlh->nlmsg_type = NLMSG_ERROR;
-                       nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
+                       nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
                        skb_trim(skb, nlh->nlmsg_len);
-                       ((struct nlmsgerr *)NLMSG_DATA(nlh))->error = -ETIMEDOUT;
+                       ((struct nlmsgerr *)nlmsg_data(nlh))->error = -ETIMEDOUT;
                        rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
                } else
                        kfree_skb(skb);
@@ -1100,13 +1100,13 @@ static void ip6mr_cache_resolve(struct net *net, struct mr6_table *mrt,
                if (ipv6_hdr(skb)->version == 0) {
                        struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct ipv6hdr));
 
-                       if (__ip6mr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
+                       if (__ip6mr_fill_mroute(mrt, skb, c, nlmsg_data(nlh)) > 0) {
                                nlh->nlmsg_len = skb_tail_pointer(skb) - (u8 *)nlh;
                        } else {
                                nlh->nlmsg_type = NLMSG_ERROR;
-                               nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
+                               nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
                                skb_trim(skb, nlh->nlmsg_len);
-                               ((struct nlmsgerr *)NLMSG_DATA(nlh))->error = -EMSGSIZE;
+                               ((struct nlmsgerr *)nlmsg_data(nlh))->error = -EMSGSIZE;
                        }
                        rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
                } else
index 76ef4353d51872c1688b52531eadf1f4f446a8a4..2712ab22a174087c09cc705e1f6adec2bd601154 100644 (file)
@@ -610,8 +610,6 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
                }
        }
 #endif
-       if (!dev->addr_len)
-               send_sllao = 0;
        if (send_sllao)
                optlen += ndisc_opt_addr_space(dev);
 
index 429089cb073dd1ae4f565a6b5382d6b12b9354ce..72836f40b73075430f4ef76e0fb7d20c32b7401c 100644 (file)
@@ -1,3 +1,9 @@
+/*
+ * IPv6 specific functions of netfilter core
+ *
+ * Rusty Russell (C) 2000 -- This code is GPL.
+ * Patrick McHardy (C) 2006-2012
+ */
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/ipv6.h>
@@ -29,7 +35,7 @@ int ip6_route_me_harder(struct sk_buff *skb)
                IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
                LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n");
                dst_release(dst);
-               return -EINVAL;
+               return dst->error;
        }
 
        /* Drop old route. */
@@ -43,7 +49,7 @@ int ip6_route_me_harder(struct sk_buff *skb)
                skb_dst_set(skb, NULL);
                dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), skb->sk, 0);
                if (IS_ERR(dst))
-                       return -1;
+                       return PTR_ERR(dst);
                skb_dst_set(skb, dst);
        }
 #endif
@@ -53,7 +59,7 @@ int ip6_route_me_harder(struct sk_buff *skb)
        if (skb_headroom(skb) < hh_len &&
            pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
                             0, GFP_ATOMIC))
-               return -1;
+               return -ENOMEM;
 
        return 0;
 }
index c72532a60d887aeaaf31187f470622de24aabda8..4433ab40e7de05a2b518e20dacfa370d736bded3 100644 (file)
@@ -105,7 +105,7 @@ config IP6_NF_MATCH_MH
 
 config IP6_NF_MATCH_RPFILTER
        tristate '"rpfilter" reverse path filter match support'
-       depends on NETFILTER_ADVANCED
+       depends on NETFILTER_ADVANCED && (IP6_NF_MANGLE || IP6_NF_RAW)
        ---help---
          This option allows you to match packets whose replies would
          go out via the interface the packet came in.
index 341b54ade72cd045472e5957c33370c84d3338b0..44400c216dc6361e4607fe9af3e2999639766a11 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
  * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
+ * Copyright (c) 2006-2010 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -284,6 +285,7 @@ static void trace_packet(const struct sk_buff *skb,
        const char *hookname, *chainname, *comment;
        const struct ip6t_entry *iter;
        unsigned int rulenum = 0;
+       struct net *net = dev_net(in ? in : out);
 
        table_base = private->entries[smp_processor_id()];
        root = get_entry(table_base, private->hook_entry[hook]);
@@ -296,7 +298,7 @@ static void trace_packet(const struct sk_buff *skb,
                    &chainname, &comment, &rulenum) != 0)
                        break;
 
-       nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
+       nf_log_packet(net, AF_INET6, hook, skb, in, out, &trace_loginfo,
                      "TRACE: %s:%s:%s:%u ",
                      tablename, chainname, comment, rulenum);
 }
index cb631143721c0afb13152121c74a9d49f51e99dc..590f767db5d4df887c201597ee22f395d8a6869b 100644 (file)
@@ -18,9 +18,8 @@
 static int ip6t_npt_checkentry(const struct xt_tgchk_param *par)
 {
        struct ip6t_npt_tginfo *npt = par->targinfo;
-       __wsum src_sum = 0, dst_sum = 0;
        struct in6_addr pfx;
-       unsigned int i;
+       __wsum src_sum, dst_sum;
 
        if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64)
                return -EINVAL;
@@ -33,12 +32,8 @@ static int ip6t_npt_checkentry(const struct xt_tgchk_param *par)
        if (!ipv6_addr_equal(&pfx, &npt->dst_pfx.in6))
                return -EINVAL;
 
-       for (i = 0; i < ARRAY_SIZE(npt->src_pfx.in6.s6_addr16); i++) {
-               src_sum = csum_add(src_sum,
-                               (__force __wsum)npt->src_pfx.in6.s6_addr16[i]);
-               dst_sum = csum_add(dst_sum,
-                               (__force __wsum)npt->dst_pfx.in6.s6_addr16[i]);
-       }
+       src_sum = csum_partial(&npt->src_pfx.in6, sizeof(npt->src_pfx.in6), 0);
+       dst_sum = csum_partial(&npt->dst_pfx.in6, sizeof(npt->dst_pfx.in6), 0);
 
        npt->adjustment = ~csum_fold(csum_sub(src_sum, dst_sum));
        return 0;
index ed3b427b284142c037d66d63ee3fa7e205925708..70f9abc0efe9a0ba0bc8fdbb6060f3bfe593291f 100644 (file)
@@ -7,6 +7,8 @@
  * Authors:
  *     Yasuyuki Kozakai        <yasuyuki.kozakai@toshiba.co.jp>
  *
+ * Copyright (c) 2005-2007 Patrick McHardy <kaber@trash.net>
+ *
  * Based on net/ipv4/netfilter/ipt_REJECT.c
  *
  * This program is free software; you can redistribute it and/or
index 6134a1ebfb1b001273e3a034a7cb9d47400b72ee..e075399d8b72490e74e9a8f2a70da6409dd858ee 100644 (file)
@@ -38,7 +38,7 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out)
        struct in6_addr saddr, daddr;
        u_int8_t hop_limit;
        u_int32_t flowlabel, mark;
-
+       int err;
 #if 0
        /* root is playing with raw sockets. */
        if (skb->len < sizeof(struct iphdr) ||
@@ -65,8 +65,11 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out)
             !ipv6_addr_equal(&ipv6_hdr(skb)->daddr, &daddr) ||
             skb->mark != mark ||
             ipv6_hdr(skb)->hop_limit != hop_limit ||
-            flowlabel != *((u_int32_t *)ipv6_hdr(skb))))
-               return ip6_route_me_harder(skb) == 0 ? ret : NF_DROP;
+            flowlabel != *((u_int32_t *)ipv6_hdr(skb)))) {
+               err = ip6_route_me_harder(skb);
+               if (err < 0)
+                       ret = NF_DROP_ERR(err);
+       }
 
        return ret;
 }
index e0e788d25b1431624ae9470f0af904803fe5d92a..6383f90efda82c23ad04ef05be9deec70bd8c08f 100644 (file)
@@ -179,6 +179,7 @@ nf_nat_ipv6_out(unsigned int hooknum,
 #ifdef CONFIG_XFRM
        const struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
+       int err;
 #endif
        unsigned int ret;
 
@@ -197,9 +198,11 @@ nf_nat_ipv6_out(unsigned int hooknum,
                                      &ct->tuplehash[!dir].tuple.dst.u3) ||
                    (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
                     ct->tuplehash[dir].tuple.src.u.all !=
-                    ct->tuplehash[!dir].tuple.dst.u.all))
-                       if (nf_xfrm_me_harder(skb, AF_INET6) < 0)
-                               ret = NF_DROP;
+                    ct->tuplehash[!dir].tuple.dst.u.all)) {
+                       err = nf_xfrm_me_harder(skb, AF_INET6);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
+               }
        }
 #endif
        return ret;
@@ -215,6 +218,7 @@ nf_nat_ipv6_local_fn(unsigned int hooknum,
        const struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
        unsigned int ret;
+       int err;
 
        /* root is playing with raw sockets. */
        if (skb->len < sizeof(struct ipv6hdr))
@@ -227,16 +231,19 @@ nf_nat_ipv6_local_fn(unsigned int hooknum,
 
                if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
                                      &ct->tuplehash[!dir].tuple.src.u3)) {
-                       if (ip6_route_me_harder(skb))
-                               ret = NF_DROP;
+                       err = ip6_route_me_harder(skb);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
                }
 #ifdef CONFIG_XFRM
                else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
                         ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
                         ct->tuplehash[dir].tuple.dst.u.all !=
-                        ct->tuplehash[!dir].tuple.src.u.all)
-                       if (nf_xfrm_me_harder(skb, AF_INET6))
-                               ret = NF_DROP;
+                        ct->tuplehash[!dir].tuple.src.u.all) {
+                       err = nf_xfrm_me_harder(skb, AF_INET6);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
+               }
 #endif
        }
        return ret;
index 2b6c226f5198e42e60f7cdcd5b9ae3e10d50b831..97bcf2bae8574a1a79d5410e18826f78e2ecc5dd 100644 (file)
@@ -330,12 +330,8 @@ ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len)
                                        sizeof(sin6.sin6_addr));
 
        nf_ct_put(ct);
-
-       if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
-               sin6.sin6_scope_id = sk->sk_bound_dev_if;
-       else
-               sin6.sin6_scope_id = 0;
-
+       sin6.sin6_scope_id = ipv6_iface_scope_id(&sin6.sin6_addr,
+                                                sk->sk_bound_dev_if);
        return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0;
 }
 
index 24df3dde0076bc58d6081f306832c73d568d72a9..b3807c5cb888736f2d7d46d332722ead3e5a6801 100644 (file)
@@ -131,7 +131,8 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb,
                         type + 128);
                nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple);
                if (LOG_INVALID(nf_ct_net(ct), IPPROTO_ICMPV6))
-                       nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(nf_ct_net(ct), PF_INET6, 0, skb, NULL,
+                                     NULL, NULL,
                                      "nf_ct_icmpv6: invalid new with type %d ",
                                      type + 128);
                return false;
@@ -203,7 +204,7 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl,
        icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih);
        if (icmp6h == NULL) {
                if (LOG_INVALID(net, IPPROTO_ICMPV6))
-               nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL,
                              "nf_ct_icmpv6: short packet ");
                return -NF_ACCEPT;
        }
@@ -211,7 +212,7 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl,
        if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
            nf_ip6_checksum(skb, hooknum, dataoff, IPPROTO_ICMPV6)) {
                if (LOG_INVALID(net, IPPROTO_ICMPV6))
-                       nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_icmpv6: ICMPv6 checksum failed ");
                return -NF_ACCEPT;
        }
index 6700069949ddc81b13f7f0c2bc2622258b6fb2a4..dffdc1a389c57da1d16ebeda9b9aff44d6cd961a 100644 (file)
@@ -41,6 +41,7 @@
 #include <net/rawv6.h>
 #include <net/ndisc.h>
 #include <net/addrconf.h>
+#include <net/inet_ecn.h>
 #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
 #include <linux/sysctl.h>
 #include <linux/netfilter.h>
@@ -138,6 +139,11 @@ static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
 }
 #endif
 
+static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
+{
+       return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
+}
+
 static unsigned int nf_hashfn(struct inet_frag_queue *q)
 {
        const struct frag_queue *nq;
@@ -166,7 +172,7 @@ static void nf_ct_frag6_expire(unsigned long data)
 /* Creation primitives. */
 static inline struct frag_queue *fq_find(struct net *net, __be32 id,
                                         u32 user, struct in6_addr *src,
-                                        struct in6_addr *dst)
+                                        struct in6_addr *dst, u8 ecn)
 {
        struct inet_frag_queue *q;
        struct ip6_create_arg arg;
@@ -176,6 +182,7 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id,
        arg.user = user;
        arg.src = src;
        arg.dst = dst;
+       arg.ecn = ecn;
 
        read_lock_bh(&nf_frags.lock);
        hash = inet6_hash_frag(id, src, dst, nf_frags.rnd);
@@ -196,6 +203,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
        struct sk_buff *prev, *next;
        unsigned int payload_len;
        int offset, end;
+       u8 ecn;
 
        if (fq->q.last_in & INET_FRAG_COMPLETE) {
                pr_debug("Already completed\n");
@@ -213,6 +221,8 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
                return -1;
        }
 
+       ecn = ip6_frag_ecn(ipv6_hdr(skb));
+
        if (skb->ip_summed == CHECKSUM_COMPLETE) {
                const unsigned char *nh = skb_network_header(skb);
                skb->csum = csum_sub(skb->csum,
@@ -317,6 +327,7 @@ found:
        }
        fq->q.stamp = skb->tstamp;
        fq->q.meat += skb->len;
+       fq->ecn |= ecn;
        if (payload_len > fq->q.max_size)
                fq->q.max_size = payload_len;
        add_frag_mem_limit(&fq->q, skb->truesize);
@@ -352,12 +363,17 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
 {
        struct sk_buff *fp, *op, *head = fq->q.fragments;
        int    payload_len;
+       u8 ecn;
 
        inet_frag_kill(&fq->q, &nf_frags);
 
        WARN_ON(head == NULL);
        WARN_ON(NFCT_FRAG6_CB(head)->offset != 0);
 
+       ecn = ip_frag_ecn_table[fq->ecn];
+       if (unlikely(ecn == 0xff))
+               goto out_fail;
+
        /* Unfragmented part is taken from the first segment. */
        payload_len = ((head->data - skb_network_header(head)) -
                       sizeof(struct ipv6hdr) + fq->q.len -
@@ -428,6 +444,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
        head->dev = dev;
        head->tstamp = fq->q.stamp;
        ipv6_hdr(head)->payload_len = htons(payload_len);
+       ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
        IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;
 
        /* Yes, and fold redundant checksum back. 8) */
@@ -572,7 +589,8 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user)
        inet_frag_evictor(&net->nf_frag.frags, &nf_frags, false);
        local_bh_enable();
 
-       fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr);
+       fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr,
+                    ip6_frag_ecn(hdr));
        if (fq == NULL) {
                pr_debug("Can't find and can't create new queue\n");
                goto ret_orig;
index 330b5e7b7df6f705f1bcf2c66880b1e3de374df3..eedff8ccded507cc977bd073dbbf334b2624033b 100644 (file)
@@ -263,7 +263,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        if (addr_type != IPV6_ADDR_ANY) {
                struct net_device *dev = NULL;
 
-               if (addr_type & IPV6_ADDR_LINKLOCAL) {
+               if (__ipv6_addr_needs_scope_id(addr_type)) {
                        if (addr_len >= sizeof(struct sockaddr_in6) &&
                            addr->sin6_scope_id) {
                                /* Override any existing binding, if another
@@ -498,9 +498,8 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
                sin6->sin6_port = 0;
                sin6->sin6_addr = ipv6_hdr(skb)->saddr;
                sin6->sin6_flowinfo = 0;
-               sin6->sin6_scope_id = 0;
-               if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
-                       sin6->sin6_scope_id = IP6CB(skb)->iif;
+               sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
+                                                         IP6CB(skb)->iif);
        }
 
        sock_recv_ts_and_drops(msg, sk, skb);
@@ -802,7 +801,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 
                if (addr_len >= sizeof(struct sockaddr_in6) &&
                    sin6->sin6_scope_id &&
-                   ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
+                   __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr)))
                        fl6.flowi6_oif = sin6->sin6_scope_id;
        } else {
                if (sk->sk_state != TCP_ESTABLISHED)
index 0ba10e53a6298f07cb64193681ccbfb419d40f36..790d9f4b8b0b21c1d4dd4577ee6a472bd96fd729 100644 (file)
@@ -58,6 +58,7 @@
 #include <net/ndisc.h>
 #include <net/addrconf.h>
 #include <net/inet_frag.h>
+#include <net/inet_ecn.h>
 
 struct ip6frag_skb_cb
 {
@@ -67,6 +68,10 @@ struct ip6frag_skb_cb
 
 #define FRAG6_CB(skb)  ((struct ip6frag_skb_cb*)((skb)->cb))
 
+static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
+{
+       return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
+}
 
 static struct inet_frags ip6_frags;
 
@@ -119,6 +124,7 @@ void ip6_frag_init(struct inet_frag_queue *q, void *a)
        fq->user = arg->user;
        fq->saddr = *arg->src;
        fq->daddr = *arg->dst;
+       fq->ecn = arg->ecn;
 }
 EXPORT_SYMBOL(ip6_frag_init);
 
@@ -173,7 +179,8 @@ static void ip6_frag_expire(unsigned long data)
 }
 
 static __inline__ struct frag_queue *
-fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6_addr *dst)
+fq_find(struct net *net, __be32 id, const struct in6_addr *src,
+       const struct in6_addr *dst, u8 ecn)
 {
        struct inet_frag_queue *q;
        struct ip6_create_arg arg;
@@ -183,6 +190,7 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6
        arg.user = IP6_DEFRAG_LOCAL_DELIVER;
        arg.src = src;
        arg.dst = dst;
+       arg.ecn = ecn;
 
        read_lock(&ip6_frags.lock);
        hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);
@@ -202,6 +210,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
        struct net_device *dev;
        int offset, end;
        struct net *net = dev_net(skb_dst(skb)->dev);
+       u8 ecn;
 
        if (fq->q.last_in & INET_FRAG_COMPLETE)
                goto err;
@@ -219,6 +228,8 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
                return -1;
        }
 
+       ecn = ip6_frag_ecn(ipv6_hdr(skb));
+
        if (skb->ip_summed == CHECKSUM_COMPLETE) {
                const unsigned char *nh = skb_network_header(skb);
                skb->csum = csum_sub(skb->csum,
@@ -319,6 +330,7 @@ found:
        }
        fq->q.stamp = skb->tstamp;
        fq->q.meat += skb->len;
+       fq->ecn |= ecn;
        add_frag_mem_limit(&fq->q, skb->truesize);
 
        /* The first fragment.
@@ -370,9 +382,14 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
        int    payload_len;
        unsigned int nhoff;
        int sum_truesize;
+       u8 ecn;
 
        inet_frag_kill(&fq->q, &ip6_frags);
 
+       ecn = ip_frag_ecn_table[fq->ecn];
+       if (unlikely(ecn == 0xff))
+               goto out_fail;
+
        /* Make the one we just received the head. */
        if (prev) {
                head = prev->next;
@@ -471,6 +488,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
        head->dev = dev;
        head->tstamp = fq->q.stamp;
        ipv6_hdr(head)->payload_len = htons(payload_len);
+       ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
        IP6CB(head)->nhoff = nhoff;
 
        /* Yes, and fold redundant checksum back. 8) */
@@ -534,7 +552,8 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
                IP6_ADD_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
                                 IPSTATS_MIB_REASMFAILS, evicted);
 
-       fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr);
+       fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr,
+                    ip6_frag_ecn(hdr));
        if (fq != NULL) {
                int ret;
 
index e5fe0041adfa389388060bffae7055db3508c407..ad0aa6b0b86ae02f80b6b2184588605a3d5d7a6c 100644 (file)
@@ -2355,7 +2355,7 @@ beginning:
        return last_err;
 }
 
-static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh)
 {
        struct fib6_config cfg;
        int err;
@@ -2370,7 +2370,7 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *a
                return ip6_route_del(&cfg);
 }
 
-static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh)
 {
        struct fib6_config cfg;
        int err;
@@ -2562,7 +2562,7 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
                     prefix, 0, NLM_F_MULTI);
 }
 
-static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh)
 {
        struct net *net = sock_net(in_skb->sk);
        struct nlattr *tb[RTA_MAX+1];
index 02f96dcbcf02a15639fdc359a5058a2650029454..335363478bbfa037e5a4ddc8c3020cc4eeae0a15 100644 (file)
@@ -49,7 +49,7 @@
 #include <net/ip.h>
 #include <net/udp.h>
 #include <net/icmp.h>
-#include <net/ipip.h>
+#include <net/ip_tunnels.h>
 #include <net/inet_ecn.h>
 #include <net/xfrm.h>
 #include <net/dsfield.h>
@@ -87,41 +87,6 @@ struct sit_net {
        struct net_device *fb_tunnel_dev;
 };
 
-static struct rtnl_link_stats64 *ipip6_get_stats64(struct net_device *dev,
-                                                  struct rtnl_link_stats64 *tot)
-{
-       int i;
-
-       for_each_possible_cpu(i) {
-               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
-               u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
-               unsigned int start;
-
-               do {
-                       start = u64_stats_fetch_begin_bh(&tstats->syncp);
-                       rx_packets = tstats->rx_packets;
-                       tx_packets = tstats->tx_packets;
-                       rx_bytes = tstats->rx_bytes;
-                       tx_bytes = tstats->tx_bytes;
-               } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
-
-               tot->rx_packets += rx_packets;
-               tot->tx_packets += tx_packets;
-               tot->rx_bytes   += rx_bytes;
-               tot->tx_bytes   += tx_bytes;
-       }
-
-       tot->rx_errors = dev->stats.rx_errors;
-       tot->rx_frame_errors = dev->stats.rx_frame_errors;
-       tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
-       tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
-       tot->tx_dropped = dev->stats.tx_dropped;
-       tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
-       tot->tx_errors = dev->stats.tx_errors;
-
-       return tot;
-}
-
 /*
  * Must be invoked with rcu_read_lock
  */
@@ -899,6 +864,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
        if ((iph->ttl = tiph->ttl) == 0)
                iph->ttl        =       iph6->hop_limit;
 
+       skb->ip_summed = CHECKSUM_NONE;
+       ip_select_ident(iph, skb_dst(skb), NULL);
        iptunnel_xmit(skb, dev);
        return NETDEV_TX_OK;
 
@@ -1200,7 +1167,7 @@ static const struct net_device_ops ipip6_netdev_ops = {
        .ndo_start_xmit = ipip6_tunnel_xmit,
        .ndo_do_ioctl   = ipip6_tunnel_ioctl,
        .ndo_change_mtu = ipip6_tunnel_change_mtu,
-       .ndo_get_stats64= ipip6_get_stats64,
+       .ndo_get_stats64 = ip_tunnel_get_stats64,
 };
 
 static void ipip6_dev_free(struct net_device *dev)
index 8a0848b60b3539babdf245f4cf256761cfb704cd..d5dda20bd717404a07d4dd45567b5bba23997250 100644 (file)
@@ -149,7 +149,6 @@ static inline int cookie_check(const struct sk_buff *skb, __u32 cookie)
 struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_options_received tcp_opt;
-       const u8 *hash_location;
        struct inet_request_sock *ireq;
        struct inet6_request_sock *ireq6;
        struct tcp_request_sock *treq;
@@ -177,7 +176,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
 
        /* check for timestamp cookie support */
        memset(&tcp_opt, 0, sizeof(tcp_opt));
-       tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL);
+       tcp_parse_options(skb, &tcp_opt, 0, NULL);
 
        if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok))
                goto out;
index 46a5be85be87ebed43ec8b01d6b63263df556279..e51bd1a58264eb9a98d5ed863967ee799a802b44 100644 (file)
@@ -462,7 +462,6 @@ out:
 static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
                              struct flowi6 *fl6,
                              struct request_sock *req,
-                             struct request_values *rvp,
                              u16 queue_mapping)
 {
        struct inet6_request_sock *treq = inet6_rsk(req);
@@ -474,7 +473,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
        if (!dst && (dst = inet6_csk_route_req(sk, fl6, req)) == NULL)
                goto done;
 
-       skb = tcp_make_synack(sk, dst, req, rvp, NULL);
+       skb = tcp_make_synack(sk, dst, req, NULL);
 
        if (skb) {
                __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr);
@@ -489,13 +488,12 @@ done:
        return err;
 }
 
-static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req,
-                            struct request_values *rvp)
+static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req)
 {
        struct flowi6 fl6;
        int res;
 
-       res = tcp_v6_send_synack(sk, NULL, &fl6, req, rvp, 0);
+       res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0);
        if (!res)
                TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
        return res;
@@ -948,9 +946,7 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
  */
 static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
-       struct tcp_extend_values tmp_ext;
        struct tcp_options_received tmp_opt;
-       const u8 *hash_location;
        struct request_sock *req;
        struct inet6_request_sock *treq;
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -988,50 +984,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_clear_options(&tmp_opt);
        tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
        tmp_opt.user_mss = tp->rx_opt.user_mss;
-       tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL);
-
-       if (tmp_opt.cookie_plus > 0 &&
-           tmp_opt.saw_tstamp &&
-           !tp->rx_opt.cookie_out_never &&
-           (sysctl_tcp_cookie_size > 0 ||
-            (tp->cookie_values != NULL &&
-             tp->cookie_values->cookie_desired > 0))) {
-               u8 *c;
-               u32 *d;
-               u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS];
-               int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE;
-
-               if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0)
-                       goto drop_and_free;
-
-               /* Secret recipe starts with IP addresses */
-               d = (__force u32 *)&ipv6_hdr(skb)->daddr.s6_addr32[0];
-               *mess++ ^= *d++;
-               *mess++ ^= *d++;
-               *mess++ ^= *d++;
-               *mess++ ^= *d++;
-               d = (__force u32 *)&ipv6_hdr(skb)->saddr.s6_addr32[0];
-               *mess++ ^= *d++;
-               *mess++ ^= *d++;
-               *mess++ ^= *d++;
-               *mess++ ^= *d++;
-
-               /* plus variable length Initiator Cookie */
-               c = (u8 *)mess;
-               while (l-- > 0)
-                       *c++ ^= *hash_location++;
-
-               want_cookie = false;    /* not our kind of cookie */
-               tmp_ext.cookie_out_never = 0; /* false */
-               tmp_ext.cookie_plus = tmp_opt.cookie_plus;
-       } else if (!tp->rx_opt.cookie_in_always) {
-               /* redundant indications, but ensure initialization. */
-               tmp_ext.cookie_out_never = 1; /* true */
-               tmp_ext.cookie_plus = 0;
-       } else {
-               goto drop_and_free;
-       }
-       tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;
+       tcp_parse_options(skb, &tmp_opt, 0, NULL);
 
        if (want_cookie && !tmp_opt.saw_tstamp)
                tcp_clear_options(&tmp_opt);
@@ -1109,7 +1062,6 @@ have_isn:
                goto drop_and_release;
 
        if (tcp_v6_send_synack(sk, dst, &fl6, req,
-                              (struct request_values *)&tmp_ext,
                               skb_get_queue_mapping(skb)) ||
            want_cookie)
                goto drop_and_free;
index d8e5e852fc7a08b26879c81a4ecaad0be2cf2f90..da6019b63730fcf81f4af2a41bb2b30a8fcde3b1 100644 (file)
@@ -450,15 +450,16 @@ try_again:
                sin6->sin6_family = AF_INET6;
                sin6->sin6_port = udp_hdr(skb)->source;
                sin6->sin6_flowinfo = 0;
-               sin6->sin6_scope_id = 0;
 
-               if (is_udp4)
+               if (is_udp4) {
                        ipv6_addr_set_v4mapped(ip_hdr(skb)->saddr,
                                               &sin6->sin6_addr);
-               else {
+                       sin6->sin6_scope_id = 0;
+               } else {
                        sin6->sin6_addr = ipv6_hdr(skb)->saddr;
-                       if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
-                               sin6->sin6_scope_id = IP6CB(skb)->iif;
+                       sin6->sin6_scope_id =
+                               ipv6_iface_scope_id(&sin6->sin6_addr,
+                                                   IP6CB(skb)->iif);
                }
 
        }
@@ -1118,7 +1119,7 @@ do_udp_sendmsg:
 
                if (addr_len >= sizeof(struct sockaddr_in6) &&
                    sin6->sin6_scope_id &&
-                   ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
+                   __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr)))
                        fl6.flowi6_oif = sin6->sin6_scope_id;
        } else {
                if (sk->sk_state != TCP_ESTABLISHED)
index cf05cf073c517d2e0fae4c967acffffa77befcbc..3bb3a891a42416b23ddb278d3fd7c051d25cfcf7 100644 (file)
@@ -21,6 +21,10 @@ static int udp6_ufo_send_check(struct sk_buff *skb)
        const struct ipv6hdr *ipv6h;
        struct udphdr *uh;
 
+       /* UDP Tunnel offload on ipv6 is not yet supported. */
+       if (skb->encapsulation)
+               return -EINVAL;
+
        if (!pskb_may_pull(skb, sizeof(*uh)))
                return -EINVAL;
 
@@ -56,7 +60,9 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
                /* Packet is from an untrusted source, reset gso_segs. */
                int type = skb_shinfo(skb)->gso_type;
 
-               if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
+               if (unlikely(type & ~(SKB_GSO_UDP |
+                                     SKB_GSO_DODGY |
+                                     SKB_GSO_UDP_TUNNEL |
                                      SKB_GSO_GRE) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
index 9bf6a74a71d261061201e3863fd32713c91720d7..4770d515c2c856684e93416d4696067cae7e3114 100644 (file)
@@ -49,8 +49,11 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
               sizeof(top_iph->flow_lbl));
        top_iph->nexthdr = xfrm_af2proto(skb_dst(skb)->ops->family);
 
-       dsfield = XFRM_MODE_SKB_CB(skb)->tos;
-       dsfield = INET_ECN_encapsulate(dsfield, dsfield);
+       if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
+               dsfield = 0;
+       else
+               dsfield = XFRM_MODE_SKB_CB(skb)->tos;
+       dsfield = INET_ECN_encapsulate(dsfield, XFRM_MODE_SKB_CB(skb)->tos);
        if (x->props.flags & XFRM_STATE_NOECN)
                dsfield &= ~INET_ECN_MASK;
        ipv6_change_dsfield(top_iph, 0, dsfield);
index e493b3397ae3337b4582d811febb932e0711a09e..0578d4fa00a9cf1c274b2adb42d8277801dbdeb3 100644 (file)
@@ -305,8 +305,7 @@ static void irda_connect_response(struct irda_sock *self)
 
        IRDA_DEBUG(2, "%s()\n", __func__);
 
-       skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER,
-                       GFP_ATOMIC);
+       skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL);
        if (skb == NULL) {
                IRDA_DEBUG(0, "%s() Unable to allocate sk_buff!\n",
                           __func__);
@@ -1120,7 +1119,7 @@ static int irda_create(struct net *net, struct socket *sock, int protocol,
        }
 
        /* Allocate networking socket */
-       sk = sk_alloc(net, PF_IRDA, GFP_ATOMIC, &irda_proto);
+       sk = sk_alloc(net, PF_IRDA, GFP_KERNEL, &irda_proto);
        if (sk == NULL)
                return -ENOMEM;
 
index 52079f19bbbe4a087efbf0d1e4627a4e6bc8b408..b797daac063c2231efb0dc053bbf51301dae756d 100644 (file)
@@ -117,7 +117,7 @@ struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
 
        IRDA_ASSERT(ircomm != NULL, return NULL;);
 
-       self = kzalloc(sizeof(struct ircomm_cb), GFP_ATOMIC);
+       self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL);
        if (self == NULL)
                return NULL;
 
index 206ce6db2c36a2ae44d694614f8be29317f5b74d..ae691651b72141d649a4cca1aaaedc8920db5192 100644 (file)
@@ -1461,7 +1461,8 @@ unsigned int iucv_sock_poll(struct file *file, struct socket *sock,
                return iucv_accept_poll(sk);
 
        if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
-               mask |= POLLERR;
+               mask |= POLLERR |
+                       (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 
        if (sk->sk_shutdown & RCV_SHUTDOWN)
                mask |= POLLRDHUP;
index 8aecf5df66569a6ca0ae1b8921e20954d6812336..6984c3a353cd003bc91150485faeecefa078c763 100644 (file)
@@ -1777,7 +1777,7 @@ int l2tp_session_delete(struct l2tp_session *session)
        if (session->session_close != NULL)
                (*session->session_close)(session);
        if (session->deref)
-               (*session->ref)(session);
+               (*session->deref)(session);
        l2tp_session_dec_refcount(session);
        return 0;
 }
index a6893602f87a753ebafeae0d0b8f449bdc7251f7..c50c19402588807482ef7d7aad715818eed4bb90 100644 (file)
@@ -175,7 +175,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                 *       add it to the device after the station.
                 */
                if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) {
-                       ieee80211_key_free(sdata->local, key);
+                       ieee80211_key_free_unused(key);
                        err = -ENOENT;
                        goto out_unlock;
                }
@@ -214,8 +214,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        }
 
        err = ieee80211_key_link(key, sdata, sta);
-       if (err)
-               ieee80211_key_free(sdata->local, key);
 
  out_unlock:
        mutex_unlock(&sdata->local->sta_mtx);
@@ -254,7 +252,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
                goto out_unlock;
        }
 
-       __ieee80211_key_free(key);
+       ieee80211_key_free(key, true);
 
        ret = 0;
  out_unlock:
@@ -445,12 +443,14 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
        struct timespec uptime;
+       u64 packets = 0;
+       int ac;
 
        sinfo->generation = sdata->local->sta_generation;
 
        sinfo->filled = STATION_INFO_INACTIVE_TIME |
-                       STATION_INFO_RX_BYTES |
-                       STATION_INFO_TX_BYTES |
+                       STATION_INFO_RX_BYTES64 |
+                       STATION_INFO_TX_BYTES64 |
                        STATION_INFO_RX_PACKETS |
                        STATION_INFO_TX_PACKETS |
                        STATION_INFO_TX_RETRIES |
@@ -467,10 +467,14 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
        sinfo->connected_time = uptime.tv_sec - sta->last_connected;
 
        sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
+       sinfo->tx_bytes = 0;
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               sinfo->tx_bytes += sta->tx_bytes[ac];
+               packets += sta->tx_packets[ac];
+       }
+       sinfo->tx_packets = packets;
        sinfo->rx_bytes = sta->rx_bytes;
-       sinfo->tx_bytes = sta->tx_bytes;
        sinfo->rx_packets = sta->rx_packets;
-       sinfo->tx_packets = sta->tx_packets;
        sinfo->tx_retries = sta->tx_retry_count;
        sinfo->tx_failed = sta->tx_retry_failed;
        sinfo->rx_dropped_misc = sta->rx_dropped;
@@ -598,8 +602,8 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
                data[i++] += sta->rx_fragments;         \
                data[i++] += sta->rx_dropped;           \
                                                        \
-               data[i++] += sta->tx_packets;           \
-               data[i++] += sta->tx_bytes;             \
+               data[i++] += sinfo.tx_packets;          \
+               data[i++] += sinfo.tx_bytes;            \
                data[i++] += sta->tx_fragments;         \
                data[i++] += sta->tx_filtered_count;    \
                data[i++] += sta->tx_retry_failed;      \
@@ -621,13 +625,14 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
                if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
                        goto do_survey;
 
+               sinfo.filled = 0;
+               sta_set_sinfo(sta, &sinfo);
+
                i = 0;
                ADD_STA_STATS(sta);
 
                data[i++] = sta->sta_state;
 
-               sinfo.filled = 0;
-               sta_set_sinfo(sta, &sinfo);
 
                if (sinfo.filled & STATION_INFO_TX_BITRATE)
                        data[i] = 100000 *
@@ -1035,9 +1040,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
                sta_info_flush_defer(vlan);
        sta_info_flush_defer(sdata);
        rcu_barrier();
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
                sta_info_flush_cleanup(vlan);
+               ieee80211_free_keys(vlan);
+       }
        sta_info_flush_cleanup(sdata);
+       ieee80211_free_keys(sdata);
 
        sdata->vif.bss_conf.enable_beacon = false;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
@@ -1177,6 +1185,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                        mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
                if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
                        set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+       } else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               /*
+                * TDLS -- everything follows authorized, but
+                * only becoming authorized is possible, not
+                * going back
+                */
+               if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
+                       set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                              BIT(NL80211_STA_FLAG_ASSOCIATED);
+                       mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                               BIT(NL80211_STA_FLAG_ASSOCIATED);
+               }
        }
 
        ret = sta_apply_auth_flags(local, sta, mask, set);
@@ -1261,7 +1281,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
                u32 changed = 0;
-               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) {
+
+               if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
                        switch (params->plink_state) {
                        case NL80211_PLINK_ESTAB:
                                if (sta->plink_state != NL80211_PLINK_ESTAB)
@@ -1292,15 +1313,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                /*  nothing  */
                                break;
                        }
-               } else {
-                       switch (params->plink_action) {
-                       case PLINK_ACTION_OPEN:
-                               changed |= mesh_plink_open(sta);
-                               break;
-                       case PLINK_ACTION_BLOCK:
-                               changed |= mesh_plink_block(sta);
-                               break;
-                       }
+               }
+
+               switch (params->plink_action) {
+               case NL80211_PLINK_ACTION_NO_ACTION:
+                       /* nothing */
+                       break;
+               case NL80211_PLINK_ACTION_OPEN:
+                       changed |= mesh_plink_open(sta);
+                       break;
+               case NL80211_PLINK_ACTION_BLOCK:
+                       changed |= mesh_plink_block(sta);
+                       break;
                }
 
                if (params->local_pm)
@@ -1346,8 +1370,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
         * defaults -- if userspace wants something else we'll
         * change it accordingly in sta_apply_parameters()
         */
-       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_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+               sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       }
 
        err = sta_apply_parameters(local, sta, params);
        if (err) {
@@ -1356,8 +1382,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        }
 
        /*
-        * for TDLS, rate control should be initialized only when supported
-        * rates are known.
+        * for TDLS, rate control should be initialized only when
+        * rates are known and station is marked authorized
         */
        if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                rate_control_rate_init(sta);
@@ -1394,50 +1420,67 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_change_station(struct wiphy *wiphy,
-                                   struct net_device *dev,
-                                   u8 *mac,
+                                   struct net_device *dev, u8 *mac,
                                    struct station_parameters *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct sta_info *sta;
        struct ieee80211_sub_if_data *vlansdata;
+       enum cfg80211_station_type statype;
        int err;
 
        mutex_lock(&local->sta_mtx);
 
        sta = sta_info_get_bss(sdata, mac);
        if (!sta) {
-               mutex_unlock(&local->sta_mtx);
-               return -ENOENT;
+               err = -ENOENT;
+               goto out_err;
        }
 
-       /* in station mode, some updates are only valid with TDLS */
-       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-           (params->supported_rates || params->ht_capa || params->vht_capa ||
-            params->sta_modify_mask ||
-            (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) &&
-           !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
-               mutex_unlock(&local->sta_mtx);
-               return -EINVAL;
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_MESH_POINT:
+               if (sdata->u.mesh.user_mpm)
+                       statype = CFG80211_STA_MESH_PEER_USER;
+               else
+                       statype = CFG80211_STA_MESH_PEER_KERNEL;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               statype = CFG80211_STA_IBSS;
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+                       statype = CFG80211_STA_AP_STA;
+                       break;
+               }
+               if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       statype = CFG80211_STA_TDLS_PEER_ACTIVE;
+               else
+                       statype = CFG80211_STA_TDLS_PEER_SETUP;
+               break;
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               statype = CFG80211_STA_AP_CLIENT;
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               goto out_err;
        }
 
+       err = cfg80211_check_station_change(wiphy, params, statype);
+       if (err)
+               goto out_err;
+
        if (params->vlan && params->vlan != sta->sdata->dev) {
                bool prev_4addr = false;
                bool new_4addr = false;
 
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
-               if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
-                   vlansdata->vif.type != NL80211_IFTYPE_AP) {
-                       mutex_unlock(&local->sta_mtx);
-                       return -EINVAL;
-               }
-
                if (params->vlan->ieee80211_ptr->use_4addr) {
                        if (vlansdata->u.vlan.sta) {
-                               mutex_unlock(&local->sta_mtx);
-                               return -EBUSY;
+                               err = -EBUSY;
+                               goto out_err;
                        }
 
                        rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
@@ -1464,12 +1507,12 @@ static int ieee80211_change_station(struct wiphy *wiphy,
        }
 
        err = sta_apply_parameters(local, sta, params);
-       if (err) {
-               mutex_unlock(&local->sta_mtx);
-               return err;
-       }
+       if (err)
+               goto out_err;
 
-       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates)
+       /* When peer becomes authorized, init rate control as well */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           test_sta_flag(sta, WLAN_STA_AUTHORIZED))
                rate_control_rate_init(sta);
 
        mutex_unlock(&local->sta_mtx);
@@ -1479,7 +1522,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                ieee80211_recalc_ps(local, -1);
                ieee80211_recalc_ps_vif(sdata);
        }
+
        return 0;
+out_err:
+       mutex_unlock(&local->sta_mtx);
+       return err;
 }
 
 #ifdef CONFIG_MAC80211_MESH
@@ -1687,6 +1734,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
        ifmsh->mesh_sp_id = setup->sync_method;
        ifmsh->mesh_pp_id = setup->path_sel_proto;
        ifmsh->mesh_pm_id = setup->path_metric;
+       ifmsh->user_mpm = setup->user_mpm;
        ifmsh->security = IEEE80211_MESH_SEC_NONE;
        if (setup->is_authenticated)
                ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
@@ -1730,8 +1778,11 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
                conf->dot11MeshTTL = nconf->dot11MeshTTL;
        if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))
                conf->element_ttl = nconf->element_ttl;
-       if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask))
+       if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) {
+               if (ifmsh->user_mpm)
+                       return -EBUSY;
                conf->auto_open_plinks = nconf->auto_open_plinks;
+       }
        if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask))
                conf->dot11MeshNbrOffsetMaxNeighbor =
                        nconf->dot11MeshNbrOffsetMaxNeighbor;
@@ -2371,7 +2422,8 @@ 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)
+                                   struct sk_buff *txskb,
+                                   enum ieee80211_roc_type type)
 {
        struct ieee80211_roc_work *roc, *tmp;
        bool queued = false;
@@ -2390,6 +2442,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        roc->duration = duration;
        roc->req_duration = duration;
        roc->frame = txskb;
+       roc->type = type;
        roc->mgmt_tx_cookie = (unsigned long)txskb;
        roc->sdata = sdata;
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
@@ -2420,7 +2473,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        if (!duration)
                duration = 10;
 
-       ret = drv_remain_on_channel(local, sdata, channel, duration);
+       ret = drv_remain_on_channel(local, sdata, channel, duration, type);
        if (ret) {
                kfree(roc);
                return ret;
@@ -2439,10 +2492,13 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                 *
                 * 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;
                }
@@ -2454,16 +2510,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                        /*
                         * In the offloaded ROC case, if it hasn't begun, add
                         * this new one to the dependent list to be handled
-                        * when the the master one begins. If it has begun,
+                        * when the master one begins. If it has begun,
                         * check that there's still a minimum time left and
                         * if so, start this one, transmitting the frame, but
-                        * add it to the list directly after this one with a
+                        * add it to the list directly after this one with
                         * a reduced time so we'll ask the driver to execute
                         * it right after finishing the previous one, in the
                         * hope that it'll also be executed right afterwards,
                         * effectively extending the old one.
                         * If there's no minimum time left, just add it to the
                         * normal list.
+                        * TODO: the ROC type is ignored here, assuming that it
+                        * is better to immediately use the current ROC.
                         */
                        if (!tmp->hw_begun) {
                                list_add_tail(&roc->list, &tmp->dependents);
@@ -2557,7 +2615,8 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
 
        mutex_lock(&local->mtx);
        ret = ieee80211_start_roc_work(local, sdata, chan,
-                                      duration, cookie, NULL);
+                                      duration, cookie, NULL,
+                                      IEEE80211_ROC_TYPE_NORMAL);
        mutex_unlock(&local->mtx);
 
        return ret;
@@ -2792,7 +2851,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        /* This will handle all kinds of coalescing and immediate TX */
        ret = ieee80211_start_roc_work(local, sdata, chan,
-                                      wait, cookie, skb);
+                                      wait, cookie, skb,
+                                      IEEE80211_ROC_TYPE_MGMT_TX);
        if (ret)
                kfree_skb(skb);
  out_unlock:
index c3a3082b72e5cde611b5fb89a63829c7fb4eee27..1521cabad3d6d0f246acfc37b7512a489f14965a 100644 (file)
@@ -295,7 +295,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
        char buf[50];
        struct ieee80211_key *key;
 
-       if (!sdata->debugfs.dir)
+       if (!sdata->vif.debugfs_dir)
                return;
 
        lockdep_assert_held(&sdata->local->key_mtx);
@@ -311,7 +311,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
                sprintf(buf, "../keys/%d", key->debugfs.cnt);
                sdata->debugfs.default_unicast_key =
                        debugfs_create_symlink("default_unicast_key",
-                                              sdata->debugfs.dir, buf);
+                                              sdata->vif.debugfs_dir, buf);
        }
 
        if (sdata->debugfs.default_multicast_key) {
@@ -325,7 +325,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
                sprintf(buf, "../keys/%d", key->debugfs.cnt);
                sdata->debugfs.default_multicast_key =
                        debugfs_create_symlink("default_multicast_key",
-                                              sdata->debugfs.dir, buf);
+                                              sdata->vif.debugfs_dir, buf);
        }
 }
 
@@ -334,7 +334,7 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)
        char buf[50];
        struct ieee80211_key *key;
 
-       if (!sdata->debugfs.dir)
+       if (!sdata->vif.debugfs_dir)
                return;
 
        key = key_mtx_dereference(sdata->local,
@@ -343,7 +343,7 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)
                sprintf(buf, "../keys/%d", key->debugfs.cnt);
                sdata->debugfs.default_mgmt_key =
                        debugfs_create_symlink("default_mgmt_key",
-                                              sdata->debugfs.dir, buf);
+                                              sdata->vif.debugfs_dir, buf);
        } else
                ieee80211_debugfs_key_remove_mgmt_default(sdata);
 }
index 059bbb82e84fec6f6d6fec5d5bdd5e400648cfc5..ddb426867904d55c3b14ab4d7642bef3b0cd66cd 100644 (file)
@@ -521,7 +521,7 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
 #endif
 
 #define DEBUGFS_ADD_MODE(name, mode) \
-       debugfs_create_file(#name, mode, sdata->debugfs.dir, \
+       debugfs_create_file(#name, mode, sdata->vif.debugfs_dir, \
                            sdata, &name##_ops);
 
 #define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400)
@@ -577,7 +577,7 @@ static void add_mesh_files(struct ieee80211_sub_if_data *sdata)
 static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
 {
        struct dentry *dir = debugfs_create_dir("mesh_stats",
-                                               sdata->debugfs.dir);
+                                               sdata->vif.debugfs_dir);
 #define MESHSTATS_ADD(name)\
        debugfs_create_file(#name, 0400, dir, sdata, &name##_ops);
 
@@ -594,7 +594,7 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
 static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
 {
        struct dentry *dir = debugfs_create_dir("mesh_config",
-                                               sdata->debugfs.dir);
+                                               sdata->vif.debugfs_dir);
 
 #define MESHPARAMS_ADD(name) \
        debugfs_create_file(#name, 0600, dir, sdata, &name##_ops);
@@ -631,7 +631,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
 
 static void add_files(struct ieee80211_sub_if_data *sdata)
 {
-       if (!sdata->debugfs.dir)
+       if (!sdata->vif.debugfs_dir)
                return;
 
        DEBUGFS_ADD(flags);
@@ -673,21 +673,21 @@ void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)
        char buf[10+IFNAMSIZ];
 
        sprintf(buf, "netdev:%s", sdata->name);
-       sdata->debugfs.dir = debugfs_create_dir(buf,
+       sdata->vif.debugfs_dir = debugfs_create_dir(buf,
                sdata->local->hw.wiphy->debugfsdir);
-       if (sdata->debugfs.dir)
+       if (sdata->vif.debugfs_dir)
                sdata->debugfs.subdir_stations = debugfs_create_dir("stations",
-                       sdata->debugfs.dir);
+                       sdata->vif.debugfs_dir);
        add_files(sdata);
 }
 
 void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)
 {
-       if (!sdata->debugfs.dir)
+       if (!sdata->vif.debugfs_dir)
                return;
 
-       debugfs_remove_recursive(sdata->debugfs.dir);
-       sdata->debugfs.dir = NULL;
+       debugfs_remove_recursive(sdata->vif.debugfs_dir);
+       sdata->vif.debugfs_dir = NULL;
 }
 
 void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)
@@ -695,7 +695,7 @@ void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)
        struct dentry *dir;
        char buf[10 + IFNAMSIZ];
 
-       dir = sdata->debugfs.dir;
+       dir = sdata->vif.debugfs_dir;
 
        if (!dir)
                return;
index c7591f73dbc33296ae26972d87363c2be2e1839c..4f841fe559df3d834516bf50c60709929d1c4639 100644 (file)
@@ -325,6 +325,36 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
 }
 STA_OPS(ht_capa);
 
+static ssize_t sta_vht_capa_read(struct file *file, char __user *userbuf,
+                                size_t count, loff_t *ppos)
+{
+       char buf[128], *p = buf;
+       struct sta_info *sta = file->private_data;
+       struct ieee80211_sta_vht_cap *vhtc = &sta->sta.vht_cap;
+
+       p += scnprintf(p, sizeof(buf) + buf - p, "VHT %ssupported\n",
+                       vhtc->vht_supported ? "" : "not ");
+       if (vhtc->vht_supported) {
+               p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.8x\n", vhtc->cap);
+
+               p += scnprintf(p, sizeof(buf)+buf-p, "RX MCS: %.4x\n",
+                              le16_to_cpu(vhtc->vht_mcs.rx_mcs_map));
+               if (vhtc->vht_mcs.rx_highest)
+                       p += scnprintf(p, sizeof(buf)+buf-p,
+                                      "MCS RX highest: %d Mbps\n",
+                                      le16_to_cpu(vhtc->vht_mcs.rx_highest));
+               p += scnprintf(p, sizeof(buf)+buf-p, "TX MCS: %.4x\n",
+                              le16_to_cpu(vhtc->vht_mcs.tx_mcs_map));
+               if (vhtc->vht_mcs.tx_highest)
+                       p += scnprintf(p, sizeof(buf)+buf-p,
+                                      "MCS TX highest: %d Mbps\n",
+                                      le16_to_cpu(vhtc->vht_mcs.tx_highest));
+       }
+
+       return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+}
+STA_OPS(vht_capa);
+
 static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf,
                                        size_t count, loff_t *ppos)
 {
@@ -405,6 +435,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
        DEBUGFS_ADD(dev);
        DEBUGFS_ADD(last_signal);
        DEBUGFS_ADD(ht_capa);
+       DEBUGFS_ADD(vht_capa);
        DEBUGFS_ADD(last_ack_signal);
        DEBUGFS_ADD(current_tx_rate);
        DEBUGFS_ADD(last_rx_rate);
index ee56d0779d8bdea4b5374ce5d0749cbbe1a88937..169664c122e23cbe80f89016714bdcdfec24dc6e 100644 (file)
@@ -241,6 +241,22 @@ static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
        return ret;
 }
 
+static inline void drv_set_multicast_list(struct ieee80211_local *local,
+                                         struct ieee80211_sub_if_data *sdata,
+                                         struct netdev_hw_addr_list *mc_list)
+{
+       bool allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI;
+
+       trace_drv_set_multicast_list(local, sdata, mc_list->count);
+
+       check_sdata_in_driver(sdata);
+
+       if (local->ops->set_multicast_list)
+               local->ops->set_multicast_list(&local->hw, &sdata->vif,
+                                              allmulti, mc_list);
+       trace_drv_return_void(local);
+}
+
 static inline void drv_configure_filter(struct ieee80211_local *local,
                                        unsigned int changed_flags,
                                        unsigned int *total_flags,
@@ -531,43 +547,6 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,
                local->ops->sta_remove_debugfs(&local->hw, &sdata->vif,
                                               sta, dir);
 }
-
-static inline
-void drv_add_interface_debugfs(struct ieee80211_local *local,
-                              struct ieee80211_sub_if_data *sdata)
-{
-       might_sleep();
-
-       check_sdata_in_driver(sdata);
-
-       if (!local->ops->add_interface_debugfs)
-               return;
-
-       local->ops->add_interface_debugfs(&local->hw, &sdata->vif,
-                                         sdata->debugfs.dir);
-}
-
-static inline
-void drv_remove_interface_debugfs(struct ieee80211_local *local,
-                                 struct ieee80211_sub_if_data *sdata)
-{
-       might_sleep();
-
-       check_sdata_in_driver(sdata);
-
-       if (!local->ops->remove_interface_debugfs)
-               return;
-
-       local->ops->remove_interface_debugfs(&local->hw, &sdata->vif,
-                                            sdata->debugfs.dir);
-}
-#else
-static inline
-void drv_add_interface_debugfs(struct ieee80211_local *local,
-                              struct ieee80211_sub_if_data *sdata) {}
-static inline
-void drv_remove_interface_debugfs(struct ieee80211_local *local,
-                                 struct ieee80211_sub_if_data *sdata) {}
 #endif
 
 static inline __must_check
@@ -741,13 +720,14 @@ static inline void drv_rfkill_poll(struct ieee80211_local *local)
                local->ops->rfkill_poll(&local->hw);
 }
 
-static inline void drv_flush(struct ieee80211_local *local, bool drop)
+static inline void drv_flush(struct ieee80211_local *local,
+                            u32 queues, bool drop)
 {
        might_sleep();
 
-       trace_drv_flush(local, drop);
+       trace_drv_flush(local, queues, drop);
        if (local->ops->flush)
-               local->ops->flush(&local->hw, drop);
+               local->ops->flush(&local->hw, queues, drop);
        trace_drv_return_void(local);
 }
 
@@ -787,15 +767,16 @@ static inline int drv_get_antenna(struct ieee80211_local *local,
 static inline int drv_remain_on_channel(struct ieee80211_local *local,
                                        struct ieee80211_sub_if_data *sdata,
                                        struct ieee80211_channel *chan,
-                                       unsigned int duration)
+                                       unsigned int duration,
+                                       enum ieee80211_roc_type type)
 {
        int ret;
 
        might_sleep();
 
-       trace_drv_remain_on_channel(local, sdata, chan, duration);
+       trace_drv_remain_on_channel(local, sdata, chan, duration, type);
        ret = local->ops->remain_on_channel(&local->hw, &sdata->vif,
-                                           chan, duration);
+                                           chan, duration, type);
        trace_drv_return_int(local, ret);
 
        return ret;
index 0db25d4bb223156a5420e900620c154bf7a92726..af8cee06e4f37475ed17c764e55cf75c070ef9f2 100644 (file)
@@ -40,13 +40,6 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        if (!ht_cap->ht_supported)
                return;
 
-       if (sdata->vif.type != NL80211_IFTYPE_STATION) {
-               /* AP interfaces call this code when adding new stations,
-                * so just silently ignore non station interfaces.
-                */
-               return;
-       }
-
        /* NOTE:  If you add more over-rides here, update register_hw
         * ht_capa_mod_msk logic in main.c as well.
         * And, if this method can ever change ht_cap.ht_supported, fix
@@ -97,7 +90,7 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
                                       const struct ieee80211_ht_cap *ht_cap_ie,
                                       struct sta_info *sta)
 {
-       struct ieee80211_sta_ht_cap ht_cap;
+       struct ieee80211_sta_ht_cap ht_cap, own_cap;
        u8 ampdu_info, tx_mcs_set_cap;
        int i, max_tx_streams;
        bool changed;
@@ -111,6 +104,18 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
 
        ht_cap.ht_supported = true;
 
+       own_cap = sband->ht_cap;
+
+       /*
+        * If user has specified capability over-rides, take care
+        * of that if the station we're setting up is the AP that
+        * we advertised a restricted capability set to. Override
+        * our own capabilities and then use those below.
+        */
+       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+           !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               ieee80211_apply_htcap_overrides(sdata, &own_cap);
+
        /*
         * The bits listed in this expression should be
         * the same for the peer and us, if the station
@@ -118,21 +123,20 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
         * we mask them out.
         */
        ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
-               (sband->ht_cap.cap |
-                ~(IEEE80211_HT_CAP_LDPC_CODING |
-                  IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
-                  IEEE80211_HT_CAP_GRN_FLD |
-                  IEEE80211_HT_CAP_SGI_20 |
-                  IEEE80211_HT_CAP_SGI_40 |
-                  IEEE80211_HT_CAP_DSSSCCK40));
+               (own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING |
+                                IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+                                IEEE80211_HT_CAP_GRN_FLD |
+                                IEEE80211_HT_CAP_SGI_20 |
+                                IEEE80211_HT_CAP_SGI_40 |
+                                IEEE80211_HT_CAP_DSSSCCK40));
 
        /*
         * The STBC bits are asymmetric -- if we don't have
         * TX then mask out the peer's RX and vice versa.
         */
-       if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
+       if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC))
                ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
-       if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC))
+       if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC))
                ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
 
        ampdu_info = ht_cap_ie->ampdu_params_info;
@@ -142,7 +146,7 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
                (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
 
        /* own MCS TX capabilities */
-       tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
+       tx_mcs_set_cap = own_cap.mcs.tx_params;
 
        /* Copy peer MCS TX capabilities, the driver might need them. */
        ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
@@ -168,26 +172,20 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
         */
        for (i = 0; i < max_tx_streams; i++)
                ht_cap.mcs.rx_mask[i] =
-                       sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
+                       own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
 
        if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
                for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
                     i < IEEE80211_HT_MCS_MASK_LEN; i++)
                        ht_cap.mcs.rx_mask[i] =
-                               sband->ht_cap.mcs.rx_mask[i] &
+                               own_cap.mcs.rx_mask[i] &
                                        ht_cap_ie->mcs.rx_mask[i];
 
        /* handle MCS rate 32 too */
-       if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
+       if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
                ht_cap.mcs.rx_mask[32/8] |= 1;
 
  apply:
-       /*
-        * If user has specified capability over-rides, take care
-        * of that here.
-        */
-       ieee80211_apply_htcap_overrides(sdata, &ht_cap);
-
        changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
 
        memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
index 40b71dfcc79d2b61514de09949c17fe536e7f291..539d4a11b47bc9be2d30625ef0d79c97c9d9cdd5 100644 (file)
@@ -985,36 +985,9 @@ static void ieee80211_ibss_timer(unsigned long data)
 {
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
-       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-       struct ieee80211_local *local = sdata->local;
-
-       if (local->quiescing) {
-               ifibss->timer_running = true;
-               return;
-       }
-
-       ieee80211_queue_work(&local->hw, &sdata->work);
-}
-
-#ifdef CONFIG_PM
-void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 
-       if (del_timer_sync(&ifibss->timer))
-               ifibss->timer_running = true;
-}
-
-void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-
-       if (ifibss->timer_running) {
-               add_timer(&ifibss->timer);
-               ifibss->timer_running = false;
-       }
+       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
-#endif
 
 void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
 {
index 5672533a083279ddc50fe98a09bbd4363a9fc731..0b09716d22ad6b43a24fef2be6e6551a515c8bb4 100644 (file)
@@ -316,6 +316,7 @@ struct ieee80211_roc_work {
        u32 duration, req_duration;
        struct sk_buff *frame;
        u64 cookie, mgmt_tx_cookie;
+       enum ieee80211_roc_type type;
 };
 
 /* flags used in struct ieee80211_if_managed.flags */
@@ -401,7 +402,6 @@ struct ieee80211_if_managed {
 
        u16 aid;
 
-       unsigned long timers_running; /* used for quiesce/restart */
        bool powersave; /* powersave requested for this iface */
        bool broken_ap; /* AP is broken -- turn off powersave */
        u8 dtim_period;
@@ -480,6 +480,8 @@ struct ieee80211_if_managed {
 
        struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
        struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
+       struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
+       struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
 };
 
 struct ieee80211_if_ibss {
@@ -491,8 +493,6 @@ struct ieee80211_if_ibss {
 
        u32 basic_rates;
 
-       bool timer_running;
-
        bool fixed_bssid;
        bool fixed_channel;
        bool privacy;
@@ -544,8 +544,6 @@ struct ieee80211_if_mesh {
        struct timer_list mesh_path_timer;
        struct timer_list mesh_path_root_timer;
 
-       unsigned long timers_running;
-
        unsigned long wrkq_flags;
 
        u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
@@ -591,6 +589,7 @@ struct ieee80211_if_mesh {
                IEEE80211_MESH_SEC_AUTHED = 0x1,
                IEEE80211_MESH_SEC_SECURED = 0x2,
        } security;
+       bool user_mpm;
        /* Extensible Synchronization Framework */
        const struct ieee80211_mesh_sync_ops *sync_ops;
        s64 sync_offset_clockdrift_max;
@@ -683,6 +682,8 @@ struct ieee80211_sub_if_data {
 
        /* count for keys needing tailroom space allocation */
        int crypto_tx_tailroom_needed_cnt;
+       int crypto_tx_tailroom_pending_dec;
+       struct delayed_work dec_tailroom_needed_wk;
 
        struct net_device *dev;
        struct ieee80211_local *local;
@@ -758,7 +759,6 @@ struct ieee80211_sub_if_data {
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct {
-               struct dentry *dir;
                struct dentry *subdir_stations;
                struct dentry *default_unicast_key;
                struct dentry *default_multicast_key;
@@ -766,10 +766,6 @@ struct ieee80211_sub_if_data {
        } debugfs;
 #endif
 
-#ifdef CONFIG_PM
-       struct ieee80211_bss_conf suspend_bss_conf;
-#endif
-
        /* must be last, dynamically sized area in this! */
        struct ieee80211_vif vif;
 };
@@ -804,11 +800,6 @@ enum sdata_queue_type {
 enum {
        IEEE80211_RX_MSG        = 1,
        IEEE80211_TX_STATUS_MSG = 2,
-       IEEE80211_EOSP_MSG      = 3,
-};
-
-struct skb_eosp_msg_data {
-       u8 sta[ETH_ALEN], iface[ETH_ALEN];
 };
 
 enum queue_stop_reason {
@@ -819,6 +810,7 @@ enum queue_stop_reason {
        IEEE80211_QUEUE_STOP_REASON_SUSPEND,
        IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
+       IEEE80211_QUEUE_STOP_REASON_FLUSH,
 };
 
 #ifdef CONFIG_MAC80211_LEDS
@@ -1137,11 +1129,6 @@ struct ieee80211_local {
 
        struct ieee80211_sub_if_data __rcu *p2p_sdata;
 
-       /* dummy netdev for use w/ NAPI */
-       struct net_device napi_dev;
-
-       struct napi_struct napi;
-
        /* virtual monitor interface */
        struct ieee80211_sub_if_data __rcu *monitor_sdata;
        struct cfg80211_chan_def monitor_chandef;
@@ -1284,8 +1271,6 @@ void
 ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                                 const struct ieee80211_channel_sw_ie *sw_elem,
                                 struct ieee80211_bss *bss, u64 timestamp);
-void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
-void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                  struct sk_buff *skb);
@@ -1303,8 +1288,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
 int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_ibss_params *params);
 int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
-void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata);
-void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);
 void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                   struct sk_buff *skb);
@@ -1443,6 +1426,8 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta);
 void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
                                 struct sta_info *sta, u8 opmode,
                                 enum ieee80211_band band, bool nss_only);
+void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
+                                     struct ieee80211_sta_vht_cap *vht_cap);
 
 /* Spectrum management */
 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
@@ -1540,8 +1525,10 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
                             struct ieee80211_hdr *hdr, bool ack);
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+                                    unsigned long queues,
                                     enum queue_stop_reason reason);
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+                                    unsigned long queues,
                                     enum queue_stop_reason reason);
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
                                    enum queue_stop_reason reason);
@@ -1558,6 +1545,8 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local,
 {
        ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL);
 }
+void ieee80211_flush_queues(struct ieee80211_local *local,
+                           struct ieee80211_sub_if_data *sdata);
 
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
index 9ed49ad0380f151fc5ad5448e953f6497d800291..e8a260f53c16d98614a660b72d56a43dc0dd8ab9 100644 (file)
@@ -92,7 +92,7 @@ static u32 __ieee80211_idle_on(struct ieee80211_local *local)
        if (local->hw.conf.flags & IEEE80211_CONF_IDLE)
                return 0;
 
-       drv_flush(local, false);
+       ieee80211_flush_queues(local, NULL);
 
        local->hw.conf.flags |= IEEE80211_CONF_IDLE;
        return IEEE80211_CONF_CHANGE_IDLE;
@@ -499,8 +499,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                res = drv_start(local);
                if (res)
                        goto err_del_bss;
-               if (local->ops->napi_poll)
-                       napi_enable(&local->napi);
                /* we're brought up, everything changes */
                hw_reconf_flags = ~0;
                ieee80211_led_radio(local, true);
@@ -573,8 +571,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                                goto err_del_interface;
                }
 
-               drv_add_interface_debugfs(local, sdata);
-
                if (sdata->vif.type == NL80211_IFTYPE_AP) {
                        local->fif_pspoll++;
                        local->fif_probe_req++;
@@ -852,15 +848,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                rcu_barrier();
                sta_info_flush_cleanup(sdata);
 
-               skb_queue_purge(&sdata->skb_queue);
-
                /*
                 * Free all remaining keys, there shouldn't be any,
-                * except maybe group keys in AP more or WDS?
+                * except maybe in WDS mode?
                 */
                ieee80211_free_keys(sdata);
 
-               drv_remove_interface_debugfs(local, sdata);
+               /* fall through */
+       case NL80211_IFTYPE_AP:
+               skb_queue_purge(&sdata->skb_queue);
 
                if (going_down)
                        drv_remove_interface(local, sdata);
@@ -871,8 +867,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        ieee80211_recalc_ps(local, -1);
 
        if (local->open_count == 0) {
-               if (local->ops->napi_poll)
-                       napi_disable(&local->napi);
                ieee80211_clear_tx_pending(local);
                ieee80211_stop_device(local);
 
@@ -935,6 +929,17 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
                        atomic_dec(&local->iff_promiscs);
                sdata->flags ^= IEEE80211_SDATA_PROMISC;
        }
+
+       /*
+        * TODO: If somebody needs this on AP interfaces,
+        *       it can be enabled easily but multicast
+        *       addresses from VLANs need to be synced.
+        */
+       if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+           sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+           sdata->vif.type != NL80211_IFTYPE_AP)
+               drv_set_multicast_list(local, sdata, &dev->mc);
+
        spin_lock_bh(&local->filter_lock);
        __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
        spin_unlock_bh(&local->filter_lock);
@@ -1561,6 +1566,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
        INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
                          ieee80211_dfs_cac_timer_work);
+       INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
+                         ieee80211_delayed_tailroom_dec);
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                struct ieee80211_supported_band *sband;
index ef252eb58c36b0740e9ecbb6f9b93aae66e6b054..67059b88fea5f28619c97ad2ca8c150840045055 100644 (file)
@@ -248,11 +248,11 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
 }
 
 
-static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
-                                   struct sta_info *sta,
-                                   bool pairwise,
-                                   struct ieee80211_key *old,
-                                   struct ieee80211_key *new)
+static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
+                                 struct sta_info *sta,
+                                 bool pairwise,
+                                 struct ieee80211_key *old,
+                                 struct ieee80211_key *new)
 {
        int idx;
        bool defunikey, defmultikey, defmgmtkey;
@@ -397,7 +397,41 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
        return key;
 }
 
-static void __ieee80211_key_destroy(struct ieee80211_key *key)
+static void ieee80211_key_free_common(struct ieee80211_key *key)
+{
+       if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP)
+               ieee80211_aes_key_free(key->u.ccmp.tfm);
+       if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+               ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
+       kfree(key);
+}
+
+static void __ieee80211_key_destroy(struct ieee80211_key *key,
+                                   bool delay_tailroom)
+{
+       if (key->local)
+               ieee80211_key_disable_hw_accel(key);
+
+       if (key->local) {
+               struct ieee80211_sub_if_data *sdata = key->sdata;
+
+               ieee80211_debugfs_key_remove(key);
+
+               if (delay_tailroom) {
+                       /* see ieee80211_delayed_tailroom_dec */
+                       sdata->crypto_tx_tailroom_pending_dec++;
+                       schedule_delayed_work(&sdata->dec_tailroom_needed_wk,
+                                             HZ/2);
+               } else {
+                       sdata->crypto_tx_tailroom_needed_cnt--;
+               }
+       }
+
+       ieee80211_key_free_common(key);
+}
+
+static void ieee80211_key_destroy(struct ieee80211_key *key,
+                                 bool delay_tailroom)
 {
        if (!key)
                return;
@@ -408,19 +442,13 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
         */
        synchronize_net();
 
-       if (key->local)
-               ieee80211_key_disable_hw_accel(key);
-
-       if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP)
-               ieee80211_aes_key_free(key->u.ccmp.tfm);
-       if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
-               ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
-       if (key->local) {
-               ieee80211_debugfs_key_remove(key);
-               key->sdata->crypto_tx_tailroom_needed_cnt--;
-       }
+       __ieee80211_key_destroy(key, delay_tailroom);
+}
 
-       kfree(key);
+void ieee80211_key_free_unused(struct ieee80211_key *key)
+{
+       WARN_ON(key->sdata || key->local);
+       ieee80211_key_free_common(key);
 }
 
 int ieee80211_key_link(struct ieee80211_key *key,
@@ -440,32 +468,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
        key->sdata = sdata;
        key->sta = sta;
 
-       if (sta) {
-               /*
-                * some hardware cannot handle TKIP with QoS, so
-                * we indicate whether QoS could be in use.
-                */
-               if (test_sta_flag(sta, WLAN_STA_WME))
-                       key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
-       } else {
-               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-                       struct sta_info *ap;
-
-                       /*
-                        * We're getting a sta pointer in, so must be under
-                        * appropriate locking for sta_info_get().
-                        */
-
-                       /* same here, the AP could be using QoS */
-                       ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid);
-                       if (ap) {
-                               if (test_sta_flag(ap, WLAN_STA_WME))
-                                       key->conf.flags |=
-                                               IEEE80211_KEY_FLAG_WMM_STA;
-                       }
-               }
-       }
-
        mutex_lock(&sdata->local->key_mtx);
 
        if (sta && pairwise)
@@ -477,19 +479,22 @@ int ieee80211_key_link(struct ieee80211_key *key,
 
        increment_tailroom_need_count(sdata);
 
-       __ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
-       __ieee80211_key_destroy(old_key);
+       ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
+       ieee80211_key_destroy(old_key, true);
 
        ieee80211_debugfs_key_add(key);
 
        ret = ieee80211_key_enable_hw_accel(key);
 
+       if (ret)
+               ieee80211_key_free(key, true);
+
        mutex_unlock(&sdata->local->key_mtx);
 
        return ret;
 }
 
-void __ieee80211_key_free(struct ieee80211_key *key)
+void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)
 {
        if (!key)
                return;
@@ -498,18 +503,10 @@ void __ieee80211_key_free(struct ieee80211_key *key)
         * Replace key with nothingness if it was ever used.
         */
        if (key->sdata)
-               __ieee80211_key_replace(key->sdata, key->sta,
+               ieee80211_key_replace(key->sdata, key->sta,
                                key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
                                key, NULL);
-       __ieee80211_key_destroy(key);
-}
-
-void ieee80211_key_free(struct ieee80211_local *local,
-                       struct ieee80211_key *key)
-{
-       mutex_lock(&local->key_mtx);
-       __ieee80211_key_free(key);
-       mutex_unlock(&local->key_mtx);
+       ieee80211_key_destroy(key, delay_tailroom);
 }
 
 void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
@@ -566,36 +563,109 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_iter_keys);
 
-void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
+void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_key *key;
+       struct ieee80211_key *key, *tmp;
+       LIST_HEAD(keys);
 
-       ASSERT_RTNL();
+       cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);
 
        mutex_lock(&sdata->local->key_mtx);
 
-       list_for_each_entry(key, &sdata->key_list, list)
-               ieee80211_key_disable_hw_accel(key);
+       sdata->crypto_tx_tailroom_needed_cnt -=
+               sdata->crypto_tx_tailroom_pending_dec;
+       sdata->crypto_tx_tailroom_pending_dec = 0;
+
+       ieee80211_debugfs_key_remove_mgmt_default(sdata);
+
+       list_for_each_entry_safe(key, tmp, &sdata->key_list, list) {
+               ieee80211_key_replace(key->sdata, key->sta,
+                               key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+                               key, NULL);
+               list_add_tail(&key->list, &keys);
+       }
+
+       ieee80211_debugfs_key_update_default(sdata);
+
+       if (!list_empty(&keys)) {
+               synchronize_net();
+               list_for_each_entry_safe(key, tmp, &keys, list)
+                       __ieee80211_key_destroy(key, false);
+       }
+
+       WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||
+                    sdata->crypto_tx_tailroom_pending_dec);
 
        mutex_unlock(&sdata->local->key_mtx);
 }
 
-void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
+void ieee80211_free_sta_keys(struct ieee80211_local *local,
+                            struct sta_info *sta)
 {
        struct ieee80211_key *key, *tmp;
+       LIST_HEAD(keys);
+       int i;
 
-       mutex_lock(&sdata->local->key_mtx);
+       mutex_lock(&local->key_mtx);
+       for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+               key = key_mtx_dereference(local, sta->gtk[i]);
+               if (!key)
+                       continue;
+               ieee80211_key_replace(key->sdata, key->sta,
+                               key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+                               key, NULL);
+               list_add(&key->list, &keys);
+       }
 
-       ieee80211_debugfs_key_remove_mgmt_default(sdata);
+       key = key_mtx_dereference(local, sta->ptk);
+       if (key) {
+               ieee80211_key_replace(key->sdata, key->sta,
+                               key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+                               key, NULL);
+               list_add(&key->list, &keys);
+       }
 
-       list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
-               __ieee80211_key_free(key);
+       /*
+        * NB: the station code relies on this being
+        * done even if there aren't any keys
+        */
+       synchronize_net();
 
-       ieee80211_debugfs_key_update_default(sdata);
+       list_for_each_entry_safe(key, tmp, &keys, list)
+               __ieee80211_key_destroy(key, true);
 
-       mutex_unlock(&sdata->local->key_mtx);
+       mutex_unlock(&local->key_mtx);
 }
 
+void ieee80211_delayed_tailroom_dec(struct work_struct *wk)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       sdata = container_of(wk, struct ieee80211_sub_if_data,
+                            dec_tailroom_needed_wk.work);
+
+       /*
+        * The reason for the delayed tailroom needed decrementing is to
+        * make roaming faster: during roaming, all keys are first deleted
+        * and then new keys are installed. The first new key causes the
+        * crypto_tx_tailroom_needed_cnt to go from 0 to 1, which invokes
+        * the cost of synchronize_net() (which can be slow). Avoid this
+        * by deferring the crypto_tx_tailroom_needed_cnt decrementing on
+        * key removal for a while, so if we roam the value is larger than
+        * zero and no 0->1 transition happens.
+        *
+        * The cost is that if the AP switching was from an AP with keys
+        * to one without, we still allocate tailroom while it would no
+        * longer be needed. However, in the typical (fast) roaming case
+        * within an ESS this usually won't happen.
+        */
+
+       mutex_lock(&sdata->local->key_mtx);
+       sdata->crypto_tx_tailroom_needed_cnt -=
+               sdata->crypto_tx_tailroom_pending_dec;
+       sdata->crypto_tx_tailroom_pending_dec = 0;
+       mutex_unlock(&sdata->local->key_mtx);
+}
 
 void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
                                const u8 *replay_ctr, gfp_t gfp)
index 382dc44ed330a9921764431bc88345728b0eab49..e8de3e6d7804be1e9562531be2d28372532897ea 100644 (file)
@@ -129,23 +129,25 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
                                          size_t seq_len, const u8 *seq);
 /*
  * Insert a key into data structures (sdata, sta if necessary)
- * to make it used, free old key.
+ * to make it used, free old key. On failure, also free the new key.
  */
-int __must_check ieee80211_key_link(struct ieee80211_key *key,
-                                   struct ieee80211_sub_if_data *sdata,
-                                   struct sta_info *sta);
-void __ieee80211_key_free(struct ieee80211_key *key);
-void ieee80211_key_free(struct ieee80211_local *local,
-                       struct ieee80211_key *key);
+int ieee80211_key_link(struct ieee80211_key *key,
+                      struct ieee80211_sub_if_data *sdata,
+                      struct sta_info *sta);
+void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom);
+void ieee80211_key_free_unused(struct ieee80211_key *key);
 void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
                               bool uni, bool multi);
 void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
                                    int idx);
 void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
+void ieee80211_free_sta_keys(struct ieee80211_local *local,
+                            struct sta_info *sta);
 void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata);
-void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata);
 
 #define key_mtx_dereference(local, ref) \
        rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx)))
 
+void ieee80211_delayed_tailroom_dec(struct work_struct *wk);
+
 #endif /* IEEE80211_KEY_H */
index 1a8591b77a13d1ff1e41a67f6c8ed8c57daa83e9..c6f81ecc36a15bd0375aaeaef0ac5f85e4afc575 100644 (file)
@@ -100,7 +100,6 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
        int power;
        enum nl80211_channel_type channel_type;
        u32 offchannel_flag;
-       bool scanning = false;
 
        offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
        if (local->scan_channel) {
@@ -147,9 +146,6 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
                changed |= IEEE80211_CONF_CHANGE_SMPS;
        }
 
-       scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
-                  test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
-                  test_bit(SCAN_HW_SCANNING, &local->scanning);
        power = chan->max_power;
 
        rcu_read_lock();
@@ -226,8 +222,6 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
 static void ieee80211_tasklet_handler(unsigned long data)
 {
        struct ieee80211_local *local = (struct ieee80211_local *) data;
-       struct sta_info *sta, *tmp;
-       struct skb_eosp_msg_data *eosp_data;
        struct sk_buff *skb;
 
        while ((skb = skb_dequeue(&local->skb_queue)) ||
@@ -243,18 +237,6 @@ static void ieee80211_tasklet_handler(unsigned long data)
                        skb->pkt_type = 0;
                        ieee80211_tx_status(&local->hw, skb);
                        break;
-               case IEEE80211_EOSP_MSG:
-                       eosp_data = (void *)skb->cb;
-                       for_each_sta_info(local, eosp_data->sta, sta, tmp) {
-                               /* skip wrong virtual interface */
-                               if (memcmp(eosp_data->iface,
-                                          sta->sdata->vif.addr, ETH_ALEN))
-                                       continue;
-                               clear_sta_flag(sta, WLAN_STA_SP);
-                               break;
-                       }
-                       dev_kfree_skb(skb);
-                       break;
                default:
                        WARN(1, "mac80211: Packet is of unknown type %d\n",
                             skb->pkt_type);
@@ -295,8 +277,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
                   "Hardware restart was requested\n");
 
        /* use this reason, ieee80211_reconfig will unblock it */
-       ieee80211_stop_queues_by_reason(hw,
-               IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+       ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
        /*
         * Stop all Rx during the reconfig. We don't want state changes
@@ -399,30 +381,6 @@ static int ieee80211_ifa6_changed(struct notifier_block *nb,
 }
 #endif
 
-static int ieee80211_napi_poll(struct napi_struct *napi, int budget)
-{
-       struct ieee80211_local *local =
-               container_of(napi, struct ieee80211_local, napi);
-
-       return local->ops->napi_poll(&local->hw, budget);
-}
-
-void ieee80211_napi_schedule(struct ieee80211_hw *hw)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-
-       napi_schedule(&local->napi);
-}
-EXPORT_SYMBOL(ieee80211_napi_schedule);
-
-void ieee80211_napi_complete(struct ieee80211_hw *hw)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-
-       napi_complete(&local->napi);
-}
-EXPORT_SYMBOL(ieee80211_napi_complete);
-
 /* There isn't a lot of sense in it, but you can transmit anything you like */
 static const struct ieee80211_txrx_stypes
 ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
@@ -501,6 +459,27 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
        },
 };
 
+static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = {
+       .vht_cap_info =
+               cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC |
+                           IEEE80211_VHT_CAP_SHORT_GI_80 |
+                           IEEE80211_VHT_CAP_SHORT_GI_160 |
+                           IEEE80211_VHT_CAP_RXSTBC_1 |
+                           IEEE80211_VHT_CAP_RXSTBC_2 |
+                           IEEE80211_VHT_CAP_RXSTBC_3 |
+                           IEEE80211_VHT_CAP_RXSTBC_4 |
+                           IEEE80211_VHT_CAP_TXSTBC |
+                           IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+                           IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+                           IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN |
+                           IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
+                           IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK),
+       .supp_mcs = {
+               .rx_mcs_map = cpu_to_le16(~0),
+               .tx_mcs_map = cpu_to_le16(~0),
+       },
+};
+
 static const u8 extended_capabilities[] = {
        0, 0, 0, 0, 0, 0, 0,
        WLAN_EXT_CAPA8_OPMODE_NOTIF,
@@ -572,7 +551,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
                           NL80211_FEATURE_SAE |
                           NL80211_FEATURE_HT_IBSS |
-                          NL80211_FEATURE_VIF_TXPOWER;
+                          NL80211_FEATURE_VIF_TXPOWER |
+                          NL80211_FEATURE_USERSPACE_MPM;
 
        if (!ops->hw_scan)
                wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
@@ -609,6 +589,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
                                         IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
        local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
        wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
+       wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;
 
        INIT_LIST_HEAD(&local->interfaces);
 
@@ -664,9 +645,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        skb_queue_head_init(&local->skb_queue);
        skb_queue_head_init(&local->skb_queue_unreliable);
 
-       /* init dummy netdev for use w/ NAPI */
-       init_dummy_netdev(&local->napi_dev);
-
        ieee80211_led_names(local);
 
        ieee80211_roc_setup(local);
@@ -1021,9 +999,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                goto fail_ifa6;
 #endif
 
-       netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll,
-                       local->hw.napi_weight);
-
        return 0;
 
 #if IS_ENABLED(CONFIG_IPV6)
index 4749b3858695e6c2aeae983146d6a055be562a28..123a300cef574de7b695399a413549f1d0fb3f5a 100644 (file)
 #include "ieee80211_i.h"
 #include "mesh.h"
 
-#define TMR_RUNNING_HK 0
-#define TMR_RUNNING_MP 1
-#define TMR_RUNNING_MPR        2
-
 static int mesh_allocated;
 static struct kmem_cache *rm_cache;
 
@@ -50,11 +46,6 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
 
        set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
 
-       if (local->quiescing) {
-               set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
-               return;
-       }
-
        ieee80211_queue_work(&local->hw, &sdata->work);
 }
 
@@ -165,7 +156,7 @@ void mesh_sta_cleanup(struct sta_info *sta)
         * an update.
         */
        changed = mesh_accept_plinks_update(sdata);
-       if (sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
+       if (!sdata->u.mesh.user_mpm) {
                changed |= mesh_plink_deactivate(sta);
                del_timer_sync(&sta->plink_timer);
        }
@@ -479,15 +470,8 @@ static void ieee80211_mesh_path_timer(unsigned long data)
 {
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
-       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       struct ieee80211_local *local = sdata->local;
-
-       if (local->quiescing) {
-               set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
-               return;
-       }
 
-       ieee80211_queue_work(&local->hw, &sdata->work);
+       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
 
 static void ieee80211_mesh_path_root_timer(unsigned long data)
@@ -495,16 +479,10 @@ static void ieee80211_mesh_path_root_timer(unsigned long data)
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       struct ieee80211_local *local = sdata->local;
 
        set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
 
-       if (local->quiescing) {
-               set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running);
-               return;
-       }
-
-       ieee80211_queue_work(&local->hw, &sdata->work);
+       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
 
 void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh)
@@ -622,35 +600,6 @@ static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata)
                  round_jiffies(TU_TO_EXP_TIME(interval)));
 }
 
-#ifdef CONFIG_PM
-void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-
-       /* use atomic bitops in case all timers fire at the same time */
-
-       if (del_timer_sync(&ifmsh->housekeeping_timer))
-               set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
-       if (del_timer_sync(&ifmsh->mesh_path_timer))
-               set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
-       if (del_timer_sync(&ifmsh->mesh_path_root_timer))
-               set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running);
-}
-
-void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-
-       if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running))
-               add_timer(&ifmsh->housekeeping_timer);
-       if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running))
-               add_timer(&ifmsh->mesh_path_timer);
-       if (test_and_clear_bit(TMR_RUNNING_MPR, &ifmsh->timers_running))
-               add_timer(&ifmsh->mesh_path_root_timer);
-       ieee80211_mesh_root_setup(ifmsh);
-}
-#endif
-
 static int
 ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 {
@@ -750,10 +699,8 @@ out_free:
 static int
 ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh)
 {
-       struct ieee80211_sub_if_data *sdata;
        struct beacon_data *old_bcn;
        int ret;
-       sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
 
        mutex_lock(&ifmsh->mtx);
 
@@ -871,8 +818,6 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        local->fif_other_bss--;
        atomic_dec(&local->iff_allmultis);
        ieee80211_configure_filter(local);
-
-       sdata->u.mesh.timers_running = 0;
 }
 
 static void
@@ -886,9 +831,8 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_mgmt *hdr;
        struct ieee802_11_elems elems;
        size_t baselen;
-       u8 *pos, *end;
+       u8 *pos;
 
-       end = ((u8 *) mgmt) + len;
        pos = mgmt->u.probe_req.variable;
        baselen = (u8 *) pos - (u8 *) mgmt;
        if (baselen > len)
index 336c88a166871d8e2e6570e15875991d556d2d18..6ffabbe99c4633989b2a45ab7f8bf18c376b75c2 100644 (file)
@@ -313,8 +313,6 @@ void mesh_path_timer(unsigned long data);
 void mesh_path_flush_by_nexthop(struct sta_info *sta);
 void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata,
                             struct sk_buff *skb);
-void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
-void mesh_path_restart(struct ieee80211_sub_if_data *sdata);
 void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
 
 bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
@@ -359,22 +357,12 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
 
 void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
 
-void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
-void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
-void mesh_plink_quiesce(struct sta_info *sta);
-void mesh_plink_restart(struct sta_info *sta);
 void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata);
 void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata);
 void ieee80211s_stop(void);
 #else
 static inline void
 ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
-static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
-{}
-static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
-{}
-static inline void mesh_plink_quiesce(struct sta_info *sta) {}
-static inline void mesh_plink_restart(struct sta_info *sta) {}
 static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
 { return false; }
 static inline void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata)
index 07d396d57079f921b8d314968a581d3a4fed2400..937e06fe8f2ac87f0bd640e650877f097b8aa8e5 100644 (file)
@@ -420,7 +420,6 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
                return NULL;
 
        sta->plink_state = NL80211_PLINK_LISTEN;
-       init_timer(&sta->plink_timer);
 
        sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
        sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
@@ -437,8 +436,9 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
 {
        struct sta_info *sta = NULL;
 
-       /* Userspace handles peer allocation when security is enabled */
-       if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
+       /* Userspace handles station allocation */
+       if (sdata->u.mesh.user_mpm ||
+           sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
                cfg80211_notify_new_peer_candidate(sdata->dev, addr,
                                                   elems->ie_start,
                                                   elems->total_len,
@@ -534,10 +534,8 @@ static void mesh_plink_timer(unsigned long data)
         */
        sta = (struct sta_info *) data;
 
-       if (sta->sdata->local->quiescing) {
-               sta->plink_timer_was_running = true;
+       if (sta->sdata->local->quiescing)
                return;
-       }
 
        spin_lock_bh(&sta->lock);
        if (sta->ignore_plink_timer) {
@@ -598,29 +596,6 @@ static void mesh_plink_timer(unsigned long data)
        }
 }
 
-#ifdef CONFIG_PM
-void mesh_plink_quiesce(struct sta_info *sta)
-{
-       if (!ieee80211_vif_is_mesh(&sta->sdata->vif))
-               return;
-
-       /* no kernel mesh sta timers have been initialized */
-       if (sta->sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
-               return;
-
-       if (del_timer_sync(&sta->plink_timer))
-               sta->plink_timer_was_running = true;
-}
-
-void mesh_plink_restart(struct sta_info *sta)
-{
-       if (sta->plink_timer_was_running) {
-               add_timer(&sta->plink_timer);
-               sta->plink_timer_was_running = false;
-       }
-}
-#endif
-
 static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
 {
        sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
@@ -695,6 +670,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
        if (len < IEEE80211_MIN_ACTION_SIZE + 3)
                return;
 
+       if (sdata->u.mesh.user_mpm)
+               /* userspace must register for these */
+               return;
+
        if (is_multicast_ether_addr(mgmt->da)) {
                mpl_dbg(sdata,
                        "Mesh plink: ignore frame from multicast address\n");
index 346ad4cfb01323cf471758dd5a6fcbdb12b338a6..dec42ab1fa916a75a4aff2b77690710adb2ba5c9 100644 (file)
@@ -87,9 +87,6 @@ MODULE_PARM_DESC(probe_wait_ms,
  */
 #define IEEE80211_SIGNAL_AVE_MIN_COUNT 4
 
-#define TMR_RUNNING_TIMER      0
-#define TMR_RUNNING_CHANSW     1
-
 /*
  * All cfg80211 functions have to be called outside a locked
  * section so that they can acquire a lock themselves... This
@@ -609,6 +606,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
        BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
 
        memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+       ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
 
        /* determine capability flags */
        cap = vht_cap.cap;
@@ -1011,6 +1009,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
 
        /* XXX: wait for a beacon first? */
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
  out:
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
@@ -1038,14 +1037,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
 {
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       if (sdata->local->quiescing) {
-               set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
-               return;
-       }
-
-       ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
+       ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
 }
 
 void
@@ -1116,6 +1109,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        if (sw_elem->mode)
                ieee80211_stop_queues_by_reason(&sdata->local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
 
        if (sdata->local->ops->channel_switch) {
@@ -1383,6 +1377,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
        }
 
        ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_PS);
 }
 
@@ -1444,7 +1439,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                else {
                        ieee80211_send_nullfunc(local, sdata, 1);
                        /* Flush to get the tx status of nullfunc frame */
-                       drv_flush(local, false);
+                       ieee80211_flush_queues(local, sdata);
                }
        }
 
@@ -1775,7 +1770,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */
        if (tx)
-               drv_flush(local, false);
+               ieee80211_flush_queues(local, sdata);
 
        /* deauthenticate/disassociate now */
        if (tx || frame_buf)
@@ -1784,7 +1779,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        /* flush out frame */
        if (tx)
-               drv_flush(local, false);
+               ieee80211_flush_queues(local, sdata);
 
        /* clear bssid only after building the needed mgmt frames */
        memset(ifmgd->bssid, 0, ETH_ALEN);
@@ -1802,9 +1797,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        sdata->vif.bss_conf.p2p_ctwindow = 0;
        sdata->vif.bss_conf.p2p_oppps = false;
 
-       /* on the next assoc, re-program HT parameters */
+       /* on the next assoc, re-program HT/VHT parameters */
        memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
        memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
+       memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
+       memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
 
        sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
 
@@ -1830,8 +1827,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        del_timer_sync(&sdata->u.mgd.timer);
        del_timer_sync(&sdata->u.mgd.chswitch_timer);
 
-       sdata->u.mgd.timers_running = 0;
-
        sdata->vif.bss_conf.dtim_period = 0;
 
        ifmgd->flags = 0;
@@ -1956,7 +1951,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
        run_again(ifmgd, ifmgd->probe_timeout);
        if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
-               drv_flush(sdata->local, false);
+               ieee80211_flush_queues(sdata->local, sdata);
 }
 
 static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@ -2079,6 +2074,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                               true, frame_buf);
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&ifmgd->mtx);
 
@@ -3140,15 +3136,8 @@ static void ieee80211_sta_timer(unsigned long data)
 {
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_local *local = sdata->local;
-
-       if (local->quiescing) {
-               set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
-               return;
-       }
 
-       ieee80211_queue_work(&local->hw, &sdata->work);
+       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
 
 static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
@@ -3500,72 +3489,6 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
        }
 }
 
-#ifdef CONFIG_PM
-void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-       /*
-        * Stop timers before deleting work items, as timers
-        * could race and re-add the work-items. They will be
-        * re-established on connection.
-        */
-       del_timer_sync(&ifmgd->conn_mon_timer);
-       del_timer_sync(&ifmgd->bcn_mon_timer);
-
-       /*
-        * we need to use atomic bitops for the running bits
-        * only because both timers might fire at the same
-        * time -- the code here is properly synchronised.
-        */
-
-       cancel_work_sync(&ifmgd->request_smps_work);
-
-       cancel_work_sync(&ifmgd->monitor_work);
-       cancel_work_sync(&ifmgd->beacon_connection_loss_work);
-       cancel_work_sync(&ifmgd->csa_connection_drop_work);
-       if (del_timer_sync(&ifmgd->timer))
-               set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
-
-       if (del_timer_sync(&ifmgd->chswitch_timer))
-               set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
-       cancel_work_sync(&ifmgd->chswitch_work);
-}
-
-void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-       mutex_lock(&ifmgd->mtx);
-       if (!ifmgd->associated) {
-               mutex_unlock(&ifmgd->mtx);
-               return;
-       }
-
-       if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
-               sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
-               mlme_dbg(sdata, "driver requested disconnect after resume\n");
-               ieee80211_sta_connection_lost(sdata,
-                                             ifmgd->associated->bssid,
-                                             WLAN_REASON_UNSPECIFIED,
-                                             true);
-               mutex_unlock(&ifmgd->mtx);
-               return;
-       }
-       mutex_unlock(&ifmgd->mtx);
-
-       if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
-               add_timer(&ifmgd->timer);
-       if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
-               add_timer(&ifmgd->chswitch_timer);
-       ieee80211_sta_reset_beacon_monitor(sdata);
-
-       mutex_lock(&sdata->local->mtx);
-       ieee80211_restart_sta_timer(sdata);
-       mutex_unlock(&sdata->local->mtx);
-}
-#endif
-
 /* interface setup */
 void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
 {
@@ -4089,6 +4012,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
        }
 
+       if (req->flags & ASSOC_REQ_DISABLE_VHT)
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+
        /* Also disable HT if we don't support it or the AP doesn't use WMM */
        sband = local->hw.wiphy->bands[req->bss->channel->band];
        if (!sband->ht_cap.ht_supported ||
@@ -4112,6 +4038,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,
               sizeof(ifmgd->ht_capa_mask));
 
+       memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa));
+       memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask,
+              sizeof(ifmgd->vht_capa_mask));
+
        if (req->ie && req->ie_len) {
                memcpy(assoc_data->ie, req->ie, req->ie_len);
                assoc_data->ie_len = req->ie_len;
index 430bd254e49693bf7e62744843fff8c10a2acb9a..cce795871ab1fa2373a7828d4c9872695bb0ef1e 100644 (file)
@@ -118,9 +118,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
         * Stop queues and transmit all frames queued by the driver
         * before sending nullfunc to enable powersave at the AP.
         */
-       ieee80211_stop_queues_by_reason(&local->hw,
+       ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
-       drv_flush(local, false);
+       ieee80211_flush_queues(local, NULL);
 
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry(sdata, &local->interfaces, list) {
@@ -181,7 +181,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
        }
        mutex_unlock(&local->iflist_mtx);
 
-       ieee80211_wake_queues_by_reason(&local->hw,
+       ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
 }
 
@@ -277,7 +277,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
                        duration = 10;
 
                ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
-                                           duration);
+                                           duration, roc->type);
 
                roc->started = true;
 
@@ -382,7 +382,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)
                ieee80211_roc_notify_destroy(roc, !roc->abort);
 
                if (started) {
-                       drv_flush(local, false);
+                       ieee80211_flush_queues(local, NULL);
 
                        local->tmp_channel = NULL;
                        ieee80211_hw_config(local, 0);
index d0275f34bf700397702f0f752d15388cc515d281..3d16f4e6174388f27e030f9612a64bbd752c04ea 100644 (file)
@@ -6,32 +6,11 @@
 #include "driver-ops.h"
 #include "led.h"
 
-/* return value indicates whether the driver should be further notified */
-static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
-{
-       switch (sdata->vif.type) {
-       case NL80211_IFTYPE_STATION:
-               ieee80211_sta_quiesce(sdata);
-               break;
-       case NL80211_IFTYPE_ADHOC:
-               ieee80211_ibss_quiesce(sdata);
-               break;
-       case NL80211_IFTYPE_MESH_POINT:
-               ieee80211_mesh_quiesce(sdata);
-               break;
-       default:
-               break;
-       }
-
-       cancel_work_sync(&sdata->work);
-}
-
 int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
-       struct ieee80211_chanctx *ctx;
 
        if (!local->open_count)
                goto suspend;
@@ -51,12 +30,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        }
 
        ieee80211_stop_queues_by_reason(hw,
-                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
        /* flush out all packets */
        synchronize_net();
 
-       drv_flush(local, false);
+       ieee80211_flush_queues(local, NULL);
 
        local->quiescing = true;
        /* make quiescing visible to timers everywhere */
@@ -89,23 +69,17 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                                mutex_unlock(&local->sta_mtx);
                        }
                        ieee80211_wake_queues_by_reason(hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
                        return err;
                } else if (err > 0) {
                        WARN_ON(err != 1);
-                       local->wowlan = false;
+                       return err;
                } else {
-                       list_for_each_entry(sdata, &local->interfaces, list)
-                               if (ieee80211_sdata_running(sdata))
-                                       ieee80211_quiesce(sdata);
                        goto suspend;
                }
        }
 
-       /* disable keys */
-       list_for_each_entry(sdata, &local->interfaces, list)
-               ieee80211_disable_keys(sdata);
-
        /* tear down aggregation sessions and remove STAs */
        mutex_lock(&local->sta_mtx);
        list_for_each_entry(sta, &local->sta_list, list) {
@@ -117,100 +91,25 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                                WARN_ON(drv_sta_state(local, sta->sdata, sta,
                                                      state, state - 1));
                }
-
-               mesh_plink_quiesce(sta);
        }
        mutex_unlock(&local->sta_mtx);
 
        /* remove all interfaces */
        list_for_each_entry(sdata, &local->interfaces, list) {
-               static u8 zero_addr[ETH_ALEN] = {};
-               u32 changed = 0;
-
                if (!ieee80211_sdata_running(sdata))
                        continue;
-
-               switch (sdata->vif.type) {
-               case NL80211_IFTYPE_AP_VLAN:
-               case NL80211_IFTYPE_MONITOR:
-                       /* skip these */
-                       continue;
-               case NL80211_IFTYPE_STATION:
-                       if (sdata->vif.bss_conf.assoc)
-                               changed = BSS_CHANGED_ASSOC |
-                                         BSS_CHANGED_BSSID |
-                                         BSS_CHANGED_IDLE;
-                       break;
-               case NL80211_IFTYPE_AP:
-               case NL80211_IFTYPE_ADHOC:
-               case NL80211_IFTYPE_MESH_POINT:
-                       if (sdata->vif.bss_conf.enable_beacon)
-                               changed = BSS_CHANGED_BEACON_ENABLED;
-                       break;
-               default:
-                       break;
-               }
-
-               ieee80211_quiesce(sdata);
-
-               sdata->suspend_bss_conf = sdata->vif.bss_conf;
-               memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf));
-               sdata->vif.bss_conf.idle = true;
-               if (sdata->suspend_bss_conf.bssid)
-                       sdata->vif.bss_conf.bssid = zero_addr;
-
-               /* disable beaconing or remove association */
-               ieee80211_bss_info_change_notify(sdata, changed);
-
-               if (sdata->vif.type == NL80211_IFTYPE_AP &&
-                   rcu_access_pointer(sdata->u.ap.beacon))
-                       drv_stop_ap(local, sdata);
-
-               if (local->use_chanctx) {
-                       struct ieee80211_chanctx_conf *conf;
-
-                       mutex_lock(&local->chanctx_mtx);
-                       conf = rcu_dereference_protected(
-                                       sdata->vif.chanctx_conf,
-                                       lockdep_is_held(&local->chanctx_mtx));
-                       if (conf) {
-                               ctx = container_of(conf,
-                                                  struct ieee80211_chanctx,
-                                                  conf);
-                               drv_unassign_vif_chanctx(local, sdata, ctx);
-                       }
-
-                       mutex_unlock(&local->chanctx_mtx);
-               }
                drv_remove_interface(local, sdata);
        }
 
        sdata = rtnl_dereference(local->monitor_sdata);
-       if (sdata) {
-               if (local->use_chanctx) {
-                       struct ieee80211_chanctx_conf *conf;
-
-                       mutex_lock(&local->chanctx_mtx);
-                       conf = rcu_dereference_protected(
-                                       sdata->vif.chanctx_conf,
-                                       lockdep_is_held(&local->chanctx_mtx));
-                       if (conf) {
-                               ctx = container_of(conf,
-                                                  struct ieee80211_chanctx,
-                                                  conf);
-                               drv_unassign_vif_chanctx(local, sdata, ctx);
-                       }
-
-                       mutex_unlock(&local->chanctx_mtx);
-               }
-
+       if (sdata)
                drv_remove_interface(local, sdata);
-       }
 
-       mutex_lock(&local->chanctx_mtx);
-       list_for_each_entry(ctx, &local->chanctx_list, list)
-               drv_remove_chanctx(local, ctx);
-       mutex_unlock(&local->chanctx_mtx);
+       /*
+        * We disconnected on all interfaces before suspend, all channel
+        * contexts should be released.
+        */
+       WARN_ON(!list_empty(&local->chanctx_list));
 
        /* stop hardware - this must stop RX */
        if (local->open_count)
index eea45a2c7c35aee3bf4a30ed809f9504cb82a547..1c36c9b4fa4a8f72689bcbf057edfbfa656f1d70 100644 (file)
@@ -55,7 +55,6 @@
 #include "rate.h"
 #include "rc80211_minstrel.h"
 
-#define SAMPLE_COLUMNS 10
 #define SAMPLE_TBL(_mi, _idx, _col) \
                _mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col]
 
@@ -70,16 +69,31 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix)
        return i;
 }
 
+/* find & sort topmost throughput rates */
+static inline void
+minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list)
+{
+       int j = MAX_THR_RATES;
+
+       while (j > 0 && mi->r[i].cur_tp > mi->r[tp_list[j - 1]].cur_tp)
+               j--;
+       if (j < MAX_THR_RATES - 1)
+               memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1));
+       if (j < MAX_THR_RATES)
+               tp_list[j] = i;
+}
+
 static void
 minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
 {
-       u32 max_tp = 0, index_max_tp = 0, index_max_tp2 = 0;
-       u32 max_prob = 0, index_max_prob = 0;
+       u8 tmp_tp_rate[MAX_THR_RATES];
+       u8 tmp_prob_rate = 0;
        u32 usecs;
-       u32 p;
        int i;
 
-       mi->stats_update = jiffies;
+       for (i=0; i < MAX_THR_RATES; i++)
+           tmp_tp_rate[i] = 0;
+
        for (i = 0; i < mi->n_rates; i++) {
                struct minstrel_rate *mr = &mi->r[i];
 
@@ -87,27 +101,32 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
                if (!usecs)
                        usecs = 1000000;
 
-               /* To avoid rounding issues, probabilities scale from 0 (0%)
-                * to 18000 (100%) */
-               if (mr->attempts) {
-                       p = (mr->success * 18000) / mr->attempts;
+               if (unlikely(mr->attempts > 0)) {
+                       mr->sample_skipped = 0;
+                       mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts);
                        mr->succ_hist += mr->success;
                        mr->att_hist += mr->attempts;
-                       mr->cur_prob = p;
-                       p = ((p * (100 - mp->ewma_level)) + (mr->probability *
-                               mp->ewma_level)) / 100;
-                       mr->probability = p;
-                       mr->cur_tp = p * (1000000 / usecs);
-               }
+                       mr->probability = minstrel_ewma(mr->probability,
+                                                       mr->cur_prob,
+                                                       EWMA_LEVEL);
+               } else
+                       mr->sample_skipped++;
 
                mr->last_success = mr->success;
                mr->last_attempts = mr->attempts;
                mr->success = 0;
                mr->attempts = 0;
 
+               /* Update throughput per rate, reset thr. below 10% success */
+               if (mr->probability < MINSTREL_FRAC(10, 100))
+                       mr->cur_tp = 0;
+               else
+                       mr->cur_tp = mr->probability * (1000000 / usecs);
+
                /* Sample less often below the 10% chance of success.
                 * Sample less often above the 95% chance of success. */
-               if ((mr->probability > 17100) || (mr->probability < 1800)) {
+               if (mr->probability > MINSTREL_FRAC(95, 100) ||
+                   mr->probability < MINSTREL_FRAC(10, 100)) {
                        mr->adjusted_retry_count = mr->retry_count >> 1;
                        if (mr->adjusted_retry_count > 2)
                                mr->adjusted_retry_count = 2;
@@ -118,35 +137,30 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
                }
                if (!mr->adjusted_retry_count)
                        mr->adjusted_retry_count = 2;
-       }
 
-       for (i = 0; i < mi->n_rates; i++) {
-               struct minstrel_rate *mr = &mi->r[i];
-               if (max_tp < mr->cur_tp) {
-                       index_max_tp = i;
-                       max_tp = mr->cur_tp;
-               }
-               if (max_prob < mr->probability) {
-                       index_max_prob = i;
-                       max_prob = mr->probability;
+               minstrel_sort_best_tp_rates(mi, i, tmp_tp_rate);
+
+               /* To determine the most robust rate (max_prob_rate) used at
+                * 3rd mmr stage we distinct between two cases:
+                * (1) if any success probabilitiy >= 95%, out of those rates
+                * choose the maximum throughput rate as max_prob_rate
+                * (2) if all success probabilities < 95%, the rate with
+                * highest success probability is choosen as max_prob_rate */
+               if (mr->probability >= MINSTREL_FRAC(95,100)) {
+                       if (mr->cur_tp >= mi->r[tmp_prob_rate].cur_tp)
+                               tmp_prob_rate = i;
+               } else {
+                       if (mr->probability >= mi->r[tmp_prob_rate].probability)
+                               tmp_prob_rate = i;
                }
        }
 
-       max_tp = 0;
-       for (i = 0; i < mi->n_rates; i++) {
-               struct minstrel_rate *mr = &mi->r[i];
-
-               if (i == index_max_tp)
-                       continue;
+       /* Assign the new rate set */
+       memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));
+       mi->max_prob_rate = tmp_prob_rate;
 
-               if (max_tp < mr->cur_tp) {
-                       index_max_tp2 = i;
-                       max_tp = mr->cur_tp;
-               }
-       }
-       mi->max_tp_rate = index_max_tp;
-       mi->max_tp_rate2 = index_max_tp2;
-       mi->max_prob_rate = index_max_prob;
+       /* Reset update timer */
+       mi->stats_update = jiffies;
 }
 
 static void
@@ -207,10 +221,10 @@ static int
 minstrel_get_next_sample(struct minstrel_sta_info *mi)
 {
        unsigned int sample_ndx;
-       sample_ndx = SAMPLE_TBL(mi, mi->sample_idx, mi->sample_column);
-       mi->sample_idx++;
-       if ((int) mi->sample_idx > (mi->n_rates - 2)) {
-               mi->sample_idx = 0;
+       sample_ndx = SAMPLE_TBL(mi, mi->sample_row, mi->sample_column);
+       mi->sample_row++;
+       if ((int) mi->sample_row >= mi->n_rates) {
+               mi->sample_row = 0;
                mi->sample_column++;
                if (mi->sample_column >= SAMPLE_COLUMNS)
                        mi->sample_column = 0;
@@ -228,31 +242,37 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
        struct minstrel_priv *mp = priv;
        struct ieee80211_tx_rate *ar = info->control.rates;
        unsigned int ndx, sample_ndx = 0;
-       bool mrr;
-       bool sample_slower = false;
-       bool sample = false;
+       bool mrr_capable;
+       bool indirect_rate_sampling = false;
+       bool rate_sampling = false;
        int i, delta;
        int mrr_ndx[3];
-       int sample_rate;
+       int sampling_ratio;
 
+       /* management/no-ack frames do not use rate control */
        if (rate_control_send_low(sta, priv_sta, txrc))
                return;
 
-       mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
-
-       ndx = mi->max_tp_rate;
-
-       if (mrr)
-               sample_rate = mp->lookaround_rate_mrr;
+       /* check multi-rate-retry capabilities & adjust lookaround_rate */
+       mrr_capable = mp->has_mrr &&
+                     !txrc->rts &&
+                     !txrc->bss_conf->use_cts_prot;
+       if (mrr_capable)
+               sampling_ratio = mp->lookaround_rate_mrr;
        else
-               sample_rate = mp->lookaround_rate;
+               sampling_ratio = mp->lookaround_rate;
+
+       /* init rateindex [ndx] with max throughput rate */
+       ndx = mi->max_tp_rate[0];
 
+       /* increase sum packet counter */
        mi->packet_count++;
-       delta = (mi->packet_count * sample_rate / 100) -
+
+       delta = (mi->packet_count * sampling_ratio / 100) -
                        (mi->sample_count + mi->sample_deferred / 2);
 
        /* delta > 0: sampling required */
-       if ((delta > 0) && (mrr || !mi->prev_sample)) {
+       if ((delta > 0) && (mrr_capable || !mi->prev_sample)) {
                struct minstrel_rate *msr;
                if (mi->packet_count >= 10000) {
                        mi->sample_deferred = 0;
@@ -271,21 +291,28 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
                        mi->sample_count += (delta - mi->n_rates * 2);
                }
 
+               /* get next random rate sample */
                sample_ndx = minstrel_get_next_sample(mi);
                msr = &mi->r[sample_ndx];
-               sample = true;
-               sample_slower = mrr && (msr->perfect_tx_time >
-                       mi->r[ndx].perfect_tx_time);
-
-               if (!sample_slower) {
+               rate_sampling = true;
+
+               /* Decide if direct ( 1st mrr stage) or indirect (2nd mrr stage)
+                * rate sampling method should be used.
+                * Respect such rates that are not sampled for 20 interations.
+                */
+               if (mrr_capable &&
+                   msr->perfect_tx_time > mi->r[ndx].perfect_tx_time &&
+                   msr->sample_skipped < 20)
+                               indirect_rate_sampling = true;
+
+               if (!indirect_rate_sampling) {
                        if (msr->sample_limit != 0) {
                                ndx = sample_ndx;
                                mi->sample_count++;
                                if (msr->sample_limit > 0)
                                        msr->sample_limit--;
-                       } else {
-                               sample = false;
-                       }
+                       } else
+                               rate_sampling = false;
                } else {
                        /* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark
                         * packets that have the sampling rate deferred to the
@@ -297,34 +324,39 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
                        mi->sample_deferred++;
                }
        }
-       mi->prev_sample = sample;
+       mi->prev_sample = rate_sampling;
 
        /* If we're not using MRR and the sampling rate already
         * has a probability of >95%, we shouldn't be attempting
         * to use it, as this only wastes precious airtime */
-       if (!mrr && sample && (mi->r[ndx].probability > 17100))
-               ndx = mi->max_tp_rate;
+       if (!mrr_capable && rate_sampling &&
+          (mi->r[ndx].probability > MINSTREL_FRAC(95, 100)))
+               ndx = mi->max_tp_rate[0];
 
+       /* mrr setup for 1st stage */
        ar[0].idx = mi->r[ndx].rix;
        ar[0].count = minstrel_get_retry_count(&mi->r[ndx], info);
 
-       if (!mrr) {
-               if (!sample)
+       /* non mrr setup for 2nd stage */
+       if (!mrr_capable) {
+               if (!rate_sampling)
                        ar[0].count = mp->max_retry;
                ar[1].idx = mi->lowest_rix;
                ar[1].count = mp->max_retry;
                return;
        }
 
-       /* MRR setup */
-       if (sample) {
-               if (sample_slower)
+       /* mrr setup for 2nd stage */
+       if (rate_sampling) {
+               if (indirect_rate_sampling)
                        mrr_ndx[0] = sample_ndx;
                else
-                       mrr_ndx[0] = mi->max_tp_rate;
+                       mrr_ndx[0] = mi->max_tp_rate[0];
        } else {
-               mrr_ndx[0] = mi->max_tp_rate2;
+               mrr_ndx[0] = mi->max_tp_rate[1];
        }
+
+       /* mrr setup for 3rd & 4th stage */
        mrr_ndx[1] = mi->max_prob_rate;
        mrr_ndx[2] = 0;
        for (i = 1; i < 4; i++) {
@@ -351,26 +383,21 @@ static void
 init_sample_table(struct minstrel_sta_info *mi)
 {
        unsigned int i, col, new_idx;
-       unsigned int n_srates = mi->n_rates - 1;
        u8 rnd[8];
 
        mi->sample_column = 0;
-       mi->sample_idx = 0;
-       memset(mi->sample_table, 0, SAMPLE_COLUMNS * mi->n_rates);
+       mi->sample_row = 0;
+       memset(mi->sample_table, 0xff, SAMPLE_COLUMNS * mi->n_rates);
 
        for (col = 0; col < SAMPLE_COLUMNS; col++) {
-               for (i = 0; i < n_srates; i++) {
+               for (i = 0; i < mi->n_rates; i++) {
                        get_random_bytes(rnd, sizeof(rnd));
-                       new_idx = (i + rnd[i & 7]) % n_srates;
+                       new_idx = (i + rnd[i & 7]) % mi->n_rates;
 
-                       while (SAMPLE_TBL(mi, new_idx, col) != 0)
-                               new_idx = (new_idx + 1) % n_srates;
+                       while (SAMPLE_TBL(mi, new_idx, col) != 0xff)
+                               new_idx = (new_idx + 1) % mi->n_rates;
 
-                       /* Don't sample the slowest rate (i.e. slowest base
-                        * rate). We must presume that the slowest rate works
-                        * fine, or else other management frames will also be
-                        * failing and the link will break */
-                       SAMPLE_TBL(mi, new_idx, col) = i + 1;
+                       SAMPLE_TBL(mi, new_idx, col) = i;
                }
        }
 }
@@ -542,9 +569,6 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
        mp->lookaround_rate = 5;
        mp->lookaround_rate_mrr = 10;
 
-       /* moving average weight for EWMA */
-       mp->ewma_level = 75;
-
        /* maximum time that the hw is allowed to stay in one MRR segment */
        mp->segment_size = 6000;
 
index 5ecf757817f289f76eee112093fae345c7f4cc0f..85ebf42cb46de4e11a032a496025c9f0ea7be375 100644 (file)
@@ -9,6 +9,28 @@
 #ifndef __RC_MINSTREL_H
 #define __RC_MINSTREL_H
 
+#define EWMA_LEVEL     75      /* ewma weighting factor [%] */
+#define SAMPLE_COLUMNS 10      /* number of columns in sample table */
+
+
+/* scaled fraction values */
+#define MINSTREL_SCALE  16
+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
+#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
+
+/* number of highest throughput rates to consider*/
+#define MAX_THR_RATES 4
+
+/*
+ * Perform EWMA (Exponentially Weighted Moving Average) calculation
+  */
+static inline int
+minstrel_ewma(int old, int new, int weight)
+{
+       return (new * (100 - weight) + old * weight) / 100;
+}
+
+
 struct minstrel_rate {
        int bitrate;
        int rix;
@@ -26,6 +48,7 @@ struct minstrel_rate {
        u32 attempts;
        u32 last_attempts;
        u32 last_success;
+       u8 sample_skipped;
 
        /* parts per thousand */
        u32 cur_prob;
@@ -45,14 +68,13 @@ struct minstrel_sta_info {
 
        unsigned int lowest_rix;
 
-       unsigned int max_tp_rate;
-       unsigned int max_tp_rate2;
-       unsigned int max_prob_rate;
+       u8 max_tp_rate[MAX_THR_RATES];
+       u8 max_prob_rate;
        unsigned int packet_count;
        unsigned int sample_count;
        int sample_deferred;
 
-       unsigned int sample_idx;
+       unsigned int sample_row;
        unsigned int sample_column;
 
        int n_rates;
@@ -73,7 +95,6 @@ struct minstrel_priv {
        unsigned int cw_min;
        unsigned int cw_max;
        unsigned int max_retry;
-       unsigned int ewma_level;
        unsigned int segment_size;
        unsigned int update_interval;
        unsigned int lookaround_rate;
index d5a56226e675970c3a396df9d73b22135dabc39e..d1048348d399697306993b47c45988ef8602f96b 100644 (file)
@@ -73,15 +73,17 @@ minstrel_stats_open(struct inode *inode, struct file *file)
        for (i = 0; i < mi->n_rates; i++) {
                struct minstrel_rate *mr = &mi->r[i];
 
-               *(p++) = (i == mi->max_tp_rate) ? 'T' : ' ';
-               *(p++) = (i == mi->max_tp_rate2) ? 't' : ' ';
+               *(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' ';
+               *(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' ';
+               *(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' ';
+               *(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';
                *(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';
                p += sprintf(p, "%3u%s", mr->bitrate / 2,
                                (mr->bitrate & 1 ? ".5" : "  "));
 
-               tp = mr->cur_tp / ((18000 << 10) / 96);
-               prob = mr->cur_prob / 18;
-               eprob = mr->probability / 18;
+               tp = MINSTREL_TRUNC(mr->cur_tp / 10);
+               prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
+               eprob = MINSTREL_TRUNC(mr->probability * 1000);
 
                p += sprintf(p, "  %6u.%1u   %6u.%1u   %6u.%1u        "
                                "%3u(%3u)   %8llu    %8llu\n",
index 3af141c6971214e9b1bb29b2976e858d86585da6..d2b264d1311d25a337fc267b69f931b996e8e07a 100644 (file)
@@ -17,8 +17,6 @@
 #include "rc80211_minstrel_ht.h"
 
 #define AVG_PKT_SIZE   1200
-#define SAMPLE_COLUMNS 10
-#define EWMA_LEVEL             75
 
 /* Number of bits for an average sized packet */
 #define MCS_NBITS (AVG_PKT_SIZE << 3)
 /* Number of symbols for a packet with (bps) bits per symbol */
 #define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps))
 
-/* Transmission time for a packet containing (syms) symbols */
+/* Transmission time (nanoseconds) for a packet containing (syms) symbols */
 #define MCS_SYMBOL_TIME(sgi, syms)                                     \
        (sgi ?                                                          \
-         ((syms) * 18 + 4) / 5 :       /* syms * 3.6 us */             \
-         (syms) << 2                   /* syms * 4 us */               \
+         ((syms) * 18000 + 4000) / 5 : /* syms * 3.6 us */             \
+         ((syms) * 1000) << 2          /* syms * 4 us */               \
        )
 
 /* Transmit duration for the raw data part of an average sized packet */
@@ -64,9 +62,9 @@
 }
 
 #define CCK_DURATION(_bitrate, _short, _len)           \
-       (10 /* SIFS */ +                                \
+       (1000 * (10 /* SIFS */ +                        \
         (_short ? 72 + 24 : 144 + 48 ) +               \
-        (8 * (_len + 4) * 10) / (_bitrate))
+        (8 * (_len + 4) * 10) / (_bitrate)))
 
 #define CCK_ACK_DURATION(_bitrate, _short)                     \
        (CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) +   \
@@ -128,15 +126,6 @@ const struct mcs_group minstrel_mcs_groups[] = {
 
 static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES];
 
-/*
- * Perform EWMA (Exponentially Weighted Moving Average) calculation
- */
-static int
-minstrel_ewma(int old, int new, int weight)
-{
-       return (new * (100 - weight) + old * weight) / 100;
-}
-
 /*
  * Look up an MCS group index based on mac80211 rate information
  */
@@ -211,20 +200,32 @@ static void
 minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
 {
        struct minstrel_rate_stats *mr;
-       unsigned int usecs = 0;
+       unsigned int nsecs = 0;
+       unsigned int tp;
+       unsigned int prob;
 
        mr = &mi->groups[group].rates[rate];
+       prob = mr->probability;
 
-       if (mr->probability < MINSTREL_FRAC(1, 10)) {
+       if (prob < MINSTREL_FRAC(1, 10)) {
                mr->cur_tp = 0;
                return;
        }
 
+       /*
+        * For the throughput calculation, limit the probability value to 90% to
+        * account for collision related packet error rate fluctuation
+        */
+       if (prob > MINSTREL_FRAC(9, 10))
+               prob = MINSTREL_FRAC(9, 10);
+
        if (group != MINSTREL_CCK_GROUP)
-               usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
+               nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
+
+       nsecs += minstrel_mcs_groups[group].duration[rate];
+       tp = 1000000 * ((mr->probability * 1000) / nsecs);
 
-       usecs += minstrel_mcs_groups[group].duration[rate];
-       mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability);
+       mr->cur_tp = MINSTREL_TRUNC(tp);
 }
 
 /*
@@ -308,8 +309,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
                }
        }
 
-       /* try to sample up to half of the available rates during each interval */
-       mi->sample_count *= 4;
+       /* try to sample all available rates during each interval */
+       mi->sample_count *= 8;
 
        cur_prob = 0;
        cur_prob_tp = 0;
@@ -320,20 +321,13 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
                if (!mg->supported)
                        continue;
 
-               mr = minstrel_get_ratestats(mi, mg->max_prob_rate);
-               if (cur_prob_tp < mr->cur_tp &&
-                   minstrel_mcs_groups[group].streams == 1) {
-                       mi->max_prob_rate = mg->max_prob_rate;
-                       cur_prob = mr->cur_prob;
-                       cur_prob_tp = mr->cur_tp;
-               }
-
                mr = minstrel_get_ratestats(mi, mg->max_tp_rate);
                if (cur_tp < mr->cur_tp) {
                        mi->max_tp_rate2 = mi->max_tp_rate;
                        cur_tp2 = cur_tp;
                        mi->max_tp_rate = mg->max_tp_rate;
                        cur_tp = mr->cur_tp;
+                       mi->max_prob_streams = minstrel_mcs_groups[group].streams - 1;
                }
 
                mr = minstrel_get_ratestats(mi, mg->max_tp_rate2);
@@ -343,6 +337,23 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
                }
        }
 
+       if (mi->max_prob_streams < 1)
+               mi->max_prob_streams = 1;
+
+       for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
+               mg = &mi->groups[group];
+               if (!mg->supported)
+                       continue;
+               mr = minstrel_get_ratestats(mi, mg->max_prob_rate);
+               if (cur_prob_tp < mr->cur_tp &&
+                   minstrel_mcs_groups[group].streams <= mi->max_prob_streams) {
+                       mi->max_prob_rate = mg->max_prob_rate;
+                       cur_prob = mr->cur_prob;
+                       cur_prob_tp = mr->cur_tp;
+               }
+       }
+
+
        mi->stats_update = jiffies;
 }
 
@@ -467,7 +478,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
 
        if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) {
                mi->sample_wait = 16 + 2 * MINSTREL_TRUNC(mi->avg_ampdu_len);
-               mi->sample_tries = 2;
+               mi->sample_tries = 1;
                mi->sample_count--;
        }
 
@@ -536,7 +547,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        mr->retry_updated = true;
 
        group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
-       tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len;
+       tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000;
 
        /* Contention time for first 2 tries */
        ctime = (t_slot * cw) >> 1;
@@ -616,6 +627,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 {
        struct minstrel_rate_stats *mr;
        struct minstrel_mcs_group_data *mg;
+       unsigned int sample_dur, sample_group;
        int sample_idx = 0;
 
        if (mi->sample_wait > 0) {
@@ -626,39 +638,46 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
        if (!mi->sample_tries)
                return -1;
 
-       mi->sample_tries--;
        mg = &mi->groups[mi->sample_group];
        sample_idx = sample_table[mg->column][mg->index];
        mr = &mg->rates[sample_idx];
-       sample_idx += mi->sample_group * MCS_GROUP_RATES;
+       sample_group = mi->sample_group;
+       sample_idx += sample_group * MCS_GROUP_RATES;
        minstrel_next_sample_idx(mi);
 
        /*
         * Sampling might add some overhead (RTS, no aggregation)
         * to the frame. Hence, don't use sampling for the currently
-        * used max TP rate.
+        * used rates.
         */
-       if (sample_idx == mi->max_tp_rate)
+       if (sample_idx == mi->max_tp_rate ||
+           sample_idx == mi->max_tp_rate2 ||
+           sample_idx == mi->max_prob_rate)
                return -1;
+
        /*
-        * When not using MRR, do not sample if the probability is already
-        * higher than 95% to avoid wasting airtime
+        * Do not sample if the probability is already higher than 95%
+        * to avoid wasting airtime.
         */
-       if (!mp->has_mrr && (mr->probability > MINSTREL_FRAC(95, 100)))
+       if (mr->probability > MINSTREL_FRAC(95, 100))
                return -1;
 
        /*
         * Make sure that lower rates get sampled only occasionally,
         * if the link is working perfectly.
         */
-       if (minstrel_get_duration(sample_idx) >
-           minstrel_get_duration(mi->max_tp_rate)) {
+       sample_dur = minstrel_get_duration(sample_idx);
+       if (sample_dur >= minstrel_get_duration(mi->max_tp_rate2) &&
+           (mi->max_prob_streams <
+            minstrel_mcs_groups[sample_group].streams ||
+            sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {
                if (mr->sample_skipped < 20)
                        return -1;
 
                if (mi->sample_slow++ > 2)
                        return -1;
        }
+       mi->sample_tries--;
 
        return sample_idx;
 }
index 302dbd52180d273c7616720310aa9dcf1b6f00ef..9b16e9de9923758fe52c49279027f1fbe0b81462 100644 (file)
 #define MINSTREL_MAX_STREAMS   3
 #define MINSTREL_STREAM_GROUPS 4
 
-/* scaled fraction values */
-#define MINSTREL_SCALE 16
-#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
-#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
-
 #define MCS_GROUP_RATES        8
 
 struct mcs_group {
@@ -85,6 +80,7 @@ struct minstrel_ht_sta {
 
        /* best probability rate */
        unsigned int max_prob_rate;
+       unsigned int max_prob_streams;
 
        /* time of last status update */
        unsigned long stats_update;
index c6844ad080beee0123969c3aca15b7ca349eddda..2528b5a4d6d42b208e29287bbf3458a3554a0af8 100644 (file)
@@ -648,24 +648,6 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
        return RX_CONTINUE;
 }
 
-#define SEQ_MODULO 0x1000
-#define SEQ_MASK   0xfff
-
-static inline int seq_less(u16 sq1, u16 sq2)
-{
-       return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
-}
-
-static inline u16 seq_inc(u16 sq)
-{
-       return (sq + 1) & SEQ_MASK;
-}
-
-static inline u16 seq_sub(u16 sq1, u16 sq2)
-{
-       return (sq1 - sq2) & SEQ_MASK;
-}
-
 static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
                                            struct tid_ampdu_rx *tid_agg_rx,
                                            int index,
@@ -687,7 +669,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
        __skb_queue_tail(frames, skb);
 
 no_frame:
-       tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
+       tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
 }
 
 static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata,
@@ -699,8 +681,9 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata
 
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
-       while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
-               index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
+       while (ieee80211_sn_less(tid_agg_rx->head_seq_num, head_seq_num)) {
+               index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
+                                        tid_agg_rx->ssn) %
                                                        tid_agg_rx->buf_size;
                ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
                                                frames);
@@ -727,8 +710,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
        /* release the buffer until next missing frame */
-       index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
-                                               tid_agg_rx->buf_size;
+       index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
+                                tid_agg_rx->ssn) % tid_agg_rx->buf_size;
        if (!tid_agg_rx->reorder_buf[index] &&
            tid_agg_rx->stored_mpdu_num) {
                /*
@@ -756,19 +739,22 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                         * Increment the head seq# also for the skipped slots.
                         */
                        tid_agg_rx->head_seq_num =
-                               (tid_agg_rx->head_seq_num + skipped) & SEQ_MASK;
+                               (tid_agg_rx->head_seq_num +
+                                skipped) & IEEE80211_SN_MASK;
                        skipped = 0;
                }
        } else while (tid_agg_rx->reorder_buf[index]) {
                ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
                                                frames);
-               index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
+               index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
+                                        tid_agg_rx->ssn) %
                                                        tid_agg_rx->buf_size;
        }
 
        if (tid_agg_rx->stored_mpdu_num) {
-               j = index = seq_sub(tid_agg_rx->head_seq_num,
-                                   tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+               j = index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
+                                            tid_agg_rx->ssn) %
+                                                       tid_agg_rx->buf_size;
 
                for (; j != (index - 1) % tid_agg_rx->buf_size;
                     j = (j + 1) % tid_agg_rx->buf_size) {
@@ -809,7 +795,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
        head_seq_num = tid_agg_rx->head_seq_num;
 
        /* frame with out of date sequence number */
-       if (seq_less(mpdu_seq_num, head_seq_num)) {
+       if (ieee80211_sn_less(mpdu_seq_num, head_seq_num)) {
                dev_kfree_skb(skb);
                goto out;
        }
@@ -818,8 +804,9 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
         * If frame the sequence number exceeds our buffering window
         * size release some previous frames to make room for this one.
         */
-       if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) {
-               head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size));
+       if (!ieee80211_sn_less(mpdu_seq_num, head_seq_num + buf_size)) {
+               head_seq_num = ieee80211_sn_inc(
+                               ieee80211_sn_sub(mpdu_seq_num, buf_size));
                /* release stored frames up to new head to stack */
                ieee80211_release_reorder_frames(sdata, tid_agg_rx,
                                                 head_seq_num, frames);
@@ -827,7 +814,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
 
        /* Now the new frame is always in the range of the reordering buffer */
 
-       index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+       index = ieee80211_sn_sub(mpdu_seq_num,
+                                tid_agg_rx->ssn) % tid_agg_rx->buf_size;
 
        /* check if we already stored this frame */
        if (tid_agg_rx->reorder_buf[index]) {
@@ -843,7 +831,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
         */
        if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
            tid_agg_rx->stored_mpdu_num == 0) {
-               tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
+               tid_agg_rx->head_seq_num =
+                       ieee80211_sn_inc(tid_agg_rx->head_seq_num);
                ret = false;
                goto out;
        }
@@ -1894,8 +1883,10 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                 * 'align' will only take the values 0 or 2 here
                 * since all frames are required to be aligned
                 * to 2-byte boundaries when being passed to
-                * mac80211. That also explains the __skb_push()
-                * below.
+                * mac80211; the code here works just as well if
+                * that isn't true, but mac80211 assumes it can
+                * access fields as 2-byte aligned (e.g. for
+                * compare_ether_addr)
                 */
                align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;
                if (align) {
@@ -2552,7 +2543,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                case WLAN_SP_MESH_PEERING_CONFIRM:
                        if (!ieee80211_vif_is_mesh(&sdata->vif))
                                goto invalid;
-                       if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
+                       if (sdata->u.mesh.user_mpm)
                                /* userspace handles this frame */
                                break;
                        goto queue;
index 43a45cf00e06803b091a350694621d87150ee46f..cb34cbbaa20ce9f003b22f17e5def519d83873af 100644 (file)
@@ -153,7 +153,6 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
        u8 *elements;
        struct ieee80211_channel *channel;
        size_t baselen;
-       bool beacon;
        struct ieee802_11_elems elems;
 
        if (skb->len < 24 ||
@@ -175,11 +174,9 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
 
                elements = mgmt->u.probe_resp.variable;
                baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
-               beacon = false;
        } else {
                baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
                elements = mgmt->u.beacon.variable;
-               beacon = true;
        }
 
        if (baselen > skb->len)
@@ -335,7 +332,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
        ieee80211_offchannel_stop_vifs(local);
 
        /* ensure nullfunc is transmitted before leaving operating channel */
-       drv_flush(local, false);
+       ieee80211_flush_queues(local, NULL);
 
        ieee80211_configure_filter(local);
 
@@ -671,7 +668,7 @@ static void ieee80211_scan_state_resume(struct ieee80211_local *local,
        ieee80211_offchannel_stop_vifs(local);
 
        if (local->ops->flush) {
-               drv_flush(local, false);
+               ieee80211_flush_queues(local, NULL);
                *next_delay = 0;
        } else
                *next_delay = HZ / 10;
index 238a0cca320e621dad86cbca1830e4867459cb6d..11216bc13b27565a4734638014ad01f5d2d88911 100644 (file)
@@ -342,6 +342,11 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
+#ifdef CONFIG_MAC80211_MESH
+       if (ieee80211_vif_is_mesh(&sdata->vif) &&
+           !sdata->u.mesh.user_mpm)
+               init_timer(&sta->plink_timer);
+#endif
 
        memcpy(sta->sta.addr, addr, ETH_ALEN);
        sta->local = local;
@@ -551,6 +556,15 @@ static inline void __bss_tim_clear(u8 *tim, u16 id)
        tim[id / 8] &= ~(1 << (id % 8));
 }
 
+static inline bool __bss_tim_get(u8 *tim, u16 id)
+{
+       /*
+        * This format has been mandated by the IEEE specifications,
+        * so this line may not be changed to use the test_bit() format.
+        */
+       return tim[id / 8] & (1 << (id % 8));
+}
+
 static unsigned long ieee80211_tids_for_ac(int ac)
 {
        /* If we ever support TIDs > 7, this obviously needs to be adjusted */
@@ -631,6 +645,9 @@ void sta_info_recalc_tim(struct sta_info *sta)
  done:
        spin_lock_bh(&local->tim_lock);
 
+       if (indicate_tim == __bss_tim_get(ps->tim, id))
+               goto out_unlock;
+
        if (indicate_tim)
                __bss_tim_set(ps->tim, id);
        else
@@ -642,6 +659,7 @@ void sta_info_recalc_tim(struct sta_info *sta)
                local->tim_in_locked_section = false;
        }
 
+out_unlock:
        spin_unlock_bh(&local->tim_lock);
 }
 
@@ -765,8 +783,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
 {
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
-       int ret, i;
-       bool have_key = false;
+       int ret;
 
        might_sleep();
 
@@ -793,19 +810,8 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
 
        list_del_rcu(&sta->list);
 
-       mutex_lock(&local->key_mtx);
-       for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
-               __ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i]));
-               have_key = true;
-       }
-       if (sta->ptk) {
-               __ieee80211_key_free(key_mtx_dereference(local, sta->ptk));
-               have_key = true;
-       }
-       mutex_unlock(&local->key_mtx);
-
-       if (!have_key)
-               synchronize_net();
+       /* this always calls synchronize_net() */
+       ieee80211_free_sta_keys(local, sta);
 
        sta->dead = true;
 
@@ -1391,30 +1397,16 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_sta_block_awake);
 
-void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta)
+void ieee80211_sta_eosp(struct ieee80211_sta *pubsta)
 {
        struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
        struct ieee80211_local *local = sta->local;
-       struct sk_buff *skb;
-       struct skb_eosp_msg_data *data;
 
        trace_api_eosp(local, pubsta);
 
-       skb = alloc_skb(0, GFP_ATOMIC);
-       if (!skb) {
-               /* too bad ... but race is better than loss */
-               clear_sta_flag(sta, WLAN_STA_SP);
-               return;
-       }
-
-       data = (void *)skb->cb;
-       memcpy(data->sta, pubsta->addr, ETH_ALEN);
-       memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN);
-       skb->pkt_type = IEEE80211_EOSP_MSG;
-       skb_queue_tail(&local->skb_queue, skb);
-       tasklet_schedule(&local->tasklet);
+       clear_sta_flag(sta, WLAN_STA_SP);
 }
-EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe);
+EXPORT_SYMBOL(ieee80211_sta_eosp);
 
 void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
                                u8 tid, bool buffered)
index 4947341a2a82419bb2e5938fd4d41f46bd821ac0..adc30045f99ec9f029c0bd8cfc0a4d7c75b10403 100644 (file)
@@ -281,7 +281,6 @@ struct sta_ampdu_mlme {
  * @plink_state: peer link state
  * @plink_timeout: timeout of peer link
  * @plink_timer: peer link watch timer
- * @plink_timer_was_running: used by suspend/resume to restore timers
  * @t_offset: timing offset relative to this host
  * @t_offset_setpoint: reference timing offset of this sta to be used when
  *     calculating clockdrift
@@ -334,7 +333,8 @@ struct sta_info {
        unsigned long driver_buffered_tids;
 
        /* Updated from RX path only, no locking requirements */
-       unsigned long rx_packets, rx_bytes;
+       unsigned long rx_packets;
+       u64 rx_bytes;
        unsigned long wep_weak_iv_count;
        unsigned long last_rx;
        long last_connected;
@@ -354,9 +354,9 @@ struct sta_info {
        unsigned int fail_avg;
 
        /* Updated from TX path only, no locking requirements */
-       unsigned long tx_packets;
-       unsigned long tx_bytes;
-       unsigned long tx_fragments;
+       u32 tx_fragments;
+       u64 tx_packets[IEEE80211_NUM_ACS];
+       u64 tx_bytes[IEEE80211_NUM_ACS];
        struct ieee80211_tx_rate last_tx_rate;
        int last_rx_rate_idx;
        u32 last_rx_rate_flag;
@@ -379,7 +379,6 @@ struct sta_info {
        __le16 reason;
        u8 plink_retries;
        bool ignore_plink_timer;
-       bool plink_timer_was_running;
        enum nl80211_plink_state plink_state;
        u32 plink_timeout;
        struct timer_list plink_timer;
index 3d7cd2a0582f4e467504d296c8150b258cc7dd91..c5899797a8d4b0f01da791da6e2b1be8a685627f 100644 (file)
@@ -431,6 +431,30 @@ TRACE_EVENT(drv_prepare_multicast,
        )
 );
 
+TRACE_EVENT(drv_set_multicast_list,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata, int mc_count),
+
+       TP_ARGS(local, sdata, mc_count),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(bool, allmulti)
+               __field(int, mc_count)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI;
+               __entry->mc_count = mc_count;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT " configure mc filter, count=%d, allmulti=%d",
+               LOCAL_PR_ARG, __entry->mc_count, __entry->allmulti
+       )
+);
+
 TRACE_EVENT(drv_configure_filter,
        TP_PROTO(struct ieee80211_local *local,
                 unsigned int changed_flags,
@@ -940,23 +964,26 @@ TRACE_EVENT(drv_get_survey,
 );
 
 TRACE_EVENT(drv_flush,
-       TP_PROTO(struct ieee80211_local *local, bool drop),
+       TP_PROTO(struct ieee80211_local *local,
+                u32 queues, bool drop),
 
-       TP_ARGS(local, drop),
+       TP_ARGS(local, queues, drop),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
                __field(bool, drop)
+               __field(u32, queues)
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
                __entry->drop = drop;
+               __entry->queues = queues;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT " drop:%d",
-               LOCAL_PR_ARG, __entry->drop
+               LOCAL_PR_FMT " queues:0x%x drop:%d",
+               LOCAL_PR_ARG, __entry->queues, __entry->drop
        )
 );
 
@@ -1042,15 +1069,17 @@ TRACE_EVENT(drv_remain_on_channel,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
                 struct ieee80211_channel *chan,
-                unsigned int duration),
+                unsigned int duration,
+                enum ieee80211_roc_type type),
 
-       TP_ARGS(local, sdata, chan, duration),
+       TP_ARGS(local, sdata, chan, duration, type),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
                VIF_ENTRY
                __field(int, center_freq)
                __field(unsigned int, duration)
+               __field(u32, type)
        ),
 
        TP_fast_assign(
@@ -1058,12 +1087,13 @@ TRACE_EVENT(drv_remain_on_channel,
                VIF_ASSIGN;
                __entry->center_freq = chan->center_freq;
                __entry->duration = duration;
+               __entry->type = type;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT  VIF_PR_FMT " freq:%dMHz duration:%dms",
+               LOCAL_PR_FMT  VIF_PR_FMT " freq:%dMHz duration:%dms type=%d",
                LOCAL_PR_ARG, VIF_PR_ARG,
-               __entry->center_freq, __entry->duration
+               __entry->center_freq, __entry->duration, __entry->type
        )
 );
 
index 8914d2d2881aab77c5741479d6dbf516db59e3b3..9e67cc97b87bec9826ceef5795f8a72d90457458 100644 (file)
@@ -233,6 +233,7 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
 
        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
                ieee80211_stop_queues_by_reason(&local->hw,
+                                               IEEE80211_MAX_QUEUE_MAP,
                                                IEEE80211_QUEUE_STOP_REASON_PS);
                ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
                ieee80211_queue_work(&local->hw,
@@ -991,15 +992,18 @@ static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
 {
        struct sk_buff *skb;
+       int ac = -1;
 
        if (!tx->sta)
                return TX_CONTINUE;
 
-       tx->sta->tx_packets++;
        skb_queue_walk(&tx->skbs, skb) {
+               ac = skb_get_queue_mapping(skb);
                tx->sta->tx_fragments++;
-               tx->sta->tx_bytes += skb->len;
+               tx->sta->tx_bytes[ac] += skb->len;
        }
+       if (ac >= 0)
+               tx->sta->tx_packets[ac]++;
 
        return TX_CONTINUE;
 }
@@ -2085,7 +2089,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                encaps_data = bridge_tunnel_header;
                encaps_len = sizeof(bridge_tunnel_header);
                skip_header_bytes -= 2;
-       } else if (ethertype >= 0x600) {
+       } else if (ethertype >= ETH_P_802_3_MIN) {
                encaps_data = rfc1042_header;
                encaps_len = sizeof(rfc1042_header);
                skip_header_bytes -= 2;
index 0f38f43ac62eac316434c60614694d41efed09ff..a7368870c8ee10cf7ee48dc764bc466974eac8ae 100644 (file)
@@ -453,7 +453,8 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
 }
 
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
-                                   enum queue_stop_reason reason)
+                                    unsigned long queues,
+                                    enum queue_stop_reason reason)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
@@ -461,7 +462,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
-       for (i = 0; i < hw->queues; i++)
+       for_each_set_bit(i, &queues, hw->queues)
                __ieee80211_stop_queue(hw, i, reason);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -469,7 +470,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
 
 void ieee80211_stop_queues(struct ieee80211_hw *hw)
 {
-       ieee80211_stop_queues_by_reason(hw,
+       ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_stop_queues);
@@ -491,6 +492,7 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
 EXPORT_SYMBOL(ieee80211_queue_stopped);
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+                                    unsigned long queues,
                                     enum queue_stop_reason reason)
 {
        struct ieee80211_local *local = hw_to_local(hw);
@@ -499,7 +501,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
-       for (i = 0; i < hw->queues; i++)
+       for_each_set_bit(i, &queues, hw->queues)
                __ieee80211_wake_queue(hw, i, reason);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -507,10 +509,42 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 
 void ieee80211_wake_queues(struct ieee80211_hw *hw)
 {
-       ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER);
+       ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
+void ieee80211_flush_queues(struct ieee80211_local *local,
+                           struct ieee80211_sub_if_data *sdata)
+{
+       u32 queues;
+
+       if (!local->ops->flush)
+               return;
+
+       if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) {
+               int ac;
+
+               queues = 0;
+
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       queues |= BIT(sdata->vif.hw_queue[ac]);
+               if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE)
+                       queues |= BIT(sdata->vif.cab_queue);
+       } else {
+               /* all queues */
+               queues = BIT(local->hw.queues) - 1;
+       }
+
+       ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_FLUSH);
+
+       drv_flush(local, queues, false);
+
+       ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_FLUSH);
+}
+
 void ieee80211_iterate_active_interfaces(
        struct ieee80211_hw *hw, u32 iter_flags,
        void (*iterator)(void *data, u8 *mac,
@@ -1357,6 +1391,25 @@ void ieee80211_stop_device(struct ieee80211_local *local)
        drv_stop(local);
 }
 
+static void ieee80211_assign_chanctx(struct ieee80211_local *local,
+                                    struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *ctx;
+
+       if (!local->use_chanctx)
+               return;
+
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (conf) {
+               ctx = container_of(conf, struct ieee80211_chanctx, conf);
+               drv_assign_vif_chanctx(local, sdata, ctx);
+       }
+       mutex_unlock(&local->chanctx_mtx);
+}
+
 int ieee80211_reconfig(struct ieee80211_local *local)
 {
        struct ieee80211_hw *hw = &local->hw;
@@ -1445,36 +1498,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        }
 
        list_for_each_entry(sdata, &local->interfaces, list) {
-               struct ieee80211_chanctx_conf *ctx_conf;
-
                if (!ieee80211_sdata_running(sdata))
                        continue;
-
-               mutex_lock(&local->chanctx_mtx);
-               ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                               lockdep_is_held(&local->chanctx_mtx));
-               if (ctx_conf) {
-                       ctx = container_of(ctx_conf, struct ieee80211_chanctx,
-                                          conf);
-                       drv_assign_vif_chanctx(local, sdata, ctx);
-               }
-               mutex_unlock(&local->chanctx_mtx);
+               ieee80211_assign_chanctx(local, sdata);
        }
 
        sdata = rtnl_dereference(local->monitor_sdata);
-       if (sdata && local->use_chanctx && ieee80211_sdata_running(sdata)) {
-               struct ieee80211_chanctx_conf *ctx_conf;
-
-               mutex_lock(&local->chanctx_mtx);
-               ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                               lockdep_is_held(&local->chanctx_mtx));
-               if (ctx_conf) {
-                       ctx = container_of(ctx_conf, struct ieee80211_chanctx,
-                                          conf);
-                       drv_assign_vif_chanctx(local, sdata, ctx);
-               }
-               mutex_unlock(&local->chanctx_mtx);
-       }
+       if (sdata && ieee80211_sdata_running(sdata))
+               ieee80211_assign_chanctx(local, sdata);
 
        /* add STAs back */
        mutex_lock(&local->sta_mtx);
@@ -1534,11 +1565,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                          BSS_CHANGED_IDLE |
                          BSS_CHANGED_TXPOWER;
 
-#ifdef CONFIG_PM
-               if (local->resuming && !reconfig_due_to_wowlan)
-                       sdata->vif.bss_conf = sdata->suspend_bss_conf;
-#endif
-
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_STATION:
                        changed |= BSS_CHANGED_ASSOC |
@@ -1659,8 +1685,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                mutex_unlock(&local->sta_mtx);
        }
 
-       ieee80211_wake_queues_by_reason(hw,
-                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+       ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
        /*
         * If this is for hw restart things are still running.
@@ -1678,28 +1704,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        mb();
        local->resuming = false;
 
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               switch(sdata->vif.type) {
-               case NL80211_IFTYPE_STATION:
-                       ieee80211_sta_restart(sdata);
-                       break;
-               case NL80211_IFTYPE_ADHOC:
-                       ieee80211_ibss_restart(sdata);
-                       break;
-               case NL80211_IFTYPE_MESH_POINT:
-                       ieee80211_mesh_restart(sdata);
-                       break;
-               default:
-                       break;
-               }
-       }
-
        mod_timer(&local->sta_cleanup, jiffies + 1);
-
-       mutex_lock(&local->sta_mtx);
-       list_for_each_entry(sta, &local->sta_list, list)
-               mesh_plink_restart(sta);
-       mutex_unlock(&local->sta_mtx);
 #else
        WARN_ON(1);
 #endif
index a2c2258bc84ec5b8b0605f4af07660b51a207abe..171344d4eb7c69077edd1047d6c1d5176a74b351 100644 (file)
 #include "rate.h"
 
 
+static void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata,
+                                  struct ieee80211_sta_vht_cap *vht_cap,
+                                  u32 flag)
+{
+       __le32 le_flag = cpu_to_le32(flag);
+
+       if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag &&
+           !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag))
+               vht_cap->cap &= ~flag;
+}
+
+void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
+                                     struct ieee80211_sta_vht_cap *vht_cap)
+{
+       int i;
+       u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n;
+
+       if (!vht_cap->vht_supported)
+               return;
+
+       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               return;
+
+       __check_vhtcap_disable(sdata, vht_cap,
+                              IEEE80211_VHT_CAP_RXLDPC);
+       __check_vhtcap_disable(sdata, vht_cap,
+                              IEEE80211_VHT_CAP_SHORT_GI_80);
+       __check_vhtcap_disable(sdata, vht_cap,
+                              IEEE80211_VHT_CAP_SHORT_GI_160);
+       __check_vhtcap_disable(sdata, vht_cap,
+                              IEEE80211_VHT_CAP_TXSTBC);
+       __check_vhtcap_disable(sdata, vht_cap,
+                              IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
+       __check_vhtcap_disable(sdata, vht_cap,
+                              IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+       __check_vhtcap_disable(sdata, vht_cap,
+                              IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN);
+       __check_vhtcap_disable(sdata, vht_cap,
+                              IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN);
+
+       /* Allow user to decrease AMPDU length exponent */
+       if (sdata->u.mgd.vht_capa_mask.vht_cap_info &
+           cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) {
+               u32 cap, n;
+
+               n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) &
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+               n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+               cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+               cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+               if (n < cap) {
+                       vht_cap->cap &=
+                               ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+                       vht_cap->cap |=
+                               n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+               }
+       }
+
+       /* Allow the user to decrease MCSes */
+       rxmcs_mask =
+               le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map);
+       rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map);
+       rxmcs_n &= rxmcs_mask;
+       rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+
+       txmcs_mask =
+               le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map);
+       txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map);
+       txmcs_n &= txmcs_mask;
+       txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
+       for (i = 0; i < 8; i++) {
+               u8 m, n, c;
+
+               m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+               n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+               c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+               if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) ||
+                         n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) {
+                       rxmcs_cap &= ~(3 << 2*i);
+                       rxmcs_cap |= (rxmcs_n & (3 << 2*i));
+               }
+
+               m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+               n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+               c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+               if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) ||
+                         n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) {
+                       txmcs_cap &= ~(3 << 2*i);
+                       txmcs_cap |= (txmcs_n & (3 << 2*i));
+               }
+       }
+       vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap);
+       vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap);
+}
+
 void
 ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_supported_band *sband,
@@ -20,6 +118,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
                                    struct sta_info *sta)
 {
        struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap;
+       struct ieee80211_sta_vht_cap own_cap;
+       u32 cap_info, i;
 
        memset(vht_cap, 0, sizeof(*vht_cap));
 
@@ -35,12 +135,122 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
 
        vht_cap->vht_supported = true;
 
-       vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info);
+       own_cap = sband->vht_cap;
+       /*
+        * If user has specified capability overrides, take care
+        * of that if the station we're setting up is the AP that
+        * we advertised a restricted capability set to. Override
+        * our own capabilities and then use those below.
+        */
+       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+           !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               ieee80211_apply_vhtcap_overrides(sdata, &own_cap);
+
+       /* take some capabilities as-is */
+       cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info);
+       vht_cap->cap = cap_info;
+       vht_cap->cap &= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 |
+                       IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 |
+                       IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+                       IEEE80211_VHT_CAP_RXLDPC |
+                       IEEE80211_VHT_CAP_VHT_TXOP_PS |
+                       IEEE80211_VHT_CAP_HTC_VHT |
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
+                       IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB |
+                       IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB |
+                       IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
+                       IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
+
+       /* and some based on our own capabilities */
+       switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+       case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+               vht_cap->cap |= cap_info &
+                               IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+               break;
+       case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+               vht_cap->cap |= cap_info &
+                               IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+               break;
+       default:
+               /* nothing */
+               break;
+       }
+
+       /* symmetric capabilities */
+       vht_cap->cap |= cap_info & own_cap.cap &
+                       (IEEE80211_VHT_CAP_SHORT_GI_80 |
+                        IEEE80211_VHT_CAP_SHORT_GI_160);
+
+       /* remaining ones */
+       if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) {
+               vht_cap->cap |= cap_info &
+                               (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+                                IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX |
+                                IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX);
+       }
+
+       if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
+               vht_cap->cap |= cap_info &
+                               IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+
+       if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
+               vht_cap->cap |= cap_info &
+                               IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+       if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
+               vht_cap->cap |= cap_info &
+                               IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+       if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
+               vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK;
+
+       if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
+               vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC;
 
        /* Copy peer MCS info, the driver might need them. */
        memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
               sizeof(struct ieee80211_vht_mcs_info));
 
+       /* but also restrict MCSes */
+       for (i = 0; i < 8; i++) {
+               u16 own_rx, own_tx, peer_rx, peer_tx;
+
+               own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map);
+               own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+               own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map);
+               own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+               peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+               peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+               peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
+               peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+               if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+                       if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+                               peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED;
+                       else if (own_rx < peer_tx)
+                               peer_tx = own_rx;
+               }
+
+               if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+                       if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+                               peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED;
+                       else if (own_tx < peer_rx)
+                               peer_rx = own_tx;
+               }
+
+               vht_cap->vht_mcs.rx_mcs_map &=
+                       ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2);
+               vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2);
+
+               vht_cap->vht_mcs.tx_mcs_map &=
+                       ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2);
+               vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2);
+       }
+
+       /* finally set up the bandwidth */
        switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
        case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
        case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
index a4dcaf1dd4b6e2cf29c47a1f4cc2084fe16ca735..5c9e021994ba89243dd3356dd0e454659d3f85a5 100644 (file)
@@ -88,8 +88,6 @@ struct mac802154_sub_if_data {
 
 #define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw)
 
-#define MAC802154_MAX_XMIT_ATTEMPTS    3
-
 #define MAC802154_CHAN_NONE            (~(u8)0) /* No channel is assigned */
 
 extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced;
@@ -114,5 +112,6 @@ void mac802154_dev_set_ieee_addr(struct net_device *dev);
 u16 mac802154_dev_get_pan_id(const struct net_device *dev);
 void mac802154_dev_set_pan_id(struct net_device *dev, u16 val);
 void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan);
+u8 mac802154_dev_get_dsn(const struct net_device *dev);
 
 #endif /* MAC802154_H */
index d8d2770060899d18d0945c72aa2c83488ee4fa72..a99910d4d52f8403a8c2141b473deba885b977ad 100644 (file)
@@ -73,4 +73,5 @@ struct ieee802154_mlme_ops mac802154_mlme_wpan = {
        .start_req = mac802154_mlme_start_req,
        .get_pan_id = mac802154_dev_get_pan_id,
        .get_short_addr = mac802154_dev_get_short_addr,
+       .get_dsn = mac802154_dev_get_dsn,
 };
index f47781ab0cccbc346bf8dc28f146fbbc49f05100..8ded97cf1c33f4336b3a59761095eff575ba6287 100644 (file)
@@ -159,6 +159,15 @@ void mac802154_dev_set_pan_id(struct net_device *dev, u16 val)
        }
 }
 
+u8 mac802154_dev_get_dsn(const struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       return priv->dsn++;
+}
+
 static void phy_chan_notify(struct work_struct *work)
 {
        struct phy_chan_notify_work *nw = container_of(work,
@@ -167,9 +176,15 @@ static void phy_chan_notify(struct work_struct *work)
        struct mac802154_sub_if_data *priv = netdev_priv(nw->dev);
        int res;
 
+       mutex_lock(&priv->hw->phy->pib_lock);
        res = hw->ops->set_channel(&hw->hw, priv->page, priv->chan);
        if (res)
                pr_debug("set_channel failed\n");
+       else {
+               priv->hw->phy->current_channel = priv->chan;
+               priv->hw->phy->current_page = priv->page;
+       }
+       mutex_unlock(&priv->hw->phy->pib_lock);
 
        kfree(nw);
 }
@@ -186,8 +201,11 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
        priv->chan = chan;
        spin_unlock_bh(&priv->mib_lock);
 
+       mutex_lock(&priv->hw->phy->pib_lock);
        if (priv->hw->phy->current_channel != priv->chan ||
            priv->hw->phy->current_page != priv->page) {
+               mutex_unlock(&priv->hw->phy->pib_lock);
+
                work = kzalloc(sizeof(*work), GFP_ATOMIC);
                if (!work)
                        return;
@@ -195,5 +213,6 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
                INIT_WORK(&work->work, phy_chan_notify);
                work->dev = dev;
                queue_work(priv->hw->dev_workqueue, &work->work);
-       }
+       } else
+               mutex_unlock(&priv->hw->phy->pib_lock);
 }
index 4e09d070995a129a065b33eb15c0c729913b4365..6d1647399d4fefb4584a7bdd2c8ac5adf9b05702 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/if_arp.h>
 #include <linux/crc-ccitt.h>
 
+#include <net/ieee802154_netdev.h>
 #include <net/mac802154.h>
 #include <net/wpan-phy.h>
 
@@ -39,12 +40,12 @@ struct xmit_work {
        struct mac802154_priv *priv;
        u8 chan;
        u8 page;
-       u8 xmit_attempts;
 };
 
 static void mac802154_xmit_worker(struct work_struct *work)
 {
        struct xmit_work *xw = container_of(work, struct xmit_work, work);
+       struct mac802154_sub_if_data *sdata;
        int res;
 
        mutex_lock(&xw->priv->phy->pib_lock);
@@ -57,21 +58,23 @@ static void mac802154_xmit_worker(struct work_struct *work)
                        pr_debug("set_channel failed\n");
                        goto out;
                }
+
+               xw->priv->phy->current_channel = xw->chan;
+               xw->priv->phy->current_page = xw->page;
        }
 
        res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
+       if (res)
+               pr_debug("transmission failed\n");
 
 out:
        mutex_unlock(&xw->priv->phy->pib_lock);
 
-       if (res) {
-               if (xw->xmit_attempts++ < MAC802154_MAX_XMIT_ATTEMPTS) {
-                       queue_work(xw->priv->dev_workqueue, &xw->work);
-                       return;
-               } else
-                       pr_debug("transmission failed for %d times",
-                                MAC802154_MAX_XMIT_ATTEMPTS);
-       }
+       /* Restart the netif queue on each sub_if_data object. */
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &xw->priv->slaves, list)
+               netif_wake_queue(sdata->dev);
+       rcu_read_unlock();
 
        dev_kfree_skb(xw->skb);
 
@@ -82,6 +85,7 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
                         u8 page, u8 chan)
 {
        struct xmit_work *work;
+       struct mac802154_sub_if_data *sdata;
 
        if (!(priv->phy->channels_supported[page] & (1 << chan))) {
                WARN_ON(1);
@@ -109,12 +113,17 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
                return NETDEV_TX_BUSY;
        }
 
+       /* Stop the netif queue on each sub_if_data object. */
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &priv->slaves, list)
+               netif_stop_queue(sdata->dev);
+       rcu_read_unlock();
+
        INIT_WORK(&work->work, mac802154_xmit_worker);
        work->skb = skb;
        work->priv = priv;
        work->page = page;
        work->chan = chan;
-       work->xmit_attempts = 0;
 
        queue_work(priv->dev_workqueue, &work->work);
 
index d20c6d3c247db1b15036977e585e9e039829f3f9..2ca2f4dceab77167dea995132ef730b6981ead2f 100644 (file)
@@ -145,6 +145,8 @@ static int mac802154_header_create(struct sk_buff *skb,
 
        head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */
        fc = mac_cb_type(skb);
+       if (mac_cb_is_ackreq(skb))
+               fc |= IEEE802154_FC_ACK_REQ;
 
        if (!saddr) {
                spin_lock_bh(&priv->mib_lock);
@@ -358,7 +360,7 @@ void mac802154_wpan_setup(struct net_device *dev)
        dev->header_ops         = &mac802154_header_ops;
        dev->needed_tailroom    = 2; /* FCS */
        dev->mtu                = IEEE802154_MTU;
-       dev->tx_queue_len       = 10;
+       dev->tx_queue_len       = 300;
        dev->type               = ARPHRD_IEEE802154;
        dev->flags              = IFF_NOARP | IFF_BROADCAST;
        dev->watchdog_timeo     = 0;
index a9c488b6c50d446e1c9e5276553728af83281fad..07c865a31a3d2adba8933c576f4228b0e8be6e55 100644 (file)
@@ -5,6 +5,7 @@
  * way.
  *
  * Rusty Russell (C)2000 -- This code is GPL.
+ * Patrick McHardy (c) 2006-2012
  */
 #include <linux/kernel.h>
 #include <linux/netfilter.h>
@@ -276,10 +277,30 @@ void (*nf_nat_decode_session_hook)(struct sk_buff *, struct flowi *);
 EXPORT_SYMBOL(nf_nat_decode_session_hook);
 #endif
 
+static int __net_init netfilter_net_init(struct net *net)
+{
 #ifdef CONFIG_PROC_FS
-struct proc_dir_entry *proc_net_netfilter;
-EXPORT_SYMBOL(proc_net_netfilter);
+       net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter",
+                                               net->proc_net);
+       if (!net->nf.proc_netfilter) {
+               if (!net_eq(net, &init_net))
+                       pr_err("cannot create netfilter proc entry");
+
+               return -ENOMEM;
+       }
 #endif
+       return 0;
+}
+
+static void __net_exit netfilter_net_exit(struct net *net)
+{
+       remove_proc_entry("netfilter", net->proc_net);
+}
+
+static struct pernet_operations netfilter_net_ops = {
+       .init = netfilter_net_init,
+       .exit = netfilter_net_exit,
+};
 
 void __init netfilter_init(void)
 {
@@ -289,11 +310,8 @@ void __init netfilter_init(void)
                        INIT_LIST_HEAD(&nf_hooks[i][h]);
        }
 
-#ifdef CONFIG_PROC_FS
-       proc_net_netfilter = proc_mkdir("netfilter", init_net.proc_net);
-       if (!proc_net_netfilter)
+       if (register_pernet_subsys(&netfilter_net_ops) < 0)
                panic("cannot create netfilter proc entry");
-#endif
 
        if (netfilter_log_init() < 0)
                panic("cannot initialize nf_log");
index 1ba9dbc0e107cae96efdf58b4b18d7c4d38d9e6b..86f5e26f39d35f3b2cd0160a82203a5528612472 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/ip.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
-#include <linux/netlink.h>
 #include <linux/rculist.h>
 #include <net/netlink.h>
 
@@ -1085,7 +1084,7 @@ static int
 dump_init(struct netlink_callback *cb)
 {
        struct nlmsghdr *nlh = nlmsg_hdr(cb->skb);
-       int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
+       int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
        struct nlattr *cda[IPSET_ATTR_CMD_MAX+1];
        struct nlattr *attr = (void *)nlh + min_len;
        u32 dump_type;
@@ -1301,7 +1300,7 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
                struct sk_buff *skb2;
                struct nlmsgerr *errmsg;
                size_t payload = sizeof(*errmsg) + nlmsg_len(nlh);
-               int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
+               int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
                struct nlattr *cda[IPSET_ATTR_CMD_MAX+1];
                struct nlattr *cmdattr;
                u32 *errline;
index 0b779d7df8813b1519d9320d72a3b46dccf7c2c3..dfd7b65b3d2a9165eed15c5e8a7b90c1ed30c050 100644 (file)
@@ -58,6 +58,18 @@ static inline void ip_vs_app_put(struct ip_vs_app *app)
        module_put(app->module);
 }
 
+static void ip_vs_app_inc_destroy(struct ip_vs_app *inc)
+{
+       kfree(inc->timeout_table);
+       kfree(inc);
+}
+
+static void ip_vs_app_inc_rcu_free(struct rcu_head *head)
+{
+       struct ip_vs_app *inc = container_of(head, struct ip_vs_app, rcu_head);
+
+       ip_vs_app_inc_destroy(inc);
+}
 
 /*
  *     Allocate/initialize app incarnation and register it in proto apps.
@@ -106,8 +118,7 @@ ip_vs_app_inc_new(struct net *net, struct ip_vs_app *app, __u16 proto,
        return 0;
 
   out:
-       kfree(inc->timeout_table);
-       kfree(inc);
+       ip_vs_app_inc_destroy(inc);
        return ret;
 }
 
@@ -131,8 +142,7 @@ ip_vs_app_inc_release(struct net *net, struct ip_vs_app *inc)
 
        list_del(&inc->a_list);
 
-       kfree(inc->timeout_table);
-       kfree(inc);
+       call_rcu(&inc->rcu_head, ip_vs_app_inc_rcu_free);
 }
 
 
@@ -144,9 +154,9 @@ int ip_vs_app_inc_get(struct ip_vs_app *inc)
 {
        int result;
 
-       atomic_inc(&inc->usecnt);
-       if (unlikely((result = ip_vs_app_get(inc->app)) != 1))
-               atomic_dec(&inc->usecnt);
+       result = ip_vs_app_get(inc->app);
+       if (result)
+               atomic_inc(&inc->usecnt);
        return result;
 }
 
@@ -156,8 +166,8 @@ int ip_vs_app_inc_get(struct ip_vs_app *inc)
  */
 void ip_vs_app_inc_put(struct ip_vs_app *inc)
 {
-       ip_vs_app_put(inc->app);
        atomic_dec(&inc->usecnt);
+       ip_vs_app_put(inc->app);
 }
 
 
@@ -218,6 +228,7 @@ out_unlock:
 /*
  *     ip_vs_app unregistration routine
  *     We are sure there are no app incarnations attached to services
+ *     Caller should use synchronize_rcu() or rcu_barrier()
  */
 void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app)
 {
@@ -341,14 +352,14 @@ static inline void vs_seq_update(struct ip_vs_conn *cp, struct ip_vs_seq *vseq,
                                 unsigned int flag, __u32 seq, int diff)
 {
        /* spinlock is to keep updating cp->flags atomic */
-       spin_lock(&cp->lock);
+       spin_lock_bh(&cp->lock);
        if (!(cp->flags & flag) || after(seq, vseq->init_seq)) {
                vseq->previous_delta = vseq->delta;
                vseq->delta += diff;
                vseq->init_seq = seq;
                cp->flags |= flag;
        }
-       spin_unlock(&cp->lock);
+       spin_unlock_bh(&cp->lock);
 }
 
 static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
index 704e514e02abec92b4676f3747468eb76c8c68a2..de6475894a39e10b04c7892fe53960a92b8ac1b4 100644 (file)
@@ -79,51 +79,21 @@ static unsigned int ip_vs_conn_rnd __read_mostly;
 
 struct ip_vs_aligned_lock
 {
-       rwlock_t        l;
+       spinlock_t      l;
 } __attribute__((__aligned__(SMP_CACHE_BYTES)));
 
 /* lock array for conn table */
 static struct ip_vs_aligned_lock
 __ip_vs_conntbl_lock_array[CT_LOCKARRAY_SIZE] __cacheline_aligned;
 
-static inline void ct_read_lock(unsigned int key)
-{
-       read_lock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
-}
-
-static inline void ct_read_unlock(unsigned int key)
-{
-       read_unlock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
-}
-
-static inline void ct_write_lock(unsigned int key)
-{
-       write_lock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
-}
-
-static inline void ct_write_unlock(unsigned int key)
-{
-       write_unlock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
-}
-
-static inline void ct_read_lock_bh(unsigned int key)
-{
-       read_lock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
-}
-
-static inline void ct_read_unlock_bh(unsigned int key)
-{
-       read_unlock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
-}
-
 static inline void ct_write_lock_bh(unsigned int key)
 {
-       write_lock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
+       spin_lock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
 }
 
 static inline void ct_write_unlock_bh(unsigned int key)
 {
-       write_unlock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
+       spin_unlock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
 }
 
 
@@ -197,13 +167,13 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
        /* Hash by protocol, client address and port */
        hash = ip_vs_conn_hashkey_conn(cp);
 
-       ct_write_lock(hash);
+       ct_write_lock_bh(hash);
        spin_lock(&cp->lock);
 
        if (!(cp->flags & IP_VS_CONN_F_HASHED)) {
-               hlist_add_head(&cp->c_list, &ip_vs_conn_tab[hash]);
                cp->flags |= IP_VS_CONN_F_HASHED;
                atomic_inc(&cp->refcnt);
+               hlist_add_head_rcu(&cp->c_list, &ip_vs_conn_tab[hash]);
                ret = 1;
        } else {
                pr_err("%s(): request for already hashed, called from %pF\n",
@@ -212,7 +182,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
        }
 
        spin_unlock(&cp->lock);
-       ct_write_unlock(hash);
+       ct_write_unlock_bh(hash);
 
        return ret;
 }
@@ -220,7 +190,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
 
 /*
  *     UNhashes ip_vs_conn from ip_vs_conn_tab.
- *     returns bool success.
+ *     returns bool success. Caller should hold conn reference.
  */
 static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
 {
@@ -230,11 +200,11 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
        /* unhash it and decrease its reference counter */
        hash = ip_vs_conn_hashkey_conn(cp);
 
-       ct_write_lock(hash);
+       ct_write_lock_bh(hash);
        spin_lock(&cp->lock);
 
        if (cp->flags & IP_VS_CONN_F_HASHED) {
-               hlist_del(&cp->c_list);
+               hlist_del_rcu(&cp->c_list);
                cp->flags &= ~IP_VS_CONN_F_HASHED;
                atomic_dec(&cp->refcnt);
                ret = 1;
@@ -242,7 +212,37 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
                ret = 0;
 
        spin_unlock(&cp->lock);
-       ct_write_unlock(hash);
+       ct_write_unlock_bh(hash);
+
+       return ret;
+}
+
+/* Try to unlink ip_vs_conn from ip_vs_conn_tab.
+ * returns bool success.
+ */
+static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
+{
+       unsigned int hash;
+       bool ret;
+
+       hash = ip_vs_conn_hashkey_conn(cp);
+
+       ct_write_lock_bh(hash);
+       spin_lock(&cp->lock);
+
+       if (cp->flags & IP_VS_CONN_F_HASHED) {
+               ret = false;
+               /* Decrease refcnt and unlink conn only if we are last user */
+               if (atomic_cmpxchg(&cp->refcnt, 1, 0) == 1) {
+                       hlist_del_rcu(&cp->c_list);
+                       cp->flags &= ~IP_VS_CONN_F_HASHED;
+                       ret = true;
+               }
+       } else
+               ret = atomic_read(&cp->refcnt) ? false : true;
+
+       spin_unlock(&cp->lock);
+       ct_write_unlock_bh(hash);
 
        return ret;
 }
@@ -262,24 +262,25 @@ __ip_vs_conn_in_get(const struct ip_vs_conn_param *p)
 
        hash = ip_vs_conn_hashkey_param(p, false);
 
-       ct_read_lock(hash);
+       rcu_read_lock();
 
-       hlist_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
-               if (cp->af == p->af &&
-                   p->cport == cp->cport && p->vport == cp->vport &&
+       hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
+               if (p->cport == cp->cport && p->vport == cp->vport &&
+                   cp->af == p->af &&
                    ip_vs_addr_equal(p->af, p->caddr, &cp->caddr) &&
                    ip_vs_addr_equal(p->af, p->vaddr, &cp->vaddr) &&
                    ((!p->cport) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) &&
                    p->protocol == cp->protocol &&
                    ip_vs_conn_net_eq(cp, p->net)) {
+                       if (!__ip_vs_conn_get(cp))
+                               continue;
                        /* HIT */
-                       atomic_inc(&cp->refcnt);
-                       ct_read_unlock(hash);
+                       rcu_read_unlock();
                        return cp;
                }
        }
 
-       ct_read_unlock(hash);
+       rcu_read_unlock();
 
        return NULL;
 }
@@ -346,14 +347,16 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p)
 
        hash = ip_vs_conn_hashkey_param(p, false);
 
-       ct_read_lock(hash);
+       rcu_read_lock();
 
-       hlist_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
-               if (!ip_vs_conn_net_eq(cp, p->net))
-                       continue;
-               if (p->pe_data && p->pe->ct_match) {
-                       if (p->pe == cp->pe && p->pe->ct_match(p, cp))
-                               goto out;
+       hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
+               if (unlikely(p->pe_data && p->pe->ct_match)) {
+                       if (!ip_vs_conn_net_eq(cp, p->net))
+                               continue;
+                       if (p->pe == cp->pe && p->pe->ct_match(p, cp)) {
+                               if (__ip_vs_conn_get(cp))
+                                       goto out;
+                       }
                        continue;
                }
 
@@ -363,17 +366,18 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p)
                     * p->vaddr is a fwmark */
                    ip_vs_addr_equal(p->protocol == IPPROTO_IP ? AF_UNSPEC :
                                     p->af, p->vaddr, &cp->vaddr) &&
-                   p->cport == cp->cport && p->vport == cp->vport &&
+                   p->vport == cp->vport && p->cport == cp->cport &&
                    cp->flags & IP_VS_CONN_F_TEMPLATE &&
-                   p->protocol == cp->protocol)
-                       goto out;
+                   p->protocol == cp->protocol &&
+                   ip_vs_conn_net_eq(cp, p->net)) {
+                       if (__ip_vs_conn_get(cp))
+                               goto out;
+               }
        }
        cp = NULL;
 
   out:
-       if (cp)
-               atomic_inc(&cp->refcnt);
-       ct_read_unlock(hash);
+       rcu_read_unlock();
 
        IP_VS_DBG_BUF(9, "template lookup/in %s %s:%d->%s:%d %s\n",
                      ip_vs_proto_name(p->protocol),
@@ -398,23 +402,24 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
         */
        hash = ip_vs_conn_hashkey_param(p, true);
 
-       ct_read_lock(hash);
+       rcu_read_lock();
 
-       hlist_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
-               if (cp->af == p->af &&
-                   p->vport == cp->cport && p->cport == cp->dport &&
+       hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
+               if (p->vport == cp->cport && p->cport == cp->dport &&
+                   cp->af == p->af &&
                    ip_vs_addr_equal(p->af, p->vaddr, &cp->caddr) &&
                    ip_vs_addr_equal(p->af, p->caddr, &cp->daddr) &&
                    p->protocol == cp->protocol &&
                    ip_vs_conn_net_eq(cp, p->net)) {
+                       if (!__ip_vs_conn_get(cp))
+                               continue;
                        /* HIT */
-                       atomic_inc(&cp->refcnt);
                        ret = cp;
                        break;
                }
        }
 
-       ct_read_unlock(hash);
+       rcu_read_unlock();
 
        IP_VS_DBG_BUF(9, "lookup/out %s %s:%d->%s:%d %s\n",
                      ip_vs_proto_name(p->protocol),
@@ -457,13 +462,13 @@ void ip_vs_conn_put(struct ip_vs_conn *cp)
 void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
 {
        if (ip_vs_conn_unhash(cp)) {
-               spin_lock(&cp->lock);
+               spin_lock_bh(&cp->lock);
                if (cp->flags & IP_VS_CONN_F_NO_CPORT) {
                        atomic_dec(&ip_vs_conn_no_cport_cnt);
                        cp->flags &= ~IP_VS_CONN_F_NO_CPORT;
                        cp->cport = cport;
                }
-               spin_unlock(&cp->lock);
+               spin_unlock_bh(&cp->lock);
 
                /* hash on new dport */
                ip_vs_conn_hash(cp);
@@ -549,7 +554,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
                return;
 
        /* Increase the refcnt counter of the dest */
-       atomic_inc(&dest->refcnt);
+       ip_vs_dest_hold(dest);
 
        conn_flags = atomic_read(&dest->conn_flags);
        if (cp->protocol != IPPROTO_UDP)
@@ -606,20 +611,22 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
  * Check if there is a destination for the connection, if so
  * bind the connection to the destination.
  */
-struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
+void ip_vs_try_bind_dest(struct ip_vs_conn *cp)
 {
        struct ip_vs_dest *dest;
 
+       rcu_read_lock();
        dest = ip_vs_find_dest(ip_vs_conn_net(cp), cp->af, &cp->daddr,
                               cp->dport, &cp->vaddr, cp->vport,
                               cp->protocol, cp->fwmark, cp->flags);
        if (dest) {
                struct ip_vs_proto_data *pd;
 
-               spin_lock(&cp->lock);
+               spin_lock_bh(&cp->lock);
                if (cp->dest) {
-                       spin_unlock(&cp->lock);
-                       return dest;
+                       spin_unlock_bh(&cp->lock);
+                       rcu_read_unlock();
+                       return;
                }
 
                /* Applications work depending on the forwarding method
@@ -628,7 +635,7 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
                        ip_vs_unbind_app(cp);
 
                ip_vs_bind_dest(cp, dest);
-               spin_unlock(&cp->lock);
+               spin_unlock_bh(&cp->lock);
 
                /* Update its packet transmitter */
                cp->packet_xmit = NULL;
@@ -643,7 +650,7 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
                if (pd && atomic_read(&pd->appcnt))
                        ip_vs_bind_app(cp, pd->pp);
        }
-       return dest;
+       rcu_read_unlock();
 }
 
 
@@ -695,12 +702,7 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
                        dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
        }
 
-       /*
-        * Simply decrease the refcnt of the dest, because the
-        * dest will be either in service's destination list
-        * or in the trash.
-        */
-       atomic_dec(&dest->refcnt);
+       ip_vs_dest_put(dest);
 }
 
 static int expire_quiescent_template(struct netns_ipvs *ipvs,
@@ -757,41 +759,36 @@ int ip_vs_check_template(struct ip_vs_conn *ct)
                 * Simply decrease the refcnt of the template,
                 * don't restart its timer.
                 */
-               atomic_dec(&ct->refcnt);
+               __ip_vs_conn_put(ct);
                return 0;
        }
        return 1;
 }
 
+static void ip_vs_conn_rcu_free(struct rcu_head *head)
+{
+       struct ip_vs_conn *cp = container_of(head, struct ip_vs_conn,
+                                            rcu_head);
+
+       ip_vs_pe_put(cp->pe);
+       kfree(cp->pe_data);
+       kmem_cache_free(ip_vs_conn_cachep, cp);
+}
+
 static void ip_vs_conn_expire(unsigned long data)
 {
        struct ip_vs_conn *cp = (struct ip_vs_conn *)data;
        struct net *net = ip_vs_conn_net(cp);
        struct netns_ipvs *ipvs = net_ipvs(net);
 
-       cp->timeout = 60*HZ;
-
-       /*
-        *      hey, I'm using it
-        */
-       atomic_inc(&cp->refcnt);
-
        /*
         *      do I control anybody?
         */
        if (atomic_read(&cp->n_control))
                goto expire_later;
 
-       /*
-        *      unhash it if it is hashed in the conn table
-        */
-       if (!ip_vs_conn_unhash(cp) && !(cp->flags & IP_VS_CONN_F_ONE_PACKET))
-               goto expire_later;
-
-       /*
-        *      refcnt==1 implies I'm the only one referrer
-        */
-       if (likely(atomic_read(&cp->refcnt) == 1)) {
+       /* Unlink conn if not referenced anymore */
+       if (likely(ip_vs_conn_unlink(cp))) {
                /* delete the timer if it is activated by other users */
                del_timer(&cp->timer);
 
@@ -810,38 +807,41 @@ static void ip_vs_conn_expire(unsigned long data)
                                ip_vs_conn_drop_conntrack(cp);
                }
 
-               ip_vs_pe_put(cp->pe);
-               kfree(cp->pe_data);
                if (unlikely(cp->app != NULL))
                        ip_vs_unbind_app(cp);
                ip_vs_unbind_dest(cp);
                if (cp->flags & IP_VS_CONN_F_NO_CPORT)
                        atomic_dec(&ip_vs_conn_no_cport_cnt);
+               call_rcu(&cp->rcu_head, ip_vs_conn_rcu_free);
                atomic_dec(&ipvs->conn_count);
-
-               kmem_cache_free(ip_vs_conn_cachep, cp);
                return;
        }
 
-       /* hash it back to the table */
-       ip_vs_conn_hash(cp);
-
   expire_later:
-       IP_VS_DBG(7, "delayed: conn->refcnt-1=%d conn->n_control=%d\n",
-                 atomic_read(&cp->refcnt)-1,
+       IP_VS_DBG(7, "delayed: conn->refcnt=%d conn->n_control=%d\n",
+                 atomic_read(&cp->refcnt),
                  atomic_read(&cp->n_control));
 
+       atomic_inc(&cp->refcnt);
+       cp->timeout = 60*HZ;
+
        if (ipvs->sync_state & IP_VS_STATE_MASTER)
                ip_vs_sync_conn(net, cp, sysctl_sync_threshold(ipvs));
 
        ip_vs_conn_put(cp);
 }
 
-
+/* Modify timer, so that it expires as soon as possible.
+ * Can be called without reference only if under RCU lock.
+ */
 void ip_vs_conn_expire_now(struct ip_vs_conn *cp)
 {
-       if (del_timer(&cp->timer))
-               mod_timer(&cp->timer, jiffies);
+       /* Using mod_timer_pending will ensure the timer is not
+        * modified after the final del_timer in ip_vs_conn_expire.
+        */
+       if (timer_pending(&cp->timer) &&
+           time_after(cp->timer.expires, jiffies))
+               mod_timer_pending(&cp->timer, jiffies);
 }
 
 
@@ -858,7 +858,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
        struct ip_vs_proto_data *pd = ip_vs_proto_data_get(p->net,
                                                           p->protocol);
 
-       cp = kmem_cache_zalloc(ip_vs_conn_cachep, GFP_ATOMIC);
+       cp = kmem_cache_alloc(ip_vs_conn_cachep, GFP_ATOMIC);
        if (cp == NULL) {
                IP_VS_ERR_RL("%s(): no memory\n", __func__);
                return NULL;
@@ -869,13 +869,13 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
        ip_vs_conn_net_set(cp, p->net);
        cp->af             = p->af;
        cp->protocol       = p->protocol;
-       ip_vs_addr_copy(p->af, &cp->caddr, p->caddr);
+       ip_vs_addr_set(p->af, &cp->caddr, p->caddr);
        cp->cport          = p->cport;
-       ip_vs_addr_copy(p->af, &cp->vaddr, p->vaddr);
+       ip_vs_addr_set(p->af, &cp->vaddr, p->vaddr);
        cp->vport          = p->vport;
        /* proto should only be IPPROTO_IP if d_addr is a fwmark */
-       ip_vs_addr_copy(p->protocol == IPPROTO_IP ? AF_UNSPEC : p->af,
-                       &cp->daddr, daddr);
+       ip_vs_addr_set(p->protocol == IPPROTO_IP ? AF_UNSPEC : p->af,
+                      &cp->daddr, daddr);
        cp->dport          = dport;
        cp->flags          = flags;
        cp->fwmark         = fwmark;
@@ -884,6 +884,10 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
                cp->pe = p->pe;
                cp->pe_data = p->pe_data;
                cp->pe_data_len = p->pe_data_len;
+       } else {
+               cp->pe = NULL;
+               cp->pe_data = NULL;
+               cp->pe_data_len = 0;
        }
        spin_lock_init(&cp->lock);
 
@@ -894,18 +898,28 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
         */
        atomic_set(&cp->refcnt, 1);
 
+       cp->control = NULL;
        atomic_set(&cp->n_control, 0);
        atomic_set(&cp->in_pkts, 0);
 
+       cp->packet_xmit = NULL;
+       cp->app = NULL;
+       cp->app_data = NULL;
+       /* reset struct ip_vs_seq */
+       cp->in_seq.delta = 0;
+       cp->out_seq.delta = 0;
+
        atomic_inc(&ipvs->conn_count);
        if (flags & IP_VS_CONN_F_NO_CPORT)
                atomic_inc(&ip_vs_conn_no_cport_cnt);
 
        /* Bind the connection with a destination server */
+       cp->dest = NULL;
        ip_vs_bind_dest(cp, dest);
 
        /* Set its state and timeout */
        cp->state = 0;
+       cp->old_state = 0;
        cp->timeout = 3*HZ;
        cp->sync_endtime = jiffies & ~3UL;
 
@@ -952,14 +966,17 @@ static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos)
        struct ip_vs_iter_state *iter = seq->private;
 
        for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
-               ct_read_lock_bh(idx);
-               hlist_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) {
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) {
+                       /* __ip_vs_conn_get() is not needed by
+                        * ip_vs_conn_seq_show and ip_vs_conn_sync_seq_show
+                        */
                        if (pos-- == 0) {
                                iter->l = &ip_vs_conn_tab[idx];
                                return cp;
                        }
                }
-               ct_read_unlock_bh(idx);
+               rcu_read_unlock();
        }
 
        return NULL;
@@ -977,6 +994,7 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        struct ip_vs_conn *cp = v;
        struct ip_vs_iter_state *iter = seq->private;
+       struct hlist_node *e;
        struct hlist_head *l = iter->l;
        int idx;
 
@@ -985,19 +1003,19 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos)
                return ip_vs_conn_array(seq, 0);
 
        /* more on same hash chain? */
-       if (cp->c_list.next)
-               return hlist_entry(cp->c_list.next, struct ip_vs_conn, c_list);
+       e = rcu_dereference(hlist_next_rcu(&cp->c_list));
+       if (e)
+               return hlist_entry(e, struct ip_vs_conn, c_list);
+       rcu_read_unlock();
 
        idx = l - ip_vs_conn_tab;
-       ct_read_unlock_bh(idx);
-
        while (++idx < ip_vs_conn_tab_size) {
-               ct_read_lock_bh(idx);
-               hlist_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) {
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) {
                        iter->l = &ip_vs_conn_tab[idx];
                        return cp;
                }
-               ct_read_unlock_bh(idx);
+               rcu_read_unlock();
        }
        iter->l = NULL;
        return NULL;
@@ -1009,7 +1027,7 @@ static void ip_vs_conn_seq_stop(struct seq_file *seq, void *v)
        struct hlist_head *l = iter->l;
 
        if (l)
-               ct_read_unlock_bh(l - ip_vs_conn_tab);
+               rcu_read_unlock();
 }
 
 static int ip_vs_conn_seq_show(struct seq_file *seq, void *v)
@@ -1188,7 +1206,7 @@ static inline int todrop_entry(struct ip_vs_conn *cp)
 void ip_vs_random_dropentry(struct net *net)
 {
        int idx;
-       struct ip_vs_conn *cp;
+       struct ip_vs_conn *cp, *cp_c;
 
        /*
         * Randomly scan 1/32 of the whole table every second
@@ -1199,9 +1217,9 @@ void ip_vs_random_dropentry(struct net *net)
                /*
                 *  Lock is actually needed in this loop.
                 */
-               ct_write_lock_bh(hash);
+               rcu_read_lock();
 
-               hlist_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
+               hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
                        if (cp->flags & IP_VS_CONN_F_TEMPLATE)
                                /* connection template */
                                continue;
@@ -1228,12 +1246,15 @@ void ip_vs_random_dropentry(struct net *net)
 
                        IP_VS_DBG(4, "del connection\n");
                        ip_vs_conn_expire_now(cp);
-                       if (cp->control) {
+                       cp_c = cp->control;
+                       /* cp->control is valid only with reference to cp */
+                       if (cp_c && __ip_vs_conn_get(cp)) {
                                IP_VS_DBG(4, "del conn template\n");
-                               ip_vs_conn_expire_now(cp->control);
+                               ip_vs_conn_expire_now(cp_c);
+                               __ip_vs_conn_put(cp);
                        }
                }
-               ct_write_unlock_bh(hash);
+               rcu_read_unlock();
        }
 }
 
@@ -1244,7 +1265,7 @@ void ip_vs_random_dropentry(struct net *net)
 static void ip_vs_conn_flush(struct net *net)
 {
        int idx;
-       struct ip_vs_conn *cp;
+       struct ip_vs_conn *cp, *cp_c;
        struct netns_ipvs *ipvs = net_ipvs(net);
 
 flush_again:
@@ -1252,19 +1273,22 @@ flush_again:
                /*
                 *  Lock is actually needed in this loop.
                 */
-               ct_write_lock_bh(idx);
+               rcu_read_lock();
 
-               hlist_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) {
+               hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) {
                        if (!ip_vs_conn_net_eq(cp, net))
                                continue;
                        IP_VS_DBG(4, "del connection\n");
                        ip_vs_conn_expire_now(cp);
-                       if (cp->control) {
+                       cp_c = cp->control;
+                       /* cp->control is valid only with reference to cp */
+                       if (cp_c && __ip_vs_conn_get(cp)) {
                                IP_VS_DBG(4, "del conn template\n");
-                               ip_vs_conn_expire_now(cp->control);
+                               ip_vs_conn_expire_now(cp_c);
+                               __ip_vs_conn_put(cp);
                        }
                }
-               ct_write_unlock_bh(idx);
+               rcu_read_unlock();
        }
 
        /* the counter may be not NULL, because maybe some conn entries
@@ -1331,7 +1355,7 @@ int __init ip_vs_conn_init(void)
                INIT_HLIST_HEAD(&ip_vs_conn_tab[idx]);
 
        for (idx = 0; idx < CT_LOCKARRAY_SIZE; idx++)  {
-               rwlock_init(&__ip_vs_conntbl_lock_array[idx].l);
+               spin_lock_init(&__ip_vs_conntbl_lock_array[idx].l);
        }
 
        /* calculate the random value for connection hash */
@@ -1342,6 +1366,8 @@ int __init ip_vs_conn_init(void)
 
 void ip_vs_conn_cleanup(void)
 {
+       /* Wait all ip_vs_conn_rcu_free() callbacks to complete */
+       rcu_barrier();
        /* Release the empty cache */
        kmem_cache_destroy(ip_vs_conn_cachep);
        vfree(ip_vs_conn_tab);
index 61f49d241712a77797829a294ee94fc10161306e..f26fe3353a306bcee128dbad2c3619be9d3dbd73 100644 (file)
@@ -69,10 +69,7 @@ EXPORT_SYMBOL(ip_vs_conn_put);
 EXPORT_SYMBOL(ip_vs_get_debug_level);
 #endif
 
-int ip_vs_net_id __read_mostly;
-#ifdef IP_VS_GENERIC_NETNS
-EXPORT_SYMBOL(ip_vs_net_id);
-#endif
+static int ip_vs_net_id __read_mostly;
 /* netns cnt used for uniqueness */
 static atomic_t ipvs_netns_cnt = ATOMIC_INIT(0);
 
@@ -206,7 +203,7 @@ ip_vs_conn_fill_param_persist(const struct ip_vs_service *svc,
 {
        ip_vs_conn_fill_param(svc->net, svc->af, protocol, caddr, cport, vaddr,
                              vport, p);
-       p->pe = svc->pe;
+       p->pe = rcu_dereference(svc->pe);
        if (p->pe && p->pe->fill_param)
                return p->pe->fill_param(p, skb);
 
@@ -299,12 +296,15 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
        /* Check if a template already exists */
        ct = ip_vs_ct_in_get(&param);
        if (!ct || !ip_vs_check_template(ct)) {
+               struct ip_vs_scheduler *sched;
+
                /*
                 * No template found or the dest of the connection
                 * template is not available.
                 * return *ignored=0 i.e. ICMP and NF_DROP
                 */
-               dest = svc->scheduler->schedule(svc, skb);
+               sched = rcu_dereference(svc->scheduler);
+               dest = sched->schedule(svc, skb);
                if (!dest) {
                        IP_VS_DBG(1, "p-schedule: no dest found.\n");
                        kfree(param.pe_data);
@@ -394,6 +394,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
 {
        struct ip_vs_protocol *pp = pd->pp;
        struct ip_vs_conn *cp = NULL;
+       struct ip_vs_scheduler *sched;
        struct ip_vs_dest *dest;
        __be16 _ports[2], *pptr;
        unsigned int flags;
@@ -449,7 +450,8 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
                return NULL;
        }
 
-       dest = svc->scheduler->schedule(svc, skb);
+       sched = rcu_dereference(svc->scheduler);
+       dest = sched->schedule(svc, skb);
        if (dest == NULL) {
                IP_VS_DBG(1, "Schedule: no dest found.\n");
                return NULL;
@@ -507,7 +509,6 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
 
        pptr = frag_safe_skb_hp(skb, iph->len, sizeof(_ports), _ports, iph);
        if (pptr == NULL) {
-               ip_vs_service_put(svc);
                return NF_DROP;
        }
 
@@ -533,8 +534,6 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
                                      IP_VS_CONN_F_ONE_PACKET : 0;
                union nf_inet_addr daddr =  { .all = { 0, 0, 0, 0 } };
 
-               ip_vs_service_put(svc);
-
                /* create a new connection entry */
                IP_VS_DBG(6, "%s(): create a cache_bypass entry\n", __func__);
                {
@@ -571,12 +570,8 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
         * listed in the ipvs table), pass the packets, because it is
         * not ipvs job to decide to drop the packets.
         */
-       if ((svc->port == FTPPORT) && (pptr[1] != FTPPORT)) {
-               ip_vs_service_put(svc);
+       if ((svc->port == FTPPORT) && (pptr[1] != FTPPORT))
                return NF_ACCEPT;
-       }
-
-       ip_vs_service_put(svc);
 
        /*
         * Notify the client that the destination is unreachable, and
@@ -643,8 +638,11 @@ static inline enum ip_defrag_users ip_vs_defrag_user(unsigned int hooknum)
 
 static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user)
 {
-       int err = ip_defrag(skb, user);
+       int err;
 
+       local_bh_disable();
+       err = ip_defrag(skb, user);
+       local_bh_enable();
        if (!err)
                ip_send_check(ip_hdr(skb));
 
@@ -1164,9 +1162,8 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
                                         sizeof(_ports), _ports, &iph);
                if (pptr == NULL)
                        return NF_ACCEPT;       /* Not for me */
-               if (ip_vs_lookup_real_service(net, af, iph.protocol,
-                                             &iph.saddr,
-                                             pptr[0])) {
+               if (ip_vs_has_real_service(net, af, iph.protocol, &iph.saddr,
+                                          pptr[0])) {
                        /*
                         * Notify the real server: there is no
                         * existing entry if it is not RST
@@ -1181,9 +1178,6 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
                                                iph.len)))) {
 #ifdef CONFIG_IP_VS_IPV6
                                if (af == AF_INET6) {
-                                       struct net *net =
-                                               dev_net(skb_dst(skb)->dev);
-
                                        if (!skb->dev)
                                                skb->dev = net->loopback_dev;
                                        icmpv6_send(skb,
@@ -1226,13 +1220,7 @@ ip_vs_local_reply4(unsigned int hooknum, struct sk_buff *skb,
                   const struct net_device *in, const struct net_device *out,
                   int (*okfn)(struct sk_buff *))
 {
-       unsigned int verdict;
-
-       /* Disable BH in LOCAL_OUT until all places are fixed */
-       local_bh_disable();
-       verdict = ip_vs_out(hooknum, skb, AF_INET);
-       local_bh_enable();
-       return verdict;
+       return ip_vs_out(hooknum, skb, AF_INET);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -1259,13 +1247,7 @@ ip_vs_local_reply6(unsigned int hooknum, struct sk_buff *skb,
                   const struct net_device *in, const struct net_device *out,
                   int (*okfn)(struct sk_buff *))
 {
-       unsigned int verdict;
-
-       /* Disable BH in LOCAL_OUT until all places are fixed */
-       local_bh_disable();
-       verdict = ip_vs_out(hooknum, skb, AF_INET6);
-       local_bh_enable();
-       return verdict;
+       return ip_vs_out(hooknum, skb, AF_INET6);
 }
 
 #endif
@@ -1401,10 +1383,13 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
                                goto ignore_ipip;
                        /* Prefer the resulting PMTU */
                        if (dest) {
-                               spin_lock(&dest->dst_lock);
-                               if (dest->dst_cache)
-                                       mtu = dst_mtu(dest->dst_cache);
-                               spin_unlock(&dest->dst_lock);
+                               struct ip_vs_dest_dst *dest_dst;
+
+                               rcu_read_lock();
+                               dest_dst = rcu_dereference(dest->dest_dst);
+                               if (dest_dst)
+                                       mtu = dst_mtu(dest_dst->dst_cache);
+                               rcu_read_unlock();
                        }
                        if (mtu > 68 + sizeof(struct iphdr))
                                mtu -= sizeof(struct iphdr);
@@ -1720,13 +1705,7 @@ ip_vs_local_request4(unsigned int hooknum, struct sk_buff *skb,
                     const struct net_device *in, const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
 {
-       unsigned int verdict;
-
-       /* Disable BH in LOCAL_OUT until all places are fixed */
-       local_bh_disable();
-       verdict = ip_vs_in(hooknum, skb, AF_INET);
-       local_bh_enable();
-       return verdict;
+       return ip_vs_in(hooknum, skb, AF_INET);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -1785,13 +1764,7 @@ ip_vs_local_request6(unsigned int hooknum, struct sk_buff *skb,
                     const struct net_device *in, const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
 {
-       unsigned int verdict;
-
-       /* Disable BH in LOCAL_OUT until all places are fixed */
-       local_bh_disable();
-       verdict = ip_vs_in(hooknum, skb, AF_INET6);
-       local_bh_enable();
-       return verdict;
+       return ip_vs_in(hooknum, skb, AF_INET6);
 }
 
 #endif
index 9e2d1cccd1eb4c647b2ece3b2c3985edc0b5b32d..9e4074c26dc29c8abc6439ea6baf3a219968d91c 100644 (file)
@@ -55,9 +55,6 @@
 /* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */
 static DEFINE_MUTEX(__ip_vs_mutex);
 
-/* lock for service table */
-static DEFINE_RWLOCK(__ip_vs_svc_lock);
-
 /* sysctl variables */
 
 #ifdef CONFIG_IP_VS_DEBUG
@@ -71,7 +68,7 @@ int ip_vs_get_debug_level(void)
 
 
 /*  Protos */
-static void __ip_vs_del_service(struct ip_vs_service *svc);
+static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup);
 
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -257,9 +254,9 @@ ip_vs_use_count_dec(void)
 #define IP_VS_SVC_TAB_MASK (IP_VS_SVC_TAB_SIZE - 1)
 
 /* the service table hashed by <protocol, addr, port> */
-static struct list_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE];
+static struct hlist_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE];
 /* the service table hashed by fwmark */
-static struct list_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE];
+static struct hlist_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE];
 
 
 /*
@@ -271,16 +268,18 @@ ip_vs_svc_hashkey(struct net *net, int af, unsigned int proto,
 {
        register unsigned int porth = ntohs(port);
        __be32 addr_fold = addr->ip;
+       __u32 ahash;
 
 #ifdef CONFIG_IP_VS_IPV6
        if (af == AF_INET6)
                addr_fold = addr->ip6[0]^addr->ip6[1]^
                            addr->ip6[2]^addr->ip6[3];
 #endif
-       addr_fold ^= ((size_t)net>>8);
+       ahash = ntohl(addr_fold);
+       ahash ^= ((size_t) net >> 8);
 
-       return (proto^ntohl(addr_fold)^(porth>>IP_VS_SVC_TAB_BITS)^porth)
-               & IP_VS_SVC_TAB_MASK;
+       return (proto ^ ahash ^ (porth >> IP_VS_SVC_TAB_BITS) ^ porth) &
+              IP_VS_SVC_TAB_MASK;
 }
 
 /*
@@ -312,13 +311,13 @@ static int ip_vs_svc_hash(struct ip_vs_service *svc)
                 */
                hash = ip_vs_svc_hashkey(svc->net, svc->af, svc->protocol,
                                         &svc->addr, svc->port);
-               list_add(&svc->s_list, &ip_vs_svc_table[hash]);
+               hlist_add_head_rcu(&svc->s_list, &ip_vs_svc_table[hash]);
        } else {
                /*
                 *  Hash it by fwmark in svc_fwm_table
                 */
                hash = ip_vs_svc_fwm_hashkey(svc->net, svc->fwmark);
-               list_add(&svc->f_list, &ip_vs_svc_fwm_table[hash]);
+               hlist_add_head_rcu(&svc->f_list, &ip_vs_svc_fwm_table[hash]);
        }
 
        svc->flags |= IP_VS_SVC_F_HASHED;
@@ -342,10 +341,10 @@ static int ip_vs_svc_unhash(struct ip_vs_service *svc)
 
        if (svc->fwmark == 0) {
                /* Remove it from the svc_table table */
-               list_del(&svc->s_list);
+               hlist_del_rcu(&svc->s_list);
        } else {
                /* Remove it from the svc_fwm_table table */
-               list_del(&svc->f_list);
+               hlist_del_rcu(&svc->f_list);
        }
 
        svc->flags &= ~IP_VS_SVC_F_HASHED;
@@ -367,7 +366,7 @@ __ip_vs_service_find(struct net *net, int af, __u16 protocol,
        /* Check for "full" addressed entries */
        hash = ip_vs_svc_hashkey(net, af, protocol, vaddr, vport);
 
-       list_for_each_entry(svc, &ip_vs_svc_table[hash], s_list){
+       hlist_for_each_entry_rcu(svc, &ip_vs_svc_table[hash], s_list) {
                if ((svc->af == af)
                    && ip_vs_addr_equal(af, &svc->addr, vaddr)
                    && (svc->port == vport)
@@ -394,7 +393,7 @@ __ip_vs_svc_fwm_find(struct net *net, int af, __u32 fwmark)
        /* Check for fwmark addressed entries */
        hash = ip_vs_svc_fwm_hashkey(net, fwmark);
 
-       list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) {
+       hlist_for_each_entry_rcu(svc, &ip_vs_svc_fwm_table[hash], f_list) {
                if (svc->fwmark == fwmark && svc->af == af
                    && net_eq(svc->net, net)) {
                        /* HIT */
@@ -405,15 +404,14 @@ __ip_vs_svc_fwm_find(struct net *net, int af, __u32 fwmark)
        return NULL;
 }
 
+/* Find service, called under RCU lock */
 struct ip_vs_service *
-ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol,
-                 const union nf_inet_addr *vaddr, __be16 vport)
+ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol,
+                  const union nf_inet_addr *vaddr, __be16 vport)
 {
        struct ip_vs_service *svc;
        struct netns_ipvs *ipvs = net_ipvs(net);
 
-       read_lock(&__ip_vs_svc_lock);
-
        /*
         *      Check the table hashed by fwmark first
         */
@@ -449,10 +447,6 @@ ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol,
        }
 
   out:
-       if (svc)
-               atomic_inc(&svc->usecnt);
-       read_unlock(&__ip_vs_svc_lock);
-
        IP_VS_DBG_BUF(9, "lookup service: fwm %u %s %s:%u %s\n",
                      fwmark, ip_vs_proto_name(protocol),
                      IP_VS_DBG_ADDR(af, vaddr), ntohs(vport),
@@ -469,6 +463,13 @@ __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc)
        dest->svc = svc;
 }
 
+static void ip_vs_service_free(struct ip_vs_service *svc)
+{
+       if (svc->stats.cpustats)
+               free_percpu(svc->stats.cpustats);
+       kfree(svc);
+}
+
 static void
 __ip_vs_unbind_svc(struct ip_vs_dest *dest)
 {
@@ -476,12 +477,11 @@ __ip_vs_unbind_svc(struct ip_vs_dest *dest)
 
        dest->svc = NULL;
        if (atomic_dec_and_test(&svc->refcnt)) {
-               IP_VS_DBG_BUF(3, "Removing service %u/%s:%u usecnt=%d\n",
+               IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n",
                              svc->fwmark,
                              IP_VS_DBG_ADDR(svc->af, &svc->addr),
-                             ntohs(svc->port), atomic_read(&svc->usecnt));
-               free_percpu(svc->stats.cpustats);
-               kfree(svc);
+                             ntohs(svc->port));
+               ip_vs_service_free(svc);
        }
 }
 
@@ -506,17 +506,13 @@ static inline unsigned int ip_vs_rs_hashkey(int af,
                & IP_VS_RTAB_MASK;
 }
 
-/*
- *     Hashes ip_vs_dest in rs_table by <proto,addr,port>.
- *     should be called with locked tables.
- */
-static int ip_vs_rs_hash(struct netns_ipvs *ipvs, struct ip_vs_dest *dest)
+/* Hash ip_vs_dest in rs_table by <proto,addr,port>. */
+static void ip_vs_rs_hash(struct netns_ipvs *ipvs, struct ip_vs_dest *dest)
 {
        unsigned int hash;
 
-       if (!list_empty(&dest->d_list)) {
-               return 0;
-       }
+       if (dest->in_rs_table)
+               return;
 
        /*
         *      Hash by proto,addr,port,
@@ -524,64 +520,51 @@ static int ip_vs_rs_hash(struct netns_ipvs *ipvs, struct ip_vs_dest *dest)
         */
        hash = ip_vs_rs_hashkey(dest->af, &dest->addr, dest->port);
 
-       list_add(&dest->d_list, &ipvs->rs_table[hash]);
-
-       return 1;
+       hlist_add_head_rcu(&dest->d_list, &ipvs->rs_table[hash]);
+       dest->in_rs_table = 1;
 }
 
-/*
- *     UNhashes ip_vs_dest from rs_table.
- *     should be called with locked tables.
- */
-static int ip_vs_rs_unhash(struct ip_vs_dest *dest)
+/* Unhash ip_vs_dest from rs_table. */
+static void ip_vs_rs_unhash(struct ip_vs_dest *dest)
 {
        /*
         * Remove it from the rs_table table.
         */
-       if (!list_empty(&dest->d_list)) {
-               list_del_init(&dest->d_list);
+       if (dest->in_rs_table) {
+               hlist_del_rcu(&dest->d_list);
+               dest->in_rs_table = 0;
        }
-
-       return 1;
 }
 
-/*
- *     Lookup real service by <proto,addr,port> in the real service table.
- */
-struct ip_vs_dest *
-ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol,
-                         const union nf_inet_addr *daddr,
-                         __be16 dport)
+/* Check if real service by <proto,addr,port> is present */
+bool ip_vs_has_real_service(struct net *net, int af, __u16 protocol,
+                           const union nf_inet_addr *daddr, __be16 dport)
 {
        struct netns_ipvs *ipvs = net_ipvs(net);
        unsigned int hash;
        struct ip_vs_dest *dest;
 
-       /*
-        *      Check for "full" addressed entries
-        *      Return the first found entry
-        */
+       /* Check for "full" addressed entries */
        hash = ip_vs_rs_hashkey(af, daddr, dport);
 
-       read_lock(&ipvs->rs_lock);
-       list_for_each_entry(dest, &ipvs->rs_table[hash], d_list) {
-               if ((dest->af == af)
-                   && ip_vs_addr_equal(af, &dest->addr, daddr)
-                   && (dest->port == dport)
-                   && ((dest->protocol == protocol) ||
-                       dest->vfwmark)) {
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(dest, &ipvs->rs_table[hash], d_list) {
+               if (dest->port == dport &&
+                   dest->af == af &&
+                   ip_vs_addr_equal(af, &dest->addr, daddr) &&
+                   (dest->protocol == protocol || dest->vfwmark)) {
                        /* HIT */
-                       read_unlock(&ipvs->rs_lock);
-                       return dest;
+                       rcu_read_unlock();
+                       return true;
                }
        }
-       read_unlock(&ipvs->rs_lock);
+       rcu_read_unlock();
 
-       return NULL;
+       return false;
 }
 
-/*
- *     Lookup destination by {addr,port} in the given service
+/* Lookup destination by {addr,port} in the given service
+ * Called under RCU lock.
  */
 static struct ip_vs_dest *
 ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
@@ -592,7 +575,7 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
        /*
         * Find the destination for the given service
         */
-       list_for_each_entry(dest, &svc->destinations, n_list) {
+       list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
                if ((dest->af == svc->af)
                    && ip_vs_addr_equal(svc->af, &dest->addr, daddr)
                    && (dest->port == dport)) {
@@ -606,13 +589,11 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
 
 /*
  * Find destination by {daddr,dport,vaddr,protocol}
- * Cretaed to be used in ip_vs_process_message() in
+ * Created to be used in ip_vs_process_message() in
  * the backup synchronization daemon. It finds the
  * destination to be bound to the received connection
  * on the backup.
- *
- * ip_vs_lookup_real_service() looked promissing, but
- * seems not working as expected.
+ * Called under RCU lock, no refcnt is returned.
  */
 struct ip_vs_dest *ip_vs_find_dest(struct net  *net, int af,
                                   const union nf_inet_addr *daddr,
@@ -625,7 +606,7 @@ struct ip_vs_dest *ip_vs_find_dest(struct net  *net, int af,
        struct ip_vs_service *svc;
        __be16 port = dport;
 
-       svc = ip_vs_service_get(net, af, fwmark, protocol, vaddr, vport);
+       svc = ip_vs_service_find(net, af, fwmark, protocol, vaddr, vport);
        if (!svc)
                return NULL;
        if (fwmark && (flags & IP_VS_CONN_F_FWD_MASK) != IP_VS_CONN_F_MASQ)
@@ -633,12 +614,31 @@ struct ip_vs_dest *ip_vs_find_dest(struct net  *net, int af,
        dest = ip_vs_lookup_dest(svc, daddr, port);
        if (!dest)
                dest = ip_vs_lookup_dest(svc, daddr, port ^ dport);
-       if (dest)
-               atomic_inc(&dest->refcnt);
-       ip_vs_service_put(svc);
        return dest;
 }
 
+void ip_vs_dest_dst_rcu_free(struct rcu_head *head)
+{
+       struct ip_vs_dest_dst *dest_dst = container_of(head,
+                                                      struct ip_vs_dest_dst,
+                                                      rcu_head);
+
+       dst_release(dest_dst->dst_cache);
+       kfree(dest_dst);
+}
+
+/* Release dest_dst and dst_cache for dest in user context */
+static void __ip_vs_dst_cache_reset(struct ip_vs_dest *dest)
+{
+       struct ip_vs_dest_dst *old;
+
+       old = rcu_dereference_protected(dest->dest_dst, 1);
+       if (old) {
+               RCU_INIT_POINTER(dest->dest_dst, NULL);
+               call_rcu(&old->rcu_head, ip_vs_dest_dst_rcu_free);
+       }
+}
+
 /*
  *  Lookup dest by {svc,addr,port} in the destination trash.
  *  The destination trash is used to hold the destinations that are removed
@@ -653,19 +653,25 @@ static struct ip_vs_dest *
 ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
                     __be16 dport)
 {
-       struct ip_vs_dest *dest, *nxt;
+       struct ip_vs_dest *dest;
        struct netns_ipvs *ipvs = net_ipvs(svc->net);
 
        /*
         * Find the destination in trash
         */
-       list_for_each_entry_safe(dest, nxt, &ipvs->dest_trash, n_list) {
+       spin_lock_bh(&ipvs->dest_trash_lock);
+       list_for_each_entry(dest, &ipvs->dest_trash, t_list) {
                IP_VS_DBG_BUF(3, "Destination %u/%s:%u still in trash, "
                              "dest->refcnt=%d\n",
                              dest->vfwmark,
                              IP_VS_DBG_ADDR(svc->af, &dest->addr),
                              ntohs(dest->port),
                              atomic_read(&dest->refcnt));
+               /* We can not reuse dest while in grace period
+                * because conns still can use dest->svc
+                */
+               if (test_bit(IP_VS_DEST_STATE_REMOVING, &dest->state))
+                       continue;
                if (dest->af == svc->af &&
                    ip_vs_addr_equal(svc->af, &dest->addr, daddr) &&
                    dest->port == dport &&
@@ -675,29 +681,27 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
                     (ip_vs_addr_equal(svc->af, &dest->vaddr, &svc->addr) &&
                      dest->vport == svc->port))) {
                        /* HIT */
-                       return dest;
-               }
-
-               /*
-                * Try to purge the destination from trash if not referenced
-                */
-               if (atomic_read(&dest->refcnt) == 1) {
-                       IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u "
-                                     "from trash\n",
-                                     dest->vfwmark,
-                                     IP_VS_DBG_ADDR(svc->af, &dest->addr),
-                                     ntohs(dest->port));
-                       list_del(&dest->n_list);
-                       ip_vs_dst_reset(dest);
-                       __ip_vs_unbind_svc(dest);
-                       free_percpu(dest->stats.cpustats);
-                       kfree(dest);
+                       list_del(&dest->t_list);
+                       ip_vs_dest_hold(dest);
+                       goto out;
                }
        }
 
-       return NULL;
+       dest = NULL;
+
+out:
+       spin_unlock_bh(&ipvs->dest_trash_lock);
+
+       return dest;
 }
 
+static void ip_vs_dest_free(struct ip_vs_dest *dest)
+{
+       __ip_vs_dst_cache_reset(dest);
+       __ip_vs_unbind_svc(dest);
+       free_percpu(dest->stats.cpustats);
+       kfree(dest);
+}
 
 /*
  *  Clean up all the destinations in the trash
@@ -706,19 +710,18 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
  *  When the ip_vs_control_clearup is activated by ipvs module exit,
  *  the service tables must have been flushed and all the connections
  *  are expired, and the refcnt of each destination in the trash must
- *  be 1, so we simply release them here.
+ *  be 0, so we simply release them here.
  */
 static void ip_vs_trash_cleanup(struct net *net)
 {
        struct ip_vs_dest *dest, *nxt;
        struct netns_ipvs *ipvs = net_ipvs(net);
 
-       list_for_each_entry_safe(dest, nxt, &ipvs->dest_trash, n_list) {
-               list_del(&dest->n_list);
-               ip_vs_dst_reset(dest);
-               __ip_vs_unbind_svc(dest);
-               free_percpu(dest->stats.cpustats);
-               kfree(dest);
+       del_timer_sync(&ipvs->dest_trash_timer);
+       /* No need to use dest_trash_lock */
+       list_for_each_entry_safe(dest, nxt, &ipvs->dest_trash, t_list) {
+               list_del(&dest->t_list);
+               ip_vs_dest_free(dest);
        }
 }
 
@@ -768,6 +771,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
                    struct ip_vs_dest_user_kern *udest, int add)
 {
        struct netns_ipvs *ipvs = net_ipvs(svc->net);
+       struct ip_vs_scheduler *sched;
        int conn_flags;
 
        /* set the weight and the flags */
@@ -783,9 +787,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
                 *    Put the real service in rs_table if not present.
                 *    For now only for NAT!
                 */
-               write_lock_bh(&ipvs->rs_lock);
                ip_vs_rs_hash(ipvs, dest);
-               write_unlock_bh(&ipvs->rs_lock);
        }
        atomic_set(&dest->conn_flags, conn_flags);
 
@@ -809,27 +811,20 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
        dest->l_threshold = udest->l_threshold;
 
        spin_lock_bh(&dest->dst_lock);
-       ip_vs_dst_reset(dest);
+       __ip_vs_dst_cache_reset(dest);
        spin_unlock_bh(&dest->dst_lock);
 
-       if (add)
-               ip_vs_start_estimator(svc->net, &dest->stats);
-
-       write_lock_bh(&__ip_vs_svc_lock);
-
-       /* Wait until all other svc users go away */
-       IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
-
+       sched = rcu_dereference_protected(svc->scheduler, 1);
        if (add) {
-               list_add(&dest->n_list, &svc->destinations);
+               ip_vs_start_estimator(svc->net, &dest->stats);
+               list_add_rcu(&dest->n_list, &svc->destinations);
                svc->num_dests++;
+               if (sched->add_dest)
+                       sched->add_dest(svc, dest);
+       } else {
+               if (sched->upd_dest)
+                       sched->upd_dest(svc, dest);
        }
-
-       /* call the update_service, because server weight may be changed */
-       if (svc->scheduler->update_service)
-               svc->scheduler->update_service(svc);
-
-       write_unlock_bh(&__ip_vs_svc_lock);
 }
 
 
@@ -881,7 +876,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
        atomic_set(&dest->persistconns, 0);
        atomic_set(&dest->refcnt, 1);
 
-       INIT_LIST_HEAD(&dest->d_list);
+       INIT_HLIST_NODE(&dest->d_list);
        spin_lock_init(&dest->dst_lock);
        spin_lock_init(&dest->stats.lock);
        __ip_vs_update_dest(svc, dest, udest, 1);
@@ -923,10 +918,10 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
 
        ip_vs_addr_copy(svc->af, &daddr, &udest->addr);
 
-       /*
-        * Check if the dest already exists in the list
-        */
+       /* We use function that requires RCU lock */
+       rcu_read_lock();
        dest = ip_vs_lookup_dest(svc, &daddr, dport);
+       rcu_read_unlock();
 
        if (dest != NULL) {
                IP_VS_DBG(1, "%s(): dest already exists\n", __func__);
@@ -948,11 +943,6 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
                              IP_VS_DBG_ADDR(svc->af, &dest->vaddr),
                              ntohs(dest->vport));
 
-               /*
-                * Get the destination from the trash
-                */
-               list_del(&dest->n_list);
-
                __ip_vs_update_dest(svc, dest, udest, 1);
                ret = 0;
        } else {
@@ -992,10 +982,10 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
 
        ip_vs_addr_copy(svc->af, &daddr, &udest->addr);
 
-       /*
-        *  Lookup the destination list
-        */
+       /* We use function that requires RCU lock */
+       rcu_read_lock();
        dest = ip_vs_lookup_dest(svc, &daddr, dport);
+       rcu_read_unlock();
 
        if (dest == NULL) {
                IP_VS_DBG(1, "%s(): dest doesn't exist\n", __func__);
@@ -1008,11 +998,21 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
        return 0;
 }
 
+static void ip_vs_dest_wait_readers(struct rcu_head *head)
+{
+       struct ip_vs_dest *dest = container_of(head, struct ip_vs_dest,
+                                              rcu_head);
+
+       /* End of grace period after unlinking */
+       clear_bit(IP_VS_DEST_STATE_REMOVING, &dest->state);
+}
+
 
 /*
  *     Delete a destination (must be already unlinked from the service)
  */
-static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest)
+static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest,
+                            bool cleanup)
 {
        struct netns_ipvs *ipvs = net_ipvs(net);
 
@@ -1021,38 +1021,24 @@ static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest)
        /*
         *  Remove it from the d-linked list with the real services.
         */
-       write_lock_bh(&ipvs->rs_lock);
        ip_vs_rs_unhash(dest);
-       write_unlock_bh(&ipvs->rs_lock);
 
-       /*
-        *  Decrease the refcnt of the dest, and free the dest
-        *  if nobody refers to it (refcnt=0). Otherwise, throw
-        *  the destination into the trash.
-        */
-       if (atomic_dec_and_test(&dest->refcnt)) {
-               IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u\n",
-                             dest->vfwmark,
-                             IP_VS_DBG_ADDR(dest->af, &dest->addr),
-                             ntohs(dest->port));
-               ip_vs_dst_reset(dest);
-               /* simply decrease svc->refcnt here, let the caller check
-                  and release the service if nobody refers to it.
-                  Only user context can release destination and service,
-                  and only one user context can update virtual service at a
-                  time, so the operation here is OK */
-               atomic_dec(&dest->svc->refcnt);
-               free_percpu(dest->stats.cpustats);
-               kfree(dest);
-       } else {
-               IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, "
-                             "dest->refcnt=%d\n",
-                             IP_VS_DBG_ADDR(dest->af, &dest->addr),
-                             ntohs(dest->port),
-                             atomic_read(&dest->refcnt));
-               list_add(&dest->n_list, &ipvs->dest_trash);
-               atomic_inc(&dest->refcnt);
+       if (!cleanup) {
+               set_bit(IP_VS_DEST_STATE_REMOVING, &dest->state);
+               call_rcu(&dest->rcu_head, ip_vs_dest_wait_readers);
        }
+
+       spin_lock_bh(&ipvs->dest_trash_lock);
+       IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, dest->refcnt=%d\n",
+                     IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port),
+                     atomic_read(&dest->refcnt));
+       if (list_empty(&ipvs->dest_trash) && !cleanup)
+               mod_timer(&ipvs->dest_trash_timer,
+                         jiffies + IP_VS_DEST_TRASH_PERIOD);
+       /* dest lives in trash without reference */
+       list_add(&dest->t_list, &ipvs->dest_trash);
+       spin_unlock_bh(&ipvs->dest_trash_lock);
+       ip_vs_dest_put(dest);
 }
 
 
@@ -1068,14 +1054,16 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
        /*
         *  Remove it from the d-linked destination list.
         */
-       list_del(&dest->n_list);
+       list_del_rcu(&dest->n_list);
        svc->num_dests--;
 
-       /*
-        *  Call the update_service function of its scheduler
-        */
-       if (svcupd && svc->scheduler->update_service)
-                       svc->scheduler->update_service(svc);
+       if (svcupd) {
+               struct ip_vs_scheduler *sched;
+
+               sched = rcu_dereference_protected(svc->scheduler, 1);
+               if (sched->del_dest)
+                       sched->del_dest(svc, dest);
+       }
 }
 
 
@@ -1090,37 +1078,56 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
 
        EnterFunction(2);
 
+       /* We use function that requires RCU lock */
+       rcu_read_lock();
        dest = ip_vs_lookup_dest(svc, &udest->addr, dport);
+       rcu_read_unlock();
 
        if (dest == NULL) {
                IP_VS_DBG(1, "%s(): destination not found!\n", __func__);
                return -ENOENT;
        }
 
-       write_lock_bh(&__ip_vs_svc_lock);
-
-       /*
-        *      Wait until all other svc users go away.
-        */
-       IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
-
        /*
         *      Unlink dest from the service
         */
        __ip_vs_unlink_dest(svc, dest, 1);
 
-       write_unlock_bh(&__ip_vs_svc_lock);
-
        /*
         *      Delete the destination
         */
-       __ip_vs_del_dest(svc->net, dest);
+       __ip_vs_del_dest(svc->net, dest, false);
 
        LeaveFunction(2);
 
        return 0;
 }
 
+static void ip_vs_dest_trash_expire(unsigned long data)
+{
+       struct net *net = (struct net *) data;
+       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct ip_vs_dest *dest, *next;
+
+       spin_lock(&ipvs->dest_trash_lock);
+       list_for_each_entry_safe(dest, next, &ipvs->dest_trash, t_list) {
+               /* Skip if dest is in grace period */
+               if (test_bit(IP_VS_DEST_STATE_REMOVING, &dest->state))
+                       continue;
+               if (atomic_read(&dest->refcnt) > 0)
+                       continue;
+               IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u from trash\n",
+                             dest->vfwmark,
+                             IP_VS_DBG_ADDR(dest->svc->af, &dest->addr),
+                             ntohs(dest->port));
+               list_del(&dest->t_list);
+               ip_vs_dest_free(dest);
+       }
+       if (!list_empty(&ipvs->dest_trash))
+               mod_timer(&ipvs->dest_trash_timer,
+                         jiffies + IP_VS_DEST_TRASH_PERIOD);
+       spin_unlock(&ipvs->dest_trash_lock);
+}
 
 /*
  *     Add a service into the service hash table
@@ -1176,7 +1183,6 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
        }
 
        /* I'm the first user of the service */
-       atomic_set(&svc->usecnt, 0);
        atomic_set(&svc->refcnt, 0);
 
        svc->af = u->af;
@@ -1190,7 +1196,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
        svc->net = net;
 
        INIT_LIST_HEAD(&svc->destinations);
-       rwlock_init(&svc->sched_lock);
+       spin_lock_init(&svc->sched_lock);
        spin_lock_init(&svc->stats.lock);
 
        /* Bind the scheduler */
@@ -1200,7 +1206,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
        sched = NULL;
 
        /* Bind the ct retriever */
-       ip_vs_bind_pe(svc, pe);
+       RCU_INIT_POINTER(svc->pe, pe);
        pe = NULL;
 
        /* Update the virtual service counters */
@@ -1216,9 +1222,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
                ipvs->num_services++;
 
        /* Hash the service into the service table */
-       write_lock_bh(&__ip_vs_svc_lock);
        ip_vs_svc_hash(svc);
-       write_unlock_bh(&__ip_vs_svc_lock);
 
        *svc_p = svc;
        /* Now there is a service - full throttle */
@@ -1228,15 +1232,8 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
 
  out_err:
        if (svc != NULL) {
-               ip_vs_unbind_scheduler(svc);
-               if (svc->inc) {
-                       local_bh_disable();
-                       ip_vs_app_inc_put(svc->inc);
-                       local_bh_enable();
-               }
-               if (svc->stats.cpustats)
-                       free_percpu(svc->stats.cpustats);
-               kfree(svc);
+               ip_vs_unbind_scheduler(svc, sched);
+               ip_vs_service_free(svc);
        }
        ip_vs_scheduler_put(sched);
        ip_vs_pe_put(pe);
@@ -1286,12 +1283,17 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u)
        }
 #endif
 
-       write_lock_bh(&__ip_vs_svc_lock);
-
-       /*
-        * Wait until all other svc users go away.
-        */
-       IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
+       old_sched = rcu_dereference_protected(svc->scheduler, 1);
+       if (sched != old_sched) {
+               /* Bind the new scheduler */
+               ret = ip_vs_bind_scheduler(svc, sched);
+               if (ret) {
+                       old_sched = sched;
+                       goto out;
+               }
+               /* Unbind the old scheduler on success */
+               ip_vs_unbind_scheduler(svc, old_sched);
+       }
 
        /*
         * Set the flags and timeout value
@@ -1300,57 +1302,30 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u)
        svc->timeout = u->timeout * HZ;
        svc->netmask = u->netmask;
 
-       old_sched = svc->scheduler;
-       if (sched != old_sched) {
-               /*
-                * Unbind the old scheduler
-                */
-               if ((ret = ip_vs_unbind_scheduler(svc))) {
-                       old_sched = sched;
-                       goto out_unlock;
-               }
-
-               /*
-                * Bind the new scheduler
-                */
-               if ((ret = ip_vs_bind_scheduler(svc, sched))) {
-                       /*
-                        * If ip_vs_bind_scheduler fails, restore the old
-                        * scheduler.
-                        * The main reason of failure is out of memory.
-                        *
-                        * The question is if the old scheduler can be
-                        * restored all the time. TODO: if it cannot be
-                        * restored some time, we must delete the service,
-                        * otherwise the system may crash.
-                        */
-                       ip_vs_bind_scheduler(svc, old_sched);
-                       old_sched = sched;
-                       goto out_unlock;
-               }
-       }
+       old_pe = rcu_dereference_protected(svc->pe, 1);
+       if (pe != old_pe)
+               rcu_assign_pointer(svc->pe, pe);
 
-       old_pe = svc->pe;
-       if (pe != old_pe) {
-               ip_vs_unbind_pe(svc);
-               ip_vs_bind_pe(svc, pe);
-       }
-
-out_unlock:
-       write_unlock_bh(&__ip_vs_svc_lock);
 out:
        ip_vs_scheduler_put(old_sched);
        ip_vs_pe_put(old_pe);
        return ret;
 }
 
+static void ip_vs_service_rcu_free(struct rcu_head *head)
+{
+       struct ip_vs_service *svc;
+
+       svc = container_of(head, struct ip_vs_service, rcu_head);
+       ip_vs_service_free(svc);
+}
 
 /*
  *     Delete a service from the service list
  *     - The service must be unlinked, unlocked and not referenced!
  *     - We are called under _bh lock
  */
-static void __ip_vs_del_service(struct ip_vs_service *svc)
+static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup)
 {
        struct ip_vs_dest *dest, *nxt;
        struct ip_vs_scheduler *old_sched;
@@ -1366,27 +1341,20 @@ static void __ip_vs_del_service(struct ip_vs_service *svc)
        ip_vs_stop_estimator(svc->net, &svc->stats);
 
        /* Unbind scheduler */
-       old_sched = svc->scheduler;
-       ip_vs_unbind_scheduler(svc);
+       old_sched = rcu_dereference_protected(svc->scheduler, 1);
+       ip_vs_unbind_scheduler(svc, old_sched);
        ip_vs_scheduler_put(old_sched);
 
-       /* Unbind persistence engine */
-       old_pe = svc->pe;
-       ip_vs_unbind_pe(svc);
+       /* Unbind persistence engine, keep svc->pe */
+       old_pe = rcu_dereference_protected(svc->pe, 1);
        ip_vs_pe_put(old_pe);
 
-       /* Unbind app inc */
-       if (svc->inc) {
-               ip_vs_app_inc_put(svc->inc);
-               svc->inc = NULL;
-       }
-
        /*
         *    Unlink the whole destination list
         */
        list_for_each_entry_safe(dest, nxt, &svc->destinations, n_list) {
                __ip_vs_unlink_dest(svc, dest, 0);
-               __ip_vs_del_dest(svc->net, dest);
+               __ip_vs_del_dest(svc->net, dest, cleanup);
        }
 
        /*
@@ -1400,13 +1368,12 @@ static void __ip_vs_del_service(struct ip_vs_service *svc)
        /*
         *    Free the service if nobody refers to it
         */
-       if (atomic_read(&svc->refcnt) == 0) {
-               IP_VS_DBG_BUF(3, "Removing service %u/%s:%u usecnt=%d\n",
+       if (atomic_dec_and_test(&svc->refcnt)) {
+               IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n",
                              svc->fwmark,
                              IP_VS_DBG_ADDR(svc->af, &svc->addr),
-                             ntohs(svc->port), atomic_read(&svc->usecnt));
-               free_percpu(svc->stats.cpustats);
-               kfree(svc);
+                             ntohs(svc->port));
+               call_rcu(&svc->rcu_head, ip_vs_service_rcu_free);
        }
 
        /* decrease the module use count */
@@ -1416,23 +1383,16 @@ static void __ip_vs_del_service(struct ip_vs_service *svc)
 /*
  * Unlink a service from list and try to delete it if its refcnt reached 0
  */
-static void ip_vs_unlink_service(struct ip_vs_service *svc)
+static void ip_vs_unlink_service(struct ip_vs_service *svc, bool cleanup)
 {
+       /* Hold svc to avoid double release from dest_trash */
+       atomic_inc(&svc->refcnt);
        /*
         * Unhash it from the service table
         */
-       write_lock_bh(&__ip_vs_svc_lock);
-
        ip_vs_svc_unhash(svc);
 
-       /*
-        * Wait until all the svc users go away.
-        */
-       IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
-
-       __ip_vs_del_service(svc);
-
-       write_unlock_bh(&__ip_vs_svc_lock);
+       __ip_vs_del_service(svc, cleanup);
 }
 
 /*
@@ -1442,7 +1402,7 @@ static int ip_vs_del_service(struct ip_vs_service *svc)
 {
        if (svc == NULL)
                return -EEXIST;
-       ip_vs_unlink_service(svc);
+       ip_vs_unlink_service(svc, false);
 
        return 0;
 }
@@ -1451,19 +1411,20 @@ static int ip_vs_del_service(struct ip_vs_service *svc)
 /*
  *     Flush all the virtual services
  */
-static int ip_vs_flush(struct net *net)
+static int ip_vs_flush(struct net *net, bool cleanup)
 {
        int idx;
-       struct ip_vs_service *svc, *nxt;
+       struct ip_vs_service *svc;
+       struct hlist_node *n;
 
        /*
         * Flush the service table hashed by <netns,protocol,addr,port>
         */
        for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry_safe(svc, nxt, &ip_vs_svc_table[idx],
-                                        s_list) {
+               hlist_for_each_entry_safe(svc, n, &ip_vs_svc_table[idx],
+                                         s_list) {
                        if (net_eq(svc->net, net))
-                               ip_vs_unlink_service(svc);
+                               ip_vs_unlink_service(svc, cleanup);
                }
        }
 
@@ -1471,10 +1432,10 @@ static int ip_vs_flush(struct net *net)
         * Flush the service table hashed by fwmark
         */
        for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry_safe(svc, nxt,
-                                        &ip_vs_svc_fwm_table[idx], f_list) {
+               hlist_for_each_entry_safe(svc, n, &ip_vs_svc_fwm_table[idx],
+                                         f_list) {
                        if (net_eq(svc->net, net))
-                               ip_vs_unlink_service(svc);
+                               ip_vs_unlink_service(svc, cleanup);
                }
        }
 
@@ -1490,32 +1451,29 @@ void ip_vs_service_net_cleanup(struct net *net)
        EnterFunction(2);
        /* Check for "full" addressed entries */
        mutex_lock(&__ip_vs_mutex);
-       ip_vs_flush(net);
+       ip_vs_flush(net, true);
        mutex_unlock(&__ip_vs_mutex);
        LeaveFunction(2);
 }
-/*
- * Release dst hold by dst_cache
- */
+
+/* Put all references for device (dst_cache) */
 static inline void
-__ip_vs_dev_reset(struct ip_vs_dest *dest, struct net_device *dev)
+ip_vs_forget_dev(struct ip_vs_dest *dest, struct net_device *dev)
 {
        spin_lock_bh(&dest->dst_lock);
-       if (dest->dst_cache && dest->dst_cache->dev == dev) {
+       if (dest->dest_dst && dest->dest_dst->dst_cache->dev == dev) {
                IP_VS_DBG_BUF(3, "Reset dev:%s dest %s:%u ,dest->refcnt=%d\n",
                              dev->name,
                              IP_VS_DBG_ADDR(dest->af, &dest->addr),
                              ntohs(dest->port),
                              atomic_read(&dest->refcnt));
-               ip_vs_dst_reset(dest);
+               __ip_vs_dst_cache_reset(dest);
        }
        spin_unlock_bh(&dest->dst_lock);
 
 }
-/*
- * Netdev event receiver
- * Currently only NETDEV_UNREGISTER is handled, i.e. if we hold a reference to
- * a device that is "unregister" it must be released.
+/* Netdev event receiver
+ * Currently only NETDEV_DOWN is handled to release refs to cached dsts
  */
 static int ip_vs_dst_event(struct notifier_block *this, unsigned long event,
                            void *ptr)
@@ -1527,35 +1485,37 @@ static int ip_vs_dst_event(struct notifier_block *this, unsigned long event,
        struct ip_vs_dest *dest;
        unsigned int idx;
 
-       if (event != NETDEV_UNREGISTER || !ipvs)
+       if (event != NETDEV_DOWN || !ipvs)
                return NOTIFY_DONE;
        IP_VS_DBG(3, "%s() dev=%s\n", __func__, dev->name);
        EnterFunction(2);
        mutex_lock(&__ip_vs_mutex);
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
+               hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
                        if (net_eq(svc->net, net)) {
                                list_for_each_entry(dest, &svc->destinations,
                                                    n_list) {
-                                       __ip_vs_dev_reset(dest, dev);
+                                       ip_vs_forget_dev(dest, dev);
                                }
                        }
                }
 
-               list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
+               hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
                        if (net_eq(svc->net, net)) {
                                list_for_each_entry(dest, &svc->destinations,
                                                    n_list) {
-                                       __ip_vs_dev_reset(dest, dev);
+                                       ip_vs_forget_dev(dest, dev);
                                }
                        }
 
                }
        }
 
-       list_for_each_entry(dest, &ipvs->dest_trash, n_list) {
-               __ip_vs_dev_reset(dest, dev);
+       spin_lock_bh(&ipvs->dest_trash_lock);
+       list_for_each_entry(dest, &ipvs->dest_trash, t_list) {
+               ip_vs_forget_dev(dest, dev);
        }
+       spin_unlock_bh(&ipvs->dest_trash_lock);
        mutex_unlock(&__ip_vs_mutex);
        LeaveFunction(2);
        return NOTIFY_DONE;
@@ -1568,12 +1528,10 @@ static int ip_vs_zero_service(struct ip_vs_service *svc)
 {
        struct ip_vs_dest *dest;
 
-       write_lock_bh(&__ip_vs_svc_lock);
        list_for_each_entry(dest, &svc->destinations, n_list) {
                ip_vs_zero_stats(&dest->stats);
        }
        ip_vs_zero_stats(&svc->stats);
-       write_unlock_bh(&__ip_vs_svc_lock);
        return 0;
 }
 
@@ -1583,14 +1541,14 @@ static int ip_vs_zero_all(struct net *net)
        struct ip_vs_service *svc;
 
        for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
+               hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
                        if (net_eq(svc->net, net))
                                ip_vs_zero_service(svc);
                }
        }
 
        for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
+               hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
                        if (net_eq(svc->net, net))
                                ip_vs_zero_service(svc);
                }
@@ -1918,7 +1876,7 @@ static struct ctl_table vs_vars[] = {
 
 struct ip_vs_iter {
        struct seq_net_private p;  /* Do not move this, netns depends upon it*/
-       struct list_head *table;
+       struct hlist_head *table;
        int bucket;
 };
 
@@ -1951,7 +1909,7 @@ static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos)
 
        /* look in hash by protocol */
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
+               hlist_for_each_entry_rcu(svc, &ip_vs_svc_table[idx], s_list) {
                        if (net_eq(svc->net, net) && pos-- == 0) {
                                iter->table = ip_vs_svc_table;
                                iter->bucket = idx;
@@ -1962,7 +1920,8 @@ static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos)
 
        /* keep looking in fwmark */
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
+               hlist_for_each_entry_rcu(svc, &ip_vs_svc_fwm_table[idx],
+                                        f_list) {
                        if (net_eq(svc->net, net) && pos-- == 0) {
                                iter->table = ip_vs_svc_fwm_table;
                                iter->bucket = idx;
@@ -1975,17 +1934,16 @@ static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos)
 }
 
 static void *ip_vs_info_seq_start(struct seq_file *seq, loff_t *pos)
-__acquires(__ip_vs_svc_lock)
 {
 
-       read_lock_bh(&__ip_vs_svc_lock);
+       rcu_read_lock();
        return *pos ? ip_vs_info_array(seq, *pos - 1) : SEQ_START_TOKEN;
 }
 
 
 static void *ip_vs_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
-       struct list_head *e;
+       struct hlist_node *e;
        struct ip_vs_iter *iter;
        struct ip_vs_service *svc;
 
@@ -1998,13 +1956,14 @@ static void *ip_vs_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 
        if (iter->table == ip_vs_svc_table) {
                /* next service in table hashed by protocol */
-               if ((e = svc->s_list.next) != &ip_vs_svc_table[iter->bucket])
-                       return list_entry(e, struct ip_vs_service, s_list);
-
+               e = rcu_dereference(hlist_next_rcu(&svc->s_list));
+               if (e)
+                       return hlist_entry(e, struct ip_vs_service, s_list);
 
                while (++iter->bucket < IP_VS_SVC_TAB_SIZE) {
-                       list_for_each_entry(svc,&ip_vs_svc_table[iter->bucket],
-                                           s_list) {
+                       hlist_for_each_entry_rcu(svc,
+                                                &ip_vs_svc_table[iter->bucket],
+                                                s_list) {
                                return svc;
                        }
                }
@@ -2015,13 +1974,15 @@ static void *ip_vs_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
        }
 
        /* next service in hashed by fwmark */
-       if ((e = svc->f_list.next) != &ip_vs_svc_fwm_table[iter->bucket])
-               return list_entry(e, struct ip_vs_service, f_list);
+       e = rcu_dereference(hlist_next_rcu(&svc->f_list));
+       if (e)
+               return hlist_entry(e, struct ip_vs_service, f_list);
 
  scan_fwmark:
        while (++iter->bucket < IP_VS_SVC_TAB_SIZE) {
-               list_for_each_entry(svc, &ip_vs_svc_fwm_table[iter->bucket],
-                                   f_list)
+               hlist_for_each_entry_rcu(svc,
+                                        &ip_vs_svc_fwm_table[iter->bucket],
+                                        f_list)
                        return svc;
        }
 
@@ -2029,9 +1990,8 @@ static void *ip_vs_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 }
 
 static void ip_vs_info_seq_stop(struct seq_file *seq, void *v)
-__releases(__ip_vs_svc_lock)
 {
-       read_unlock_bh(&__ip_vs_svc_lock);
+       rcu_read_unlock();
 }
 
 
@@ -2049,6 +2009,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
                const struct ip_vs_service *svc = v;
                const struct ip_vs_iter *iter = seq->private;
                const struct ip_vs_dest *dest;
+               struct ip_vs_scheduler *sched = rcu_dereference(svc->scheduler);
 
                if (iter->table == ip_vs_svc_table) {
 #ifdef CONFIG_IP_VS_IPV6
@@ -2057,18 +2018,18 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
                                           ip_vs_proto_name(svc->protocol),
                                           &svc->addr.in6,
                                           ntohs(svc->port),
-                                          svc->scheduler->name);
+                                          sched->name);
                        else
 #endif
                                seq_printf(seq, "%s  %08X:%04X %s %s ",
                                           ip_vs_proto_name(svc->protocol),
                                           ntohl(svc->addr.ip),
                                           ntohs(svc->port),
-                                          svc->scheduler->name,
+                                          sched->name,
                                           (svc->flags & IP_VS_SVC_F_ONEPACKET)?"ops ":"");
                } else {
                        seq_printf(seq, "FWM  %08X %s %s",
-                                  svc->fwmark, svc->scheduler->name,
+                                  svc->fwmark, sched->name,
                                   (svc->flags & IP_VS_SVC_F_ONEPACKET)?"ops ":"");
                }
 
@@ -2079,7 +2040,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
                else
                        seq_putc(seq, '\n');
 
-               list_for_each_entry(dest, &svc->destinations, n_list) {
+               list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
 #ifdef CONFIG_IP_VS_IPV6
                        if (dest->af == AF_INET6)
                                seq_printf(seq,
@@ -2389,7 +2350,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
 
        if (cmd == IP_VS_SO_SET_FLUSH) {
                /* Flush the virtual service */
-               ret = ip_vs_flush(net);
+               ret = ip_vs_flush(net, false);
                goto out_unlock;
        } else if (cmd == IP_VS_SO_SET_TIMEOUT) {
                /* Set timeout values for (tcp tcpfin udp) */
@@ -2424,11 +2385,13 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
        }
 
        /* Lookup the exact service by <protocol, addr, port> or fwmark */
+       rcu_read_lock();
        if (usvc.fwmark == 0)
                svc = __ip_vs_service_find(net, usvc.af, usvc.protocol,
                                           &usvc.addr, usvc.port);
        else
                svc = __ip_vs_svc_fwm_find(net, usvc.af, usvc.fwmark);
+       rcu_read_unlock();
 
        if (cmd != IP_VS_SO_SET_ADD
            && (svc == NULL || svc->protocol != usvc.protocol)) {
@@ -2480,11 +2443,14 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
 static void
 ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src)
 {
+       struct ip_vs_scheduler *sched;
+
+       sched = rcu_dereference_protected(src->scheduler, 1);
        dst->protocol = src->protocol;
        dst->addr = src->addr.ip;
        dst->port = src->port;
        dst->fwmark = src->fwmark;
-       strlcpy(dst->sched_name, src->scheduler->name, sizeof(dst->sched_name));
+       strlcpy(dst->sched_name, sched->name, sizeof(dst->sched_name));
        dst->flags = src->flags;
        dst->timeout = src->timeout / HZ;
        dst->netmask = src->netmask;
@@ -2503,7 +2469,7 @@ __ip_vs_get_service_entries(struct net *net,
        int ret = 0;
 
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
+               hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
                        /* Only expose IPv4 entries to old interface */
                        if (svc->af != AF_INET || !net_eq(svc->net, net))
                                continue;
@@ -2522,7 +2488,7 @@ __ip_vs_get_service_entries(struct net *net,
        }
 
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
+               hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
                        /* Only expose IPv4 entries to old interface */
                        if (svc->af != AF_INET || !net_eq(svc->net, net))
                                continue;
@@ -2551,11 +2517,13 @@ __ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get,
        union nf_inet_addr addr = { .ip = get->addr };
        int ret = 0;
 
+       rcu_read_lock();
        if (get->fwmark)
                svc = __ip_vs_svc_fwm_find(net, AF_INET, get->fwmark);
        else
                svc = __ip_vs_service_find(net, AF_INET, get->protocol, &addr,
                                           get->port);
+       rcu_read_unlock();
 
        if (svc) {
                int count = 0;
@@ -2738,12 +2706,14 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
 
                entry = (struct ip_vs_service_entry *)arg;
                addr.ip = entry->addr;
+               rcu_read_lock();
                if (entry->fwmark)
                        svc = __ip_vs_svc_fwm_find(net, AF_INET, entry->fwmark);
                else
                        svc = __ip_vs_service_find(net, AF_INET,
                                                   entry->protocol, &addr,
                                                   entry->port);
+               rcu_read_unlock();
                if (svc) {
                        ip_vs_copy_service(entry, svc);
                        if (copy_to_user(user, entry, sizeof(*entry)) != 0)
@@ -2900,6 +2870,7 @@ nla_put_failure:
 static int ip_vs_genl_fill_service(struct sk_buff *skb,
                                   struct ip_vs_service *svc)
 {
+       struct ip_vs_scheduler *sched;
        struct nlattr *nl_service;
        struct ip_vs_flags flags = { .flags = svc->flags,
                                     .mask = ~0 };
@@ -2920,7 +2891,8 @@ static int ip_vs_genl_fill_service(struct sk_buff *skb,
                        goto nla_put_failure;
        }
 
-       if (nla_put_string(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name) ||
+       sched = rcu_dereference_protected(svc->scheduler, 1);
+       if (nla_put_string(skb, IPVS_SVC_ATTR_SCHED_NAME, sched->name) ||
            (svc->pe &&
             nla_put_string(skb, IPVS_SVC_ATTR_PE_NAME, svc->pe->name)) ||
            nla_put(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags) ||
@@ -2971,7 +2943,7 @@ static int ip_vs_genl_dump_services(struct sk_buff *skb,
 
        mutex_lock(&__ip_vs_mutex);
        for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
-               list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
+               hlist_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
                        if (++idx <= start || !net_eq(svc->net, net))
                                continue;
                        if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
@@ -2982,7 +2954,7 @@ static int ip_vs_genl_dump_services(struct sk_buff *skb,
        }
 
        for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
-               list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
+               hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
                        if (++idx <= start || !net_eq(svc->net, net))
                                continue;
                        if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
@@ -3042,11 +3014,13 @@ static int ip_vs_genl_parse_service(struct net *net,
                usvc->fwmark = 0;
        }
 
+       rcu_read_lock();
        if (usvc->fwmark)
                svc = __ip_vs_svc_fwm_find(net, usvc->af, usvc->fwmark);
        else
                svc = __ip_vs_service_find(net, usvc->af, usvc->protocol,
                                           &usvc->addr, usvc->port);
+       rcu_read_unlock();
        *ret_svc = svc;
 
        /* If a full entry was requested, check for the additional fields */
@@ -3398,7 +3372,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
        mutex_lock(&__ip_vs_mutex);
 
        if (cmd == IPVS_CMD_FLUSH) {
-               ret = ip_vs_flush(net);
+               ret = ip_vs_flush(net, false);
                goto out;
        } else if (cmd == IPVS_CMD_SET_CONFIG) {
                ret = ip_vs_genl_set_config(net, info->attrs);
@@ -3790,13 +3764,14 @@ int __net_init ip_vs_control_net_init(struct net *net)
        int idx;
        struct netns_ipvs *ipvs = net_ipvs(net);
 
-       rwlock_init(&ipvs->rs_lock);
-
        /* Initialize rs_table */
        for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++)
-               INIT_LIST_HEAD(&ipvs->rs_table[idx]);
+               INIT_HLIST_HEAD(&ipvs->rs_table[idx]);
 
        INIT_LIST_HEAD(&ipvs->dest_trash);
+       spin_lock_init(&ipvs->dest_trash_lock);
+       setup_timer(&ipvs->dest_trash_timer, ip_vs_dest_trash_expire,
+                   (unsigned long) net);
        atomic_set(&ipvs->ftpsvc_counter, 0);
        atomic_set(&ipvs->nullsvc_counter, 0);
 
@@ -3826,6 +3801,10 @@ void __net_exit ip_vs_control_net_cleanup(struct net *net)
 {
        struct netns_ipvs *ipvs = net_ipvs(net);
 
+       /* Some dest can be in grace period even before cleanup, we have to
+        * defer ip_vs_trash_cleanup until ip_vs_dest_wait_readers is called.
+        */
+       rcu_barrier();
        ip_vs_trash_cleanup(net);
        ip_vs_stop_estimator(net, &ipvs->tot_stats);
        ip_vs_control_net_cleanup_sysctl(net);
@@ -3871,10 +3850,10 @@ int __init ip_vs_control_init(void)
 
        EnterFunction(2);
 
-       /* Initialize svc_table, ip_vs_svc_fwm_table, rs_table */
+       /* Initialize svc_table, ip_vs_svc_fwm_table */
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
-               INIT_LIST_HEAD(&ip_vs_svc_table[idx]);
-               INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]);
+               INIT_HLIST_HEAD(&ip_vs_svc_table[idx]);
+               INIT_HLIST_HEAD(&ip_vs_svc_fwm_table[idx]);
        }
 
        smp_wmb();      /* Do we really need it now ? */
index 7f3b0cc00b7add352264752cd9db38aa9adda21a..ccab120df45e378dfe480e01f58e383f22277ac4 100644 (file)
@@ -51,7 +51,7 @@
  *      IPVS DH bucket
  */
 struct ip_vs_dh_bucket {
-       struct ip_vs_dest       *dest;          /* real server (cache) */
+       struct ip_vs_dest __rcu *dest;  /* real server (cache) */
 };
 
 /*
@@ -64,6 +64,10 @@ struct ip_vs_dh_bucket {
 #define IP_VS_DH_TAB_SIZE               (1 << IP_VS_DH_TAB_BITS)
 #define IP_VS_DH_TAB_MASK               (IP_VS_DH_TAB_SIZE - 1)
 
+struct ip_vs_dh_state {
+       struct ip_vs_dh_bucket          buckets[IP_VS_DH_TAB_SIZE];
+       struct rcu_head                 rcu_head;
+};
 
 /*
  *     Returns hash value for IPVS DH entry
@@ -85,10 +89,9 @@ static inline unsigned int ip_vs_dh_hashkey(int af, const union nf_inet_addr *ad
  *      Get ip_vs_dest associated with supplied parameters.
  */
 static inline struct ip_vs_dest *
-ip_vs_dh_get(int af, struct ip_vs_dh_bucket *tbl,
-            const union nf_inet_addr *addr)
+ip_vs_dh_get(int af, struct ip_vs_dh_state *s, const union nf_inet_addr *addr)
 {
-       return (tbl[ip_vs_dh_hashkey(af, addr)]).dest;
+       return rcu_dereference(s->buckets[ip_vs_dh_hashkey(af, addr)].dest);
 }
 
 
@@ -96,25 +99,30 @@ ip_vs_dh_get(int af, struct ip_vs_dh_bucket *tbl,
  *      Assign all the hash buckets of the specified table with the service.
  */
 static int
-ip_vs_dh_assign(struct ip_vs_dh_bucket *tbl, struct ip_vs_service *svc)
+ip_vs_dh_reassign(struct ip_vs_dh_state *s, struct ip_vs_service *svc)
 {
        int i;
        struct ip_vs_dh_bucket *b;
        struct list_head *p;
        struct ip_vs_dest *dest;
+       bool empty;
 
-       b = tbl;
+       b = &s->buckets[0];
        p = &svc->destinations;
+       empty = list_empty(p);
        for (i=0; i<IP_VS_DH_TAB_SIZE; i++) {
-               if (list_empty(p)) {
-                       b->dest = NULL;
-               } else {
+               dest = rcu_dereference_protected(b->dest, 1);
+               if (dest)
+                       ip_vs_dest_put(dest);
+               if (empty)
+                       RCU_INIT_POINTER(b->dest, NULL);
+               else {
                        if (p == &svc->destinations)
                                p = p->next;
 
                        dest = list_entry(p, struct ip_vs_dest, n_list);
-                       atomic_inc(&dest->refcnt);
-                       b->dest = dest;
+                       ip_vs_dest_hold(dest);
+                       RCU_INIT_POINTER(b->dest, dest);
 
                        p = p->next;
                }
@@ -127,16 +135,18 @@ ip_vs_dh_assign(struct ip_vs_dh_bucket *tbl, struct ip_vs_service *svc)
 /*
  *      Flush all the hash buckets of the specified table.
  */
-static void ip_vs_dh_flush(struct ip_vs_dh_bucket *tbl)
+static void ip_vs_dh_flush(struct ip_vs_dh_state *s)
 {
        int i;
        struct ip_vs_dh_bucket *b;
+       struct ip_vs_dest *dest;
 
-       b = tbl;
+       b = &s->buckets[0];
        for (i=0; i<IP_VS_DH_TAB_SIZE; i++) {
-               if (b->dest) {
-                       atomic_dec(&b->dest->refcnt);
-                       b->dest = NULL;
+               dest = rcu_dereference_protected(b->dest, 1);
+               if (dest) {
+                       ip_vs_dest_put(dest);
+                       RCU_INIT_POINTER(b->dest, NULL);
                }
                b++;
        }
@@ -145,51 +155,46 @@ static void ip_vs_dh_flush(struct ip_vs_dh_bucket *tbl)
 
 static int ip_vs_dh_init_svc(struct ip_vs_service *svc)
 {
-       struct ip_vs_dh_bucket *tbl;
+       struct ip_vs_dh_state *s;
 
        /* allocate the DH table for this service */
-       tbl = kmalloc(sizeof(struct ip_vs_dh_bucket)*IP_VS_DH_TAB_SIZE,
-                     GFP_KERNEL);
-       if (tbl == NULL)
+       s = kzalloc(sizeof(struct ip_vs_dh_state), GFP_KERNEL);
+       if (s == NULL)
                return -ENOMEM;
 
-       svc->sched_data = tbl;
+       svc->sched_data = s;
        IP_VS_DBG(6, "DH hash table (memory=%Zdbytes) allocated for "
                  "current service\n",
                  sizeof(struct ip_vs_dh_bucket)*IP_VS_DH_TAB_SIZE);
 
-       /* assign the hash buckets with the updated service */
-       ip_vs_dh_assign(tbl, svc);
+       /* assign the hash buckets with current dests */
+       ip_vs_dh_reassign(s, svc);
 
        return 0;
 }
 
 
-static int ip_vs_dh_done_svc(struct ip_vs_service *svc)
+static void ip_vs_dh_done_svc(struct ip_vs_service *svc)
 {
-       struct ip_vs_dh_bucket *tbl = svc->sched_data;
+       struct ip_vs_dh_state *s = svc->sched_data;
 
        /* got to clean up hash buckets here */
-       ip_vs_dh_flush(tbl);
+       ip_vs_dh_flush(s);
 
        /* release the table itself */
-       kfree(svc->sched_data);
+       kfree_rcu(s, rcu_head);
        IP_VS_DBG(6, "DH hash table (memory=%Zdbytes) released\n",
                  sizeof(struct ip_vs_dh_bucket)*IP_VS_DH_TAB_SIZE);
-
-       return 0;
 }
 
 
-static int ip_vs_dh_update_svc(struct ip_vs_service *svc)
+static int ip_vs_dh_dest_changed(struct ip_vs_service *svc,
+                                struct ip_vs_dest *dest)
 {
-       struct ip_vs_dh_bucket *tbl = svc->sched_data;
-
-       /* got to clean up hash buckets here */
-       ip_vs_dh_flush(tbl);
+       struct ip_vs_dh_state *s = svc->sched_data;
 
        /* assign the hash buckets with the updated service */
-       ip_vs_dh_assign(tbl, svc);
+       ip_vs_dh_reassign(s, svc);
 
        return 0;
 }
@@ -212,19 +217,20 @@ static struct ip_vs_dest *
 ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
 {
        struct ip_vs_dest *dest;
-       struct ip_vs_dh_bucket *tbl;
+       struct ip_vs_dh_state *s;
        struct ip_vs_iphdr iph;
 
        ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
 
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
-       tbl = (struct ip_vs_dh_bucket *)svc->sched_data;
-       dest = ip_vs_dh_get(svc->af, tbl, &iph.daddr);
+       s = (struct ip_vs_dh_state *) svc->sched_data;
+       dest = ip_vs_dh_get(svc->af, s, &iph.daddr);
        if (!dest
            || !(dest->flags & IP_VS_DEST_F_AVAILABLE)
            || atomic_read(&dest->weight) <= 0
            || is_overloaded(dest)) {
+               ip_vs_scheduler_err(svc, "no destination available");
                return NULL;
        }
 
@@ -248,7 +254,8 @@ static struct ip_vs_scheduler ip_vs_dh_scheduler =
        .n_list =               LIST_HEAD_INIT(ip_vs_dh_scheduler.n_list),
        .init_service =         ip_vs_dh_init_svc,
        .done_service =         ip_vs_dh_done_svc,
-       .update_service =       ip_vs_dh_update_svc,
+       .add_dest =             ip_vs_dh_dest_changed,
+       .del_dest =             ip_vs_dh_dest_changed,
        .schedule =             ip_vs_dh_schedule,
 };
 
@@ -262,6 +269,7 @@ static int __init ip_vs_dh_init(void)
 static void __exit ip_vs_dh_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_dh_scheduler);
+       synchronize_rcu();
 }
 
 
index 0fac6017b6fbc37e522a0bd9d2e39ffd9c31d1b3..6bee6d0c73a52e93e1413162b4db971340fa3312 100644 (file)
@@ -56,7 +56,7 @@
  * Make a summary from each cpu
  */
 static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum,
-                                struct ip_vs_cpu_stats *stats)
+                                struct ip_vs_cpu_stats __percpu *stats)
 {
        int i;
 
index 4f53a5f04437b4d75c9a12e672ff9beabf69c5c2..77c173282f388ce81bbd54ea6e41761656d95045 100644 (file)
@@ -267,10 +267,12 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
                         * hopefully it will succeed on the retransmitted
                         * packet.
                         */
+                       rcu_read_lock();
                        ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
                                                       iph->ihl * 4,
                                                       start-data, end-start,
                                                       buf, buf_len);
+                       rcu_read_unlock();
                        if (ret) {
                                ip_vs_nfct_expect_related(skb, ct, n_cp,
                                                          IPPROTO_TCP, 0, 0);
@@ -480,6 +482,7 @@ static int __init ip_vs_ftp_init(void)
        int rv;
 
        rv = register_pernet_subsys(&ip_vs_ftp_ops);
+       /* rcu_barrier() is called by netns on error */
        return rv;
 }
 
@@ -489,6 +492,7 @@ static int __init ip_vs_ftp_init(void)
 static void __exit ip_vs_ftp_exit(void)
 {
        unregister_pernet_subsys(&ip_vs_ftp_ops);
+       /* rcu_barrier() is called by netns */
 }
 
 
index fdd89b9564ea6e537d70cb79fdcb4f58dbd1ce34..b2cc2528a4df28db84160bfb002e4cb5838767c9 100644 (file)
  *      IP address and its destination server
  */
 struct ip_vs_lblc_entry {
-       struct list_head        list;
+       struct hlist_node       list;
        int                     af;             /* address family */
        union nf_inet_addr      addr;           /* destination IP address */
-       struct ip_vs_dest       *dest;          /* real server (cache) */
+       struct ip_vs_dest __rcu *dest;          /* real server (cache) */
        unsigned long           lastuse;        /* last used time */
+       struct rcu_head         rcu_head;
 };
 
 
@@ -102,12 +103,14 @@ struct ip_vs_lblc_entry {
  *      IPVS lblc hash table
  */
 struct ip_vs_lblc_table {
-       struct list_head        bucket[IP_VS_LBLC_TAB_SIZE];  /* hash bucket */
+       struct rcu_head         rcu_head;
+       struct hlist_head __rcu bucket[IP_VS_LBLC_TAB_SIZE];  /* hash bucket */
+       struct timer_list       periodic_timer; /* collect stale entries */
        atomic_t                entries;        /* number of entries */
        int                     max_size;       /* maximum size of entries */
-       struct timer_list       periodic_timer; /* collect stale entries */
        int                     rover;          /* rover for expire check */
        int                     counter;        /* counter for no expire */
+       bool                    dead;
 };
 
 
@@ -129,13 +132,16 @@ static ctl_table vs_vars_table[] = {
 
 static inline void ip_vs_lblc_free(struct ip_vs_lblc_entry *en)
 {
-       list_del(&en->list);
+       struct ip_vs_dest *dest;
+
+       hlist_del_rcu(&en->list);
        /*
         * We don't kfree dest because it is referred either by its service
         * or the trash dest list.
         */
-       atomic_dec(&en->dest->refcnt);
-       kfree(en);
+       dest = rcu_dereference_protected(en->dest, 1);
+       ip_vs_dest_put(dest);
+       kfree_rcu(en, rcu_head);
 }
 
 
@@ -165,15 +171,12 @@ ip_vs_lblc_hash(struct ip_vs_lblc_table *tbl, struct ip_vs_lblc_entry *en)
 {
        unsigned int hash = ip_vs_lblc_hashkey(en->af, &en->addr);
 
-       list_add(&en->list, &tbl->bucket[hash]);
+       hlist_add_head_rcu(&en->list, &tbl->bucket[hash]);
        atomic_inc(&tbl->entries);
 }
 
 
-/*
- *  Get ip_vs_lblc_entry associated with supplied parameters. Called under read
- *  lock
- */
+/* Get ip_vs_lblc_entry associated with supplied parameters. */
 static inline struct ip_vs_lblc_entry *
 ip_vs_lblc_get(int af, struct ip_vs_lblc_table *tbl,
               const union nf_inet_addr *addr)
@@ -181,7 +184,7 @@ ip_vs_lblc_get(int af, struct ip_vs_lblc_table *tbl,
        unsigned int hash = ip_vs_lblc_hashkey(af, addr);
        struct ip_vs_lblc_entry *en;
 
-       list_for_each_entry(en, &tbl->bucket[hash], list)
+       hlist_for_each_entry_rcu(en, &tbl->bucket[hash], list)
                if (ip_vs_addr_equal(af, &en->addr, addr))
                        return en;
 
@@ -191,7 +194,7 @@ ip_vs_lblc_get(int af, struct ip_vs_lblc_table *tbl,
 
 /*
  * Create or update an ip_vs_lblc_entry, which is a mapping of a destination IP
- * address to a server. Called under write lock.
+ * address to a server. Called under spin lock.
  */
 static inline struct ip_vs_lblc_entry *
 ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr,
@@ -209,14 +212,20 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr,
                ip_vs_addr_copy(dest->af, &en->addr, daddr);
                en->lastuse = jiffies;
 
-               atomic_inc(&dest->refcnt);
-               en->dest = dest;
+               ip_vs_dest_hold(dest);
+               RCU_INIT_POINTER(en->dest, dest);
 
                ip_vs_lblc_hash(tbl, en);
-       } else if (en->dest != dest) {
-               atomic_dec(&en->dest->refcnt);
-               atomic_inc(&dest->refcnt);
-               en->dest = dest;
+       } else {
+               struct ip_vs_dest *old_dest;
+
+               old_dest = rcu_dereference_protected(en->dest, 1);
+               if (old_dest != dest) {
+                       ip_vs_dest_put(old_dest);
+                       ip_vs_dest_hold(dest);
+                       /* No ordering constraints for refcnt */
+                       RCU_INIT_POINTER(en->dest, dest);
+               }
        }
 
        return en;
@@ -226,17 +235,22 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr,
 /*
  *      Flush all the entries of the specified table.
  */
-static void ip_vs_lblc_flush(struct ip_vs_lblc_table *tbl)
+static void ip_vs_lblc_flush(struct ip_vs_service *svc)
 {
-       struct ip_vs_lblc_entry *en, *nxt;
+       struct ip_vs_lblc_table *tbl = svc->sched_data;
+       struct ip_vs_lblc_entry *en;
+       struct hlist_node *next;
        int i;
 
+       spin_lock_bh(&svc->sched_lock);
+       tbl->dead = 1;
        for (i=0; i<IP_VS_LBLC_TAB_SIZE; i++) {
-               list_for_each_entry_safe(en, nxt, &tbl->bucket[i], list) {
+               hlist_for_each_entry_safe(en, next, &tbl->bucket[i], list) {
                        ip_vs_lblc_free(en);
                        atomic_dec(&tbl->entries);
                }
        }
+       spin_unlock_bh(&svc->sched_lock);
 }
 
 static int sysctl_lblc_expiration(struct ip_vs_service *svc)
@@ -252,15 +266,16 @@ static int sysctl_lblc_expiration(struct ip_vs_service *svc)
 static inline void ip_vs_lblc_full_check(struct ip_vs_service *svc)
 {
        struct ip_vs_lblc_table *tbl = svc->sched_data;
-       struct ip_vs_lblc_entry *en, *nxt;
+       struct ip_vs_lblc_entry *en;
+       struct hlist_node *next;
        unsigned long now = jiffies;
        int i, j;
 
        for (i=0, j=tbl->rover; i<IP_VS_LBLC_TAB_SIZE; i++) {
                j = (j + 1) & IP_VS_LBLC_TAB_MASK;
 
-               write_lock(&svc->sched_lock);
-               list_for_each_entry_safe(en, nxt, &tbl->bucket[j], list) {
+               spin_lock(&svc->sched_lock);
+               hlist_for_each_entry_safe(en, next, &tbl->bucket[j], list) {
                        if (time_before(now,
                                        en->lastuse +
                                        sysctl_lblc_expiration(svc)))
@@ -269,7 +284,7 @@ static inline void ip_vs_lblc_full_check(struct ip_vs_service *svc)
                        ip_vs_lblc_free(en);
                        atomic_dec(&tbl->entries);
                }
-               write_unlock(&svc->sched_lock);
+               spin_unlock(&svc->sched_lock);
        }
        tbl->rover = j;
 }
@@ -293,7 +308,8 @@ static void ip_vs_lblc_check_expire(unsigned long data)
        unsigned long now = jiffies;
        int goal;
        int i, j;
-       struct ip_vs_lblc_entry *en, *nxt;
+       struct ip_vs_lblc_entry *en;
+       struct hlist_node *next;
 
        if ((tbl->counter % COUNT_FOR_FULL_EXPIRATION) == 0) {
                /* do full expiration check */
@@ -314,8 +330,8 @@ static void ip_vs_lblc_check_expire(unsigned long data)
        for (i=0, j=tbl->rover; i<IP_VS_LBLC_TAB_SIZE; i++) {
                j = (j + 1) & IP_VS_LBLC_TAB_MASK;
 
-               write_lock(&svc->sched_lock);
-               list_for_each_entry_safe(en, nxt, &tbl->bucket[j], list) {
+               spin_lock(&svc->sched_lock);
+               hlist_for_each_entry_safe(en, next, &tbl->bucket[j], list) {
                        if (time_before(now, en->lastuse + ENTRY_TIMEOUT))
                                continue;
 
@@ -323,7 +339,7 @@ static void ip_vs_lblc_check_expire(unsigned long data)
                        atomic_dec(&tbl->entries);
                        goal--;
                }
-               write_unlock(&svc->sched_lock);
+               spin_unlock(&svc->sched_lock);
                if (goal <= 0)
                        break;
        }
@@ -354,11 +370,12 @@ static int ip_vs_lblc_init_svc(struct ip_vs_service *svc)
         *    Initialize the hash buckets
         */
        for (i=0; i<IP_VS_LBLC_TAB_SIZE; i++) {
-               INIT_LIST_HEAD(&tbl->bucket[i]);
+               INIT_HLIST_HEAD(&tbl->bucket[i]);
        }
        tbl->max_size = IP_VS_LBLC_TAB_SIZE*16;
        tbl->rover = 0;
        tbl->counter = 1;
+       tbl->dead = 0;
 
        /*
         *    Hook periodic timer for garbage collection
@@ -371,7 +388,7 @@ static int ip_vs_lblc_init_svc(struct ip_vs_service *svc)
 }
 
 
-static int ip_vs_lblc_done_svc(struct ip_vs_service *svc)
+static void ip_vs_lblc_done_svc(struct ip_vs_service *svc)
 {
        struct ip_vs_lblc_table *tbl = svc->sched_data;
 
@@ -379,14 +396,12 @@ static int ip_vs_lblc_done_svc(struct ip_vs_service *svc)
        del_timer_sync(&tbl->periodic_timer);
 
        /* got to clean up table entries here */
-       ip_vs_lblc_flush(tbl);
+       ip_vs_lblc_flush(svc);
 
        /* release the table itself */
-       kfree(tbl);
+       kfree_rcu(tbl, rcu_head);
        IP_VS_DBG(6, "LBLC hash table (memory=%Zdbytes) released\n",
                  sizeof(*tbl));
-
-       return 0;
 }
 
 
@@ -408,7 +423,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
         * The server with weight=0 is quiesced and will not receive any
         * new connection.
         */
-       list_for_each_entry(dest, &svc->destinations, n_list) {
+       list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
                if (atomic_read(&dest->weight) > 0) {
@@ -423,7 +438,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
         *    Find the destination with the least load.
         */
   nextstage:
-       list_for_each_entry_continue(dest, &svc->destinations, n_list) {
+       list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
 
@@ -457,7 +472,7 @@ is_overloaded(struct ip_vs_dest *dest, struct ip_vs_service *svc)
        if (atomic_read(&dest->activeconns) > atomic_read(&dest->weight)) {
                struct ip_vs_dest *d;
 
-               list_for_each_entry(d, &svc->destinations, n_list) {
+               list_for_each_entry_rcu(d, &svc->destinations, n_list) {
                        if (atomic_read(&d->activeconns)*2
                            < atomic_read(&d->weight)) {
                                return 1;
@@ -484,7 +499,6 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
        /* First look in our cache */
-       read_lock(&svc->sched_lock);
        en = ip_vs_lblc_get(svc->af, tbl, &iph.daddr);
        if (en) {
                /* We only hold a read lock, but this is atomic */
@@ -499,14 +513,11 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
                 * free up entries from the trash at any time.
                 */
 
-               if (en->dest->flags & IP_VS_DEST_F_AVAILABLE)
-                       dest = en->dest;
+               dest = rcu_dereference(en->dest);
+               if ((dest->flags & IP_VS_DEST_F_AVAILABLE) &&
+                   atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
+                       goto out;
        }
-       read_unlock(&svc->sched_lock);
-
-       /* If the destination has a weight and is not overloaded, use it */
-       if (dest && atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
-               goto out;
 
        /* No cache entry or it is invalid, time to schedule */
        dest = __ip_vs_lblc_schedule(svc);
@@ -516,9 +527,10 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
        }
 
        /* If we fail to create a cache entry, we'll just use the valid dest */
-       write_lock(&svc->sched_lock);
-       ip_vs_lblc_new(tbl, &iph.daddr, dest);
-       write_unlock(&svc->sched_lock);
+       spin_lock_bh(&svc->sched_lock);
+       if (!tbl->dead)
+               ip_vs_lblc_new(tbl, &iph.daddr, dest);
+       spin_unlock_bh(&svc->sched_lock);
 
 out:
        IP_VS_DBG_BUF(6, "LBLC: destination IP address %s --> server %s:%d\n",
@@ -621,6 +633,7 @@ static void __exit ip_vs_lblc_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_lblc_scheduler);
        unregister_pernet_subsys(&ip_vs_lblc_ops);
+       synchronize_rcu();
 }
 
 
index c03b6a3ade2f90a4c9d96f8a3a11dceb1b3df130..feb9656eac58675ea9a755f151bbf5b766c72bc5 100644 (file)
  */
 struct ip_vs_dest_set_elem {
        struct list_head        list;          /* list link */
-       struct ip_vs_dest       *dest;          /* destination server */
+       struct ip_vs_dest __rcu *dest;         /* destination server */
+       struct rcu_head         rcu_head;
 };
 
 struct ip_vs_dest_set {
        atomic_t                size;           /* set size */
        unsigned long           lastmod;        /* last modified time */
        struct list_head        list;           /* destination list */
-       rwlock_t                lock;           /* lock for this list */
 };
 
 
-static struct ip_vs_dest_set_elem *
-ip_vs_dest_set_insert(struct ip_vs_dest_set *set, struct ip_vs_dest *dest)
+static void ip_vs_dest_set_insert(struct ip_vs_dest_set *set,
+                                 struct ip_vs_dest *dest, bool check)
 {
        struct ip_vs_dest_set_elem *e;
 
-       list_for_each_entry(e, &set->list, list) {
-               if (e->dest == dest)
-                       /* already existed */
-                       return NULL;
+       if (check) {
+               list_for_each_entry(e, &set->list, list) {
+                       struct ip_vs_dest *d;
+
+                       d = rcu_dereference_protected(e->dest, 1);
+                       if (d == dest)
+                               /* already existed */
+                               return;
+               }
        }
 
        e = kmalloc(sizeof(*e), GFP_ATOMIC);
        if (e == NULL)
-               return NULL;
+               return;
 
-       atomic_inc(&dest->refcnt);
-       e->dest = dest;
+       ip_vs_dest_hold(dest);
+       RCU_INIT_POINTER(e->dest, dest);
 
-       list_add(&e->list, &set->list);
+       list_add_rcu(&e->list, &set->list);
        atomic_inc(&set->size);
 
        set->lastmod = jiffies;
-       return e;
 }
 
 static void
@@ -131,13 +135,16 @@ ip_vs_dest_set_erase(struct ip_vs_dest_set *set, struct ip_vs_dest *dest)
        struct ip_vs_dest_set_elem *e;
 
        list_for_each_entry(e, &set->list, list) {
-               if (e->dest == dest) {
+               struct ip_vs_dest *d;
+
+               d = rcu_dereference_protected(e->dest, 1);
+               if (d == dest) {
                        /* HIT */
                        atomic_dec(&set->size);
                        set->lastmod = jiffies;
-                       atomic_dec(&e->dest->refcnt);
-                       list_del(&e->list);
-                       kfree(e);
+                       ip_vs_dest_put(dest);
+                       list_del_rcu(&e->list);
+                       kfree_rcu(e, rcu_head);
                        break;
                }
        }
@@ -147,17 +154,18 @@ static void ip_vs_dest_set_eraseall(struct ip_vs_dest_set *set)
 {
        struct ip_vs_dest_set_elem *e, *ep;
 
-       write_lock(&set->lock);
        list_for_each_entry_safe(e, ep, &set->list, list) {
+               struct ip_vs_dest *d;
+
+               d = rcu_dereference_protected(e->dest, 1);
                /*
                 * We don't kfree dest because it is referred either
                 * by its service or by the trash dest list.
                 */
-               atomic_dec(&e->dest->refcnt);
-               list_del(&e->list);
-               kfree(e);
+               ip_vs_dest_put(d);
+               list_del_rcu(&e->list);
+               kfree_rcu(e, rcu_head);
        }
-       write_unlock(&set->lock);
 }
 
 /* get weighted least-connection node in the destination set */
@@ -171,8 +179,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
                return NULL;
 
        /* select the first destination server, whose weight > 0 */
-       list_for_each_entry(e, &set->list, list) {
-               least = e->dest;
+       list_for_each_entry_rcu(e, &set->list, list) {
+               least = rcu_dereference(e->dest);
                if (least->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
 
@@ -186,8 +194,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
 
        /* find the destination with the weighted least load */
   nextstage:
-       list_for_each_entry(e, &set->list, list) {
-               dest = e->dest;
+       list_for_each_entry_continue_rcu(e, &set->list, list) {
+               dest = rcu_dereference(e->dest);
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
 
@@ -224,7 +232,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
 
        /* select the first destination server, whose weight > 0 */
        list_for_each_entry(e, &set->list, list) {
-               most = e->dest;
+               most = rcu_dereference_protected(e->dest, 1);
                if (atomic_read(&most->weight) > 0) {
                        moh = ip_vs_dest_conn_overhead(most);
                        goto nextstage;
@@ -234,8 +242,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
 
        /* find the destination with the weighted most load */
   nextstage:
-       list_for_each_entry(e, &set->list, list) {
-               dest = e->dest;
+       list_for_each_entry_continue(e, &set->list, list) {
+               dest = rcu_dereference_protected(e->dest, 1);
                doh = ip_vs_dest_conn_overhead(dest);
                /* moh/mw < doh/dw ==> moh*dw < doh*mw, where mw,dw>0 */
                if ((moh * atomic_read(&dest->weight) <
@@ -262,11 +270,12 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
  *      IP address and its destination server set
  */
 struct ip_vs_lblcr_entry {
-       struct list_head        list;
+       struct hlist_node       list;
        int                     af;             /* address family */
        union nf_inet_addr      addr;           /* destination IP address */
        struct ip_vs_dest_set   set;            /* destination server set */
        unsigned long           lastuse;        /* last used time */
+       struct rcu_head         rcu_head;
 };
 
 
@@ -274,12 +283,14 @@ struct ip_vs_lblcr_entry {
  *      IPVS lblcr hash table
  */
 struct ip_vs_lblcr_table {
-       struct list_head        bucket[IP_VS_LBLCR_TAB_SIZE];  /* hash bucket */
+       struct rcu_head         rcu_head;
+       struct hlist_head __rcu bucket[IP_VS_LBLCR_TAB_SIZE];  /* hash bucket */
        atomic_t                entries;        /* number of entries */
        int                     max_size;       /* maximum size of entries */
        struct timer_list       periodic_timer; /* collect stale entries */
        int                     rover;          /* rover for expire check */
        int                     counter;        /* counter for no expire */
+       bool                    dead;
 };
 
 
@@ -302,9 +313,9 @@ static ctl_table vs_vars_table[] = {
 
 static inline void ip_vs_lblcr_free(struct ip_vs_lblcr_entry *en)
 {
-       list_del(&en->list);
+       hlist_del_rcu(&en->list);
        ip_vs_dest_set_eraseall(&en->set);
-       kfree(en);
+       kfree_rcu(en, rcu_head);
 }
 
 
@@ -334,15 +345,12 @@ ip_vs_lblcr_hash(struct ip_vs_lblcr_table *tbl, struct ip_vs_lblcr_entry *en)
 {
        unsigned int hash = ip_vs_lblcr_hashkey(en->af, &en->addr);
 
-       list_add(&en->list, &tbl->bucket[hash]);
+       hlist_add_head_rcu(&en->list, &tbl->bucket[hash]);
        atomic_inc(&tbl->entries);
 }
 
 
-/*
- *  Get ip_vs_lblcr_entry associated with supplied parameters. Called under
- *  read lock.
- */
+/* Get ip_vs_lblcr_entry associated with supplied parameters. */
 static inline struct ip_vs_lblcr_entry *
 ip_vs_lblcr_get(int af, struct ip_vs_lblcr_table *tbl,
                const union nf_inet_addr *addr)
@@ -350,7 +358,7 @@ ip_vs_lblcr_get(int af, struct ip_vs_lblcr_table *tbl,
        unsigned int hash = ip_vs_lblcr_hashkey(af, addr);
        struct ip_vs_lblcr_entry *en;
 
-       list_for_each_entry(en, &tbl->bucket[hash], list)
+       hlist_for_each_entry_rcu(en, &tbl->bucket[hash], list)
                if (ip_vs_addr_equal(af, &en->addr, addr))
                        return en;
 
@@ -360,7 +368,7 @@ ip_vs_lblcr_get(int af, struct ip_vs_lblcr_table *tbl,
 
 /*
  * Create or update an ip_vs_lblcr_entry, which is a mapping of a destination
- * IP address to a server. Called under write lock.
+ * IP address to a server. Called under spin lock.
  */
 static inline struct ip_vs_lblcr_entry *
 ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr,
@@ -381,14 +389,14 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr,
                /* initialize its dest set */
                atomic_set(&(en->set.size), 0);
                INIT_LIST_HEAD(&en->set.list);
-               rwlock_init(&en->set.lock);
+
+               ip_vs_dest_set_insert(&en->set, dest, false);
 
                ip_vs_lblcr_hash(tbl, en);
+               return en;
        }
 
-       write_lock(&en->set.lock);
-       ip_vs_dest_set_insert(&en->set, dest);
-       write_unlock(&en->set.lock);
+       ip_vs_dest_set_insert(&en->set, dest, true);
 
        return en;
 }
@@ -397,17 +405,21 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr,
 /*
  *      Flush all the entries of the specified table.
  */
-static void ip_vs_lblcr_flush(struct ip_vs_lblcr_table *tbl)
+static void ip_vs_lblcr_flush(struct ip_vs_service *svc)
 {
+       struct ip_vs_lblcr_table *tbl = svc->sched_data;
        int i;
-       struct ip_vs_lblcr_entry *en, *nxt;
+       struct ip_vs_lblcr_entry *en;
+       struct hlist_node *next;
 
-       /* No locking required, only called during cleanup. */
+       spin_lock_bh(&svc->sched_lock);
+       tbl->dead = 1;
        for (i=0; i<IP_VS_LBLCR_TAB_SIZE; i++) {
-               list_for_each_entry_safe(en, nxt, &tbl->bucket[i], list) {
+               hlist_for_each_entry_safe(en, next, &tbl->bucket[i], list) {
                        ip_vs_lblcr_free(en);
                }
        }
+       spin_unlock_bh(&svc->sched_lock);
 }
 
 static int sysctl_lblcr_expiration(struct ip_vs_service *svc)
@@ -425,13 +437,14 @@ static inline void ip_vs_lblcr_full_check(struct ip_vs_service *svc)
        struct ip_vs_lblcr_table *tbl = svc->sched_data;
        unsigned long now = jiffies;
        int i, j;
-       struct ip_vs_lblcr_entry *en, *nxt;
+       struct ip_vs_lblcr_entry *en;
+       struct hlist_node *next;
 
        for (i=0, j=tbl->rover; i<IP_VS_LBLCR_TAB_SIZE; i++) {
                j = (j + 1) & IP_VS_LBLCR_TAB_MASK;
 
-               write_lock(&svc->sched_lock);
-               list_for_each_entry_safe(en, nxt, &tbl->bucket[j], list) {
+               spin_lock(&svc->sched_lock);
+               hlist_for_each_entry_safe(en, next, &tbl->bucket[j], list) {
                        if (time_after(en->lastuse +
                                       sysctl_lblcr_expiration(svc), now))
                                continue;
@@ -439,7 +452,7 @@ static inline void ip_vs_lblcr_full_check(struct ip_vs_service *svc)
                        ip_vs_lblcr_free(en);
                        atomic_dec(&tbl->entries);
                }
-               write_unlock(&svc->sched_lock);
+               spin_unlock(&svc->sched_lock);
        }
        tbl->rover = j;
 }
@@ -463,7 +476,8 @@ static void ip_vs_lblcr_check_expire(unsigned long data)
        unsigned long now = jiffies;
        int goal;
        int i, j;
-       struct ip_vs_lblcr_entry *en, *nxt;
+       struct ip_vs_lblcr_entry *en;
+       struct hlist_node *next;
 
        if ((tbl->counter % COUNT_FOR_FULL_EXPIRATION) == 0) {
                /* do full expiration check */
@@ -484,8 +498,8 @@ static void ip_vs_lblcr_check_expire(unsigned long data)
        for (i=0, j=tbl->rover; i<IP_VS_LBLCR_TAB_SIZE; i++) {
                j = (j + 1) & IP_VS_LBLCR_TAB_MASK;
 
-               write_lock(&svc->sched_lock);
-               list_for_each_entry_safe(en, nxt, &tbl->bucket[j], list) {
+               spin_lock(&svc->sched_lock);
+               hlist_for_each_entry_safe(en, next, &tbl->bucket[j], list) {
                        if (time_before(now, en->lastuse+ENTRY_TIMEOUT))
                                continue;
 
@@ -493,7 +507,7 @@ static void ip_vs_lblcr_check_expire(unsigned long data)
                        atomic_dec(&tbl->entries);
                        goal--;
                }
-               write_unlock(&svc->sched_lock);
+               spin_unlock(&svc->sched_lock);
                if (goal <= 0)
                        break;
        }
@@ -523,11 +537,12 @@ static int ip_vs_lblcr_init_svc(struct ip_vs_service *svc)
         *    Initialize the hash buckets
         */
        for (i=0; i<IP_VS_LBLCR_TAB_SIZE; i++) {
-               INIT_LIST_HEAD(&tbl->bucket[i]);
+               INIT_HLIST_HEAD(&tbl->bucket[i]);
        }
        tbl->max_size = IP_VS_LBLCR_TAB_SIZE*16;
        tbl->rover = 0;
        tbl->counter = 1;
+       tbl->dead = 0;
 
        /*
         *    Hook periodic timer for garbage collection
@@ -540,7 +555,7 @@ static int ip_vs_lblcr_init_svc(struct ip_vs_service *svc)
 }
 
 
-static int ip_vs_lblcr_done_svc(struct ip_vs_service *svc)
+static void ip_vs_lblcr_done_svc(struct ip_vs_service *svc)
 {
        struct ip_vs_lblcr_table *tbl = svc->sched_data;
 
@@ -548,14 +563,12 @@ static int ip_vs_lblcr_done_svc(struct ip_vs_service *svc)
        del_timer_sync(&tbl->periodic_timer);
 
        /* got to clean up table entries here */
-       ip_vs_lblcr_flush(tbl);
+       ip_vs_lblcr_flush(svc);
 
        /* release the table itself */
-       kfree(tbl);
+       kfree_rcu(tbl, rcu_head);
        IP_VS_DBG(6, "LBLCR hash table (memory=%Zdbytes) released\n",
                  sizeof(*tbl));
-
-       return 0;
 }
 
 
@@ -577,7 +590,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
         * The server with weight=0 is quiesced and will not receive any
         * new connection.
         */
-       list_for_each_entry(dest, &svc->destinations, n_list) {
+       list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
 
@@ -593,7 +606,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
         *    Find the destination with the least load.
         */
   nextstage:
-       list_for_each_entry_continue(dest, &svc->destinations, n_list) {
+       list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
 
@@ -627,7 +640,7 @@ is_overloaded(struct ip_vs_dest *dest, struct ip_vs_service *svc)
        if (atomic_read(&dest->activeconns) > atomic_read(&dest->weight)) {
                struct ip_vs_dest *d;
 
-               list_for_each_entry(d, &svc->destinations, n_list) {
+               list_for_each_entry_rcu(d, &svc->destinations, n_list) {
                        if (atomic_read(&d->activeconns)*2
                            < atomic_read(&d->weight)) {
                                return 1;
@@ -646,7 +659,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
 {
        struct ip_vs_lblcr_table *tbl = svc->sched_data;
        struct ip_vs_iphdr iph;
-       struct ip_vs_dest *dest = NULL;
+       struct ip_vs_dest *dest;
        struct ip_vs_lblcr_entry *en;
 
        ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
@@ -654,53 +667,46 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
        /* First look in our cache */
-       read_lock(&svc->sched_lock);
        en = ip_vs_lblcr_get(svc->af, tbl, &iph.daddr);
        if (en) {
-               /* We only hold a read lock, but this is atomic */
                en->lastuse = jiffies;
 
                /* Get the least loaded destination */
-               read_lock(&en->set.lock);
                dest = ip_vs_dest_set_min(&en->set);
-               read_unlock(&en->set.lock);
 
                /* More than one destination + enough time passed by, cleanup */
                if (atomic_read(&en->set.size) > 1 &&
-                               time_after(jiffies, en->set.lastmod +
+                   time_after(jiffies, en->set.lastmod +
                                sysctl_lblcr_expiration(svc))) {
-                       struct ip_vs_dest *m;
+                       spin_lock_bh(&svc->sched_lock);
+                       if (atomic_read(&en->set.size) > 1) {
+                               struct ip_vs_dest *m;
 
-                       write_lock(&en->set.lock);
-                       m = ip_vs_dest_set_max(&en->set);
-                       if (m)
-                               ip_vs_dest_set_erase(&en->set, m);
-                       write_unlock(&en->set.lock);
+                               m = ip_vs_dest_set_max(&en->set);
+                               if (m)
+                                       ip_vs_dest_set_erase(&en->set, m);
+                       }
+                       spin_unlock_bh(&svc->sched_lock);
                }
 
                /* If the destination is not overloaded, use it */
-               if (dest && !is_overloaded(dest, svc)) {
-                       read_unlock(&svc->sched_lock);
+               if (dest && !is_overloaded(dest, svc))
                        goto out;
-               }
 
                /* The cache entry is invalid, time to schedule */
                dest = __ip_vs_lblcr_schedule(svc);
                if (!dest) {
                        ip_vs_scheduler_err(svc, "no destination available");
-                       read_unlock(&svc->sched_lock);
                        return NULL;
                }
 
                /* Update our cache entry */
-               write_lock(&en->set.lock);
-               ip_vs_dest_set_insert(&en->set, dest);
-               write_unlock(&en->set.lock);
-       }
-       read_unlock(&svc->sched_lock);
-
-       if (dest)
+               spin_lock_bh(&svc->sched_lock);
+               if (!tbl->dead)
+                       ip_vs_dest_set_insert(&en->set, dest, true);
+               spin_unlock_bh(&svc->sched_lock);
                goto out;
+       }
 
        /* No cache entry, time to schedule */
        dest = __ip_vs_lblcr_schedule(svc);
@@ -710,9 +716,10 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
        }
 
        /* If we fail to create a cache entry, we'll just use the valid dest */
-       write_lock(&svc->sched_lock);
-       ip_vs_lblcr_new(tbl, &iph.daddr, dest);
-       write_unlock(&svc->sched_lock);
+       spin_lock_bh(&svc->sched_lock);
+       if (!tbl->dead)
+               ip_vs_lblcr_new(tbl, &iph.daddr, dest);
+       spin_unlock_bh(&svc->sched_lock);
 
 out:
        IP_VS_DBG_BUF(6, "LBLCR: destination IP address %s --> server %s:%d\n",
@@ -814,6 +821,7 @@ static void __exit ip_vs_lblcr_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_lblcr_scheduler);
        unregister_pernet_subsys(&ip_vs_lblcr_ops);
+       synchronize_rcu();
 }
 
 
index f391819c0ccad893ff1b07b59a0bfb7a8005ed66..5128e338a749dc8dd9c2846607fc95f25ac59054 100644 (file)
@@ -42,7 +42,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
         * served, but no new connection is assigned to the server.
         */
 
-       list_for_each_entry(dest, &svc->destinations, n_list) {
+       list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
                if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
                    atomic_read(&dest->weight) == 0)
                        continue;
@@ -84,6 +84,7 @@ static int __init ip_vs_lc_init(void)
 static void __exit ip_vs_lc_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_lc_scheduler);
+       synchronize_rcu();
 }
 
 module_init(ip_vs_lc_init);
index 984d9c137d84ac65c140c6ee15c78a66d80c8775..646cfd4baa73b2dcd954cc20ce975bd2bd39967a 100644 (file)
@@ -75,7 +75,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
         * new connections.
         */
 
-       list_for_each_entry(dest, &svc->destinations, n_list) {
+       list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
 
                if (dest->flags & IP_VS_DEST_F_OVERLOAD ||
                    !atomic_read(&dest->weight))
@@ -133,6 +133,7 @@ static int __init ip_vs_nq_init(void)
 static void __exit ip_vs_nq_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_nq_scheduler);
+       synchronize_rcu();
 }
 
 module_init(ip_vs_nq_init);
index 5cf859ccb31bbe096c9588643a9ed0f0d5f13342..1a82b29ce8eab833cc83f05ba032431442b1c70d 100644 (file)
 /* IPVS pe list */
 static LIST_HEAD(ip_vs_pe);
 
-/* lock for service table */
-static DEFINE_SPINLOCK(ip_vs_pe_lock);
-
-/* Bind a service with a pe */
-void ip_vs_bind_pe(struct ip_vs_service *svc, struct ip_vs_pe *pe)
-{
-       svc->pe = pe;
-}
-
-/* Unbind a service from its pe */
-void ip_vs_unbind_pe(struct ip_vs_service *svc)
-{
-       svc->pe = NULL;
-}
+/* semaphore for IPVS PEs. */
+static DEFINE_MUTEX(ip_vs_pe_mutex);
 
 /* Get pe in the pe list by name */
 struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name)
@@ -36,9 +24,8 @@ struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name)
        IP_VS_DBG(10, "%s(): pe_name \"%s\"\n", __func__,
                  pe_name);
 
-       spin_lock_bh(&ip_vs_pe_lock);
-
-       list_for_each_entry(pe, &ip_vs_pe, n_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(pe, &ip_vs_pe, n_list) {
                /* Test and get the modules atomically */
                if (pe->module &&
                    !try_module_get(pe->module)) {
@@ -47,14 +34,14 @@ struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name)
                }
                if (strcmp(pe_name, pe->name)==0) {
                        /* HIT */
-                       spin_unlock_bh(&ip_vs_pe_lock);
+                       rcu_read_unlock();
                        return pe;
                }
                if (pe->module)
                        module_put(pe->module);
        }
+       rcu_read_unlock();
 
-       spin_unlock_bh(&ip_vs_pe_lock);
        return NULL;
 }
 
@@ -83,22 +70,13 @@ int register_ip_vs_pe(struct ip_vs_pe *pe)
        /* increase the module use count */
        ip_vs_use_count_inc();
 
-       spin_lock_bh(&ip_vs_pe_lock);
-
-       if (!list_empty(&pe->n_list)) {
-               spin_unlock_bh(&ip_vs_pe_lock);
-               ip_vs_use_count_dec();
-               pr_err("%s(): [%s] pe already linked\n",
-                      __func__, pe->name);
-               return -EINVAL;
-       }
-
+       mutex_lock(&ip_vs_pe_mutex);
        /* Make sure that the pe with this name doesn't exist
         * in the pe list.
         */
        list_for_each_entry(tmp, &ip_vs_pe, n_list) {
                if (strcmp(tmp->name, pe->name) == 0) {
-                       spin_unlock_bh(&ip_vs_pe_lock);
+                       mutex_unlock(&ip_vs_pe_mutex);
                        ip_vs_use_count_dec();
                        pr_err("%s(): [%s] pe already existed "
                               "in the system\n", __func__, pe->name);
@@ -106,8 +84,8 @@ int register_ip_vs_pe(struct ip_vs_pe *pe)
                }
        }
        /* Add it into the d-linked pe list */
-       list_add(&pe->n_list, &ip_vs_pe);
-       spin_unlock_bh(&ip_vs_pe_lock);
+       list_add_rcu(&pe->n_list, &ip_vs_pe);
+       mutex_unlock(&ip_vs_pe_mutex);
 
        pr_info("[%s] pe registered.\n", pe->name);
 
@@ -118,17 +96,10 @@ EXPORT_SYMBOL_GPL(register_ip_vs_pe);
 /* Unregister a pe from the pe list */
 int unregister_ip_vs_pe(struct ip_vs_pe *pe)
 {
-       spin_lock_bh(&ip_vs_pe_lock);
-       if (list_empty(&pe->n_list)) {
-               spin_unlock_bh(&ip_vs_pe_lock);
-               pr_err("%s(): [%s] pe is not in the list. failed\n",
-                      __func__, pe->name);
-               return -EINVAL;
-       }
-
+       mutex_lock(&ip_vs_pe_mutex);
        /* Remove it from the d-linked pe list */
-       list_del(&pe->n_list);
-       spin_unlock_bh(&ip_vs_pe_lock);
+       list_del_rcu(&pe->n_list);
+       mutex_unlock(&ip_vs_pe_mutex);
 
        /* decrease the module use count */
        ip_vs_use_count_dec();
index 12475ef88dafe571bbc2e031af50890c08d91a54..00cc0241ed87860b84722f20af7236428639b001 100644 (file)
@@ -172,6 +172,7 @@ static int __init ip_vs_sip_init(void)
 static void __exit ip_vs_sip_cleanup(void)
 {
        unregister_ip_vs_pe(&ip_vs_sip_pe);
+       synchronize_rcu();
 }
 
 module_init(ip_vs_sip_init);
index cd1d7298f7ba779de3e6654b85fafaf7cec58f8e..6e14a7b5602f00d64b4b4e5b4d5e9853760832d4 100644 (file)
@@ -27,9 +27,10 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
        if (sch == NULL)
                return 0;
        net = skb_net(skb);
+       rcu_read_lock();
        if ((sch->type == SCTP_CID_INIT) &&
-           (svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
-                                    &iph->daddr, sh->dest))) {
+           (svc = ip_vs_service_find(net, af, skb->mark, iph->protocol,
+                                     &iph->daddr, sh->dest))) {
                int ignored;
 
                if (ip_vs_todrop(net_ipvs(net))) {
@@ -37,7 +38,7 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                         * It seems that we are very loaded.
                         * We have to drop this packet :(
                         */
-                       ip_vs_service_put(svc);
+                       rcu_read_unlock();
                        *verdict = NF_DROP;
                        return 0;
                }
@@ -49,14 +50,13 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                if (!*cpp && ignored <= 0) {
                        if (!ignored)
                                *verdict = ip_vs_leave(svc, skb, pd, iph);
-                       else {
-                               ip_vs_service_put(svc);
+                       else
                                *verdict = NF_DROP;
-                       }
+                       rcu_read_unlock();
                        return 0;
                }
-               ip_vs_service_put(svc);
        }
+       rcu_read_unlock();
        /* NF_ACCEPT */
        return 1;
 }
@@ -994,9 +994,9 @@ static void
 sctp_state_transition(struct ip_vs_conn *cp, int direction,
                const struct sk_buff *skb, struct ip_vs_proto_data *pd)
 {
-       spin_lock(&cp->lock);
+       spin_lock_bh(&cp->lock);
        set_sctp_state(pd, cp, direction, skb);
-       spin_unlock(&cp->lock);
+       spin_unlock_bh(&cp->lock);
 }
 
 static inline __u16 sctp_app_hashkey(__be16 port)
@@ -1016,30 +1016,25 @@ static int sctp_register_app(struct net *net, struct ip_vs_app *inc)
 
        hash = sctp_app_hashkey(port);
 
-       spin_lock_bh(&ipvs->sctp_app_lock);
        list_for_each_entry(i, &ipvs->sctp_apps[hash], p_list) {
                if (i->port == port) {
                        ret = -EEXIST;
                        goto out;
                }
        }
-       list_add(&inc->p_list, &ipvs->sctp_apps[hash]);
+       list_add_rcu(&inc->p_list, &ipvs->sctp_apps[hash]);
        atomic_inc(&pd->appcnt);
 out:
-       spin_unlock_bh(&ipvs->sctp_app_lock);
 
        return ret;
 }
 
 static void sctp_unregister_app(struct net *net, struct ip_vs_app *inc)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_SCTP);
 
-       spin_lock_bh(&ipvs->sctp_app_lock);
        atomic_dec(&pd->appcnt);
-       list_del(&inc->p_list);
-       spin_unlock_bh(&ipvs->sctp_app_lock);
+       list_del_rcu(&inc->p_list);
 }
 
 static int sctp_app_conn_bind(struct ip_vs_conn *cp)
@@ -1055,12 +1050,12 @@ static int sctp_app_conn_bind(struct ip_vs_conn *cp)
        /* Lookup application incarnations and bind the right one */
        hash = sctp_app_hashkey(cp->vport);
 
-       spin_lock(&ipvs->sctp_app_lock);
-       list_for_each_entry(inc, &ipvs->sctp_apps[hash], p_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(inc, &ipvs->sctp_apps[hash], p_list) {
                if (inc->port == cp->vport) {
                        if (unlikely(!ip_vs_app_inc_get(inc)))
                                break;
-                       spin_unlock(&ipvs->sctp_app_lock);
+                       rcu_read_unlock();
 
                        IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->"
                                        "%s:%u to app %s on port %u\n",
@@ -1076,7 +1071,7 @@ static int sctp_app_conn_bind(struct ip_vs_conn *cp)
                        goto out;
                }
        }
-       spin_unlock(&ipvs->sctp_app_lock);
+       rcu_read_unlock();
 out:
        return result;
 }
@@ -1090,7 +1085,6 @@ static int __ip_vs_sctp_init(struct net *net, struct ip_vs_proto_data *pd)
        struct netns_ipvs *ipvs = net_ipvs(net);
 
        ip_vs_init_hash_table(ipvs->sctp_apps, SCTP_APP_TAB_SIZE);
-       spin_lock_init(&ipvs->sctp_app_lock);
        pd->timeout_table = ip_vs_create_timeout_table((int *)sctp_timeouts,
                                                        sizeof(sctp_timeouts));
        if (!pd->timeout_table)
index 9af653a75825d0568450445ee3f5828622ac997c..50a15944c6c147ddd2fc0ca2e396955e7ea96da0 100644 (file)
@@ -47,9 +47,10 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
        }
        net = skb_net(skb);
        /* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */
+       rcu_read_lock();
        if (th->syn &&
-           (svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
-                                    &iph->daddr, th->dest))) {
+           (svc = ip_vs_service_find(net, af, skb->mark, iph->protocol,
+                                     &iph->daddr, th->dest))) {
                int ignored;
 
                if (ip_vs_todrop(net_ipvs(net))) {
@@ -57,7 +58,7 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                         * It seems that we are very loaded.
                         * We have to drop this packet :(
                         */
-                       ip_vs_service_put(svc);
+                       rcu_read_unlock();
                        *verdict = NF_DROP;
                        return 0;
                }
@@ -70,14 +71,13 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                if (!*cpp && ignored <= 0) {
                        if (!ignored)
                                *verdict = ip_vs_leave(svc, skb, pd, iph);
-                       else {
-                               ip_vs_service_put(svc);
+                       else
                                *verdict = NF_DROP;
-                       }
+                       rcu_read_unlock();
                        return 0;
                }
-               ip_vs_service_put(svc);
        }
+       rcu_read_unlock();
        /* NF_ACCEPT */
        return 1;
 }
@@ -557,9 +557,9 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction,
        if (th == NULL)
                return;
 
-       spin_lock(&cp->lock);
+       spin_lock_bh(&cp->lock);
        set_tcp_state(pd, cp, direction, th);
-       spin_unlock(&cp->lock);
+       spin_unlock_bh(&cp->lock);
 }
 
 static inline __u16 tcp_app_hashkey(__be16 port)
@@ -580,18 +580,16 @@ static int tcp_register_app(struct net *net, struct ip_vs_app *inc)
 
        hash = tcp_app_hashkey(port);
 
-       spin_lock_bh(&ipvs->tcp_app_lock);
        list_for_each_entry(i, &ipvs->tcp_apps[hash], p_list) {
                if (i->port == port) {
                        ret = -EEXIST;
                        goto out;
                }
        }
-       list_add(&inc->p_list, &ipvs->tcp_apps[hash]);
+       list_add_rcu(&inc->p_list, &ipvs->tcp_apps[hash]);
        atomic_inc(&pd->appcnt);
 
   out:
-       spin_unlock_bh(&ipvs->tcp_app_lock);
        return ret;
 }
 
@@ -599,13 +597,10 @@ static int tcp_register_app(struct net *net, struct ip_vs_app *inc)
 static void
 tcp_unregister_app(struct net *net, struct ip_vs_app *inc)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
 
-       spin_lock_bh(&ipvs->tcp_app_lock);
        atomic_dec(&pd->appcnt);
-       list_del(&inc->p_list);
-       spin_unlock_bh(&ipvs->tcp_app_lock);
+       list_del_rcu(&inc->p_list);
 }
 
 
@@ -624,12 +619,12 @@ tcp_app_conn_bind(struct ip_vs_conn *cp)
        /* Lookup application incarnations and bind the right one */
        hash = tcp_app_hashkey(cp->vport);
 
-       spin_lock(&ipvs->tcp_app_lock);
-       list_for_each_entry(inc, &ipvs->tcp_apps[hash], p_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(inc, &ipvs->tcp_apps[hash], p_list) {
                if (inc->port == cp->vport) {
                        if (unlikely(!ip_vs_app_inc_get(inc)))
                                break;
-                       spin_unlock(&ipvs->tcp_app_lock);
+                       rcu_read_unlock();
 
                        IP_VS_DBG_BUF(9, "%s(): Binding conn %s:%u->"
                                      "%s:%u to app %s on port %u\n",
@@ -646,7 +641,7 @@ tcp_app_conn_bind(struct ip_vs_conn *cp)
                        goto out;
                }
        }
-       spin_unlock(&ipvs->tcp_app_lock);
+       rcu_read_unlock();
 
   out:
        return result;
@@ -660,11 +655,11 @@ void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp)
 {
        struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
 
-       spin_lock(&cp->lock);
+       spin_lock_bh(&cp->lock);
        cp->state = IP_VS_TCP_S_LISTEN;
        cp->timeout = (pd ? pd->timeout_table[IP_VS_TCP_S_LISTEN]
                           : tcp_timeouts[IP_VS_TCP_S_LISTEN]);
-       spin_unlock(&cp->lock);
+       spin_unlock_bh(&cp->lock);
 }
 
 /* ---------------------------------------------
@@ -676,7 +671,6 @@ static int __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd)
        struct netns_ipvs *ipvs = net_ipvs(net);
 
        ip_vs_init_hash_table(ipvs->tcp_apps, TCP_APP_TAB_SIZE);
-       spin_lock_init(&ipvs->tcp_app_lock);
        pd->timeout_table = ip_vs_create_timeout_table((int *)tcp_timeouts,
                                                        sizeof(tcp_timeouts));
        if (!pd->timeout_table)
index 503a842c90d271bac3a1c8386db92611f2888502..b62a3c0ff9bf400817b20ec1ea4056586ed5d1dc 100644 (file)
@@ -44,8 +44,9 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                return 0;
        }
        net = skb_net(skb);
-       svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
-                               &iph->daddr, uh->dest);
+       rcu_read_lock();
+       svc = ip_vs_service_find(net, af, skb->mark, iph->protocol,
+                                &iph->daddr, uh->dest);
        if (svc) {
                int ignored;
 
@@ -54,7 +55,7 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                         * It seems that we are very loaded.
                         * We have to drop this packet :(
                         */
-                       ip_vs_service_put(svc);
+                       rcu_read_unlock();
                        *verdict = NF_DROP;
                        return 0;
                }
@@ -67,14 +68,13 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                if (!*cpp && ignored <= 0) {
                        if (!ignored)
                                *verdict = ip_vs_leave(svc, skb, pd, iph);
-                       else {
-                               ip_vs_service_put(svc);
+                       else
                                *verdict = NF_DROP;
-                       }
+                       rcu_read_unlock();
                        return 0;
                }
-               ip_vs_service_put(svc);
        }
+       rcu_read_unlock();
        /* NF_ACCEPT */
        return 1;
 }
@@ -359,19 +359,16 @@ static int udp_register_app(struct net *net, struct ip_vs_app *inc)
 
        hash = udp_app_hashkey(port);
 
-
-       spin_lock_bh(&ipvs->udp_app_lock);
        list_for_each_entry(i, &ipvs->udp_apps[hash], p_list) {
                if (i->port == port) {
                        ret = -EEXIST;
                        goto out;
                }
        }
-       list_add(&inc->p_list, &ipvs->udp_apps[hash]);
+       list_add_rcu(&inc->p_list, &ipvs->udp_apps[hash]);
        atomic_inc(&pd->appcnt);
 
   out:
-       spin_unlock_bh(&ipvs->udp_app_lock);
        return ret;
 }
 
@@ -380,12 +377,9 @@ static void
 udp_unregister_app(struct net *net, struct ip_vs_app *inc)
 {
        struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_UDP);
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
-       spin_lock_bh(&ipvs->udp_app_lock);
        atomic_dec(&pd->appcnt);
-       list_del(&inc->p_list);
-       spin_unlock_bh(&ipvs->udp_app_lock);
+       list_del_rcu(&inc->p_list);
 }
 
 
@@ -403,12 +397,12 @@ static int udp_app_conn_bind(struct ip_vs_conn *cp)
        /* Lookup application incarnations and bind the right one */
        hash = udp_app_hashkey(cp->vport);
 
-       spin_lock(&ipvs->udp_app_lock);
-       list_for_each_entry(inc, &ipvs->udp_apps[hash], p_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(inc, &ipvs->udp_apps[hash], p_list) {
                if (inc->port == cp->vport) {
                        if (unlikely(!ip_vs_app_inc_get(inc)))
                                break;
-                       spin_unlock(&ipvs->udp_app_lock);
+                       rcu_read_unlock();
 
                        IP_VS_DBG_BUF(9, "%s(): Binding conn %s:%u->"
                                      "%s:%u to app %s on port %u\n",
@@ -425,7 +419,7 @@ static int udp_app_conn_bind(struct ip_vs_conn *cp)
                        goto out;
                }
        }
-       spin_unlock(&ipvs->udp_app_lock);
+       rcu_read_unlock();
 
   out:
        return result;
@@ -467,7 +461,6 @@ static int __udp_init(struct net *net, struct ip_vs_proto_data *pd)
        struct netns_ipvs *ipvs = net_ipvs(net);
 
        ip_vs_init_hash_table(ipvs->udp_apps, UDP_APP_TAB_SIZE);
-       spin_lock_init(&ipvs->udp_app_lock);
        pd->timeout_table = ip_vs_create_timeout_table((int *)udp_timeouts,
                                                        sizeof(udp_timeouts));
        if (!pd->timeout_table)
index c49b388d1085238ce435fec81fa8f97da6a8ee93..c35986c793d903d3565be462321c6e953acf556e 100644 (file)
@@ -35,9 +35,18 @@ static int ip_vs_rr_init_svc(struct ip_vs_service *svc)
 }
 
 
-static int ip_vs_rr_update_svc(struct ip_vs_service *svc)
+static int ip_vs_rr_del_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest)
 {
-       svc->sched_data = &svc->destinations;
+       struct list_head *p;
+
+       spin_lock_bh(&svc->sched_lock);
+       p = (struct list_head *) svc->sched_data;
+       /* dest is already unlinked, so p->prev is not valid but
+        * p->next is valid, use it to reach previous entry.
+        */
+       if (p == &dest->n_list)
+               svc->sched_data = p->next->prev;
+       spin_unlock_bh(&svc->sched_lock);
        return 0;
 }
 
@@ -48,36 +57,41 @@ static int ip_vs_rr_update_svc(struct ip_vs_service *svc)
 static struct ip_vs_dest *
 ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
 {
-       struct list_head *p, *q;
-       struct ip_vs_dest *dest;
+       struct list_head *p;
+       struct ip_vs_dest *dest, *last;
+       int pass = 0;
 
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
-       write_lock(&svc->sched_lock);
-       p = (struct list_head *)svc->sched_data;
-       p = p->next;
-       q = p;
+       spin_lock_bh(&svc->sched_lock);
+       p = (struct list_head *) svc->sched_data;
+       last = dest = list_entry(p, struct ip_vs_dest, n_list);
+
        do {
-               /* skip list head */
-               if (q == &svc->destinations) {
-                       q = q->next;
-                       continue;
+               list_for_each_entry_continue_rcu(dest,
+                                                &svc->destinations,
+                                                n_list) {
+                       if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+                           atomic_read(&dest->weight) > 0)
+                               /* HIT */
+                               goto out;
+                       if (dest == last)
+                               goto stop;
                }
-
-               dest = list_entry(q, struct ip_vs_dest, n_list);
-               if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
-                   atomic_read(&dest->weight) > 0)
-                       /* HIT */
-                       goto out;
-               q = q->next;
-       } while (q != p);
-       write_unlock(&svc->sched_lock);
+               pass++;
+               /* Previous dest could be unlinked, do not loop forever.
+                * If we stay at head there is no need for 2nd pass.
+                */
+       } while (pass < 2 && p != &svc->destinations);
+
+stop:
+       spin_unlock_bh(&svc->sched_lock);
        ip_vs_scheduler_err(svc, "no destination available");
        return NULL;
 
   out:
-       svc->sched_data = q;
-       write_unlock(&svc->sched_lock);
+       svc->sched_data = &dest->n_list;
+       spin_unlock_bh(&svc->sched_lock);
        IP_VS_DBG_BUF(6, "RR: server %s:%u "
                      "activeconns %d refcnt %d weight %d\n",
                      IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port),
@@ -94,7 +108,8 @@ static struct ip_vs_scheduler ip_vs_rr_scheduler = {
        .module =               THIS_MODULE,
        .n_list =               LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),
        .init_service =         ip_vs_rr_init_svc,
-       .update_service =       ip_vs_rr_update_svc,
+       .add_dest =             NULL,
+       .del_dest =             ip_vs_rr_del_dest,
        .schedule =             ip_vs_rr_schedule,
 };
 
@@ -106,6 +121,7 @@ static int __init ip_vs_rr_init(void)
 static void __exit ip_vs_rr_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_rr_scheduler);
+       synchronize_rcu();
 }
 
 module_init(ip_vs_rr_init);
index d6bf20d6cdbef724e310ed26aca7b0098bcf9182..4dbcda6258bc2c2c20ecc71e130d25a462a90be4 100644 (file)
@@ -35,8 +35,8 @@ EXPORT_SYMBOL(ip_vs_scheduler_err);
  */
 static LIST_HEAD(ip_vs_schedulers);
 
-/* lock for service table */
-static DEFINE_SPINLOCK(ip_vs_sched_lock);
+/* semaphore for schedulers */
+static DEFINE_MUTEX(ip_vs_sched_mutex);
 
 
 /*
@@ -47,8 +47,6 @@ int ip_vs_bind_scheduler(struct ip_vs_service *svc,
 {
        int ret;
 
-       svc->scheduler = scheduler;
-
        if (scheduler->init_service) {
                ret = scheduler->init_service(svc);
                if (ret) {
@@ -56,7 +54,7 @@ int ip_vs_bind_scheduler(struct ip_vs_service *svc,
                        return ret;
                }
        }
-
+       rcu_assign_pointer(svc->scheduler, scheduler);
        return 0;
 }
 
@@ -64,22 +62,19 @@ int ip_vs_bind_scheduler(struct ip_vs_service *svc,
 /*
  *  Unbind a service with its scheduler
  */
-int ip_vs_unbind_scheduler(struct ip_vs_service *svc)
+void ip_vs_unbind_scheduler(struct ip_vs_service *svc,
+                           struct ip_vs_scheduler *sched)
 {
-       struct ip_vs_scheduler *sched = svc->scheduler;
+       struct ip_vs_scheduler *cur_sched;
 
-       if (!sched)
-               return 0;
+       cur_sched = rcu_dereference_protected(svc->scheduler, 1);
+       /* This check proves that old 'sched' was installed */
+       if (!cur_sched)
+               return;
 
-       if (sched->done_service) {
-               if (sched->done_service(svc) != 0) {
-                       pr_err("%s(): done error\n", __func__);
-                       return -EINVAL;
-               }
-       }
-
-       svc->scheduler = NULL;
-       return 0;
+       if (sched->done_service)
+               sched->done_service(svc);
+       /* svc->scheduler can not be set to NULL */
 }
 
 
@@ -92,7 +87,7 @@ static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name)
 
        IP_VS_DBG(2, "%s(): sched_name \"%s\"\n", __func__, sched_name);
 
-       spin_lock_bh(&ip_vs_sched_lock);
+       mutex_lock(&ip_vs_sched_mutex);
 
        list_for_each_entry(sched, &ip_vs_schedulers, n_list) {
                /*
@@ -106,14 +101,14 @@ static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name)
                }
                if (strcmp(sched_name, sched->name)==0) {
                        /* HIT */
-                       spin_unlock_bh(&ip_vs_sched_lock);
+                       mutex_unlock(&ip_vs_sched_mutex);
                        return sched;
                }
                if (sched->module)
                        module_put(sched->module);
        }
 
-       spin_unlock_bh(&ip_vs_sched_lock);
+       mutex_unlock(&ip_vs_sched_mutex);
        return NULL;
 }
 
@@ -153,21 +148,21 @@ void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler)
 
 void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg)
 {
+       struct ip_vs_scheduler *sched;
+
+       sched = rcu_dereference(svc->scheduler);
        if (svc->fwmark) {
                IP_VS_ERR_RL("%s: FWM %u 0x%08X - %s\n",
-                            svc->scheduler->name, svc->fwmark,
-                            svc->fwmark, msg);
+                            sched->name, svc->fwmark, svc->fwmark, msg);
 #ifdef CONFIG_IP_VS_IPV6
        } else if (svc->af == AF_INET6) {
                IP_VS_ERR_RL("%s: %s [%pI6c]:%d - %s\n",
-                            svc->scheduler->name,
-                            ip_vs_proto_name(svc->protocol),
+                            sched->name, ip_vs_proto_name(svc->protocol),
                             &svc->addr.in6, ntohs(svc->port), msg);
 #endif
        } else {
                IP_VS_ERR_RL("%s: %s %pI4:%d - %s\n",
-                            svc->scheduler->name,
-                            ip_vs_proto_name(svc->protocol),
+                            sched->name, ip_vs_proto_name(svc->protocol),
                             &svc->addr.ip, ntohs(svc->port), msg);
        }
 }
@@ -192,10 +187,10 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler)
        /* increase the module use count */
        ip_vs_use_count_inc();
 
-       spin_lock_bh(&ip_vs_sched_lock);
+       mutex_lock(&ip_vs_sched_mutex);
 
        if (!list_empty(&scheduler->n_list)) {
-               spin_unlock_bh(&ip_vs_sched_lock);
+               mutex_unlock(&ip_vs_sched_mutex);
                ip_vs_use_count_dec();
                pr_err("%s(): [%s] scheduler already linked\n",
                       __func__, scheduler->name);
@@ -208,7 +203,7 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler)
         */
        list_for_each_entry(sched, &ip_vs_schedulers, n_list) {
                if (strcmp(scheduler->name, sched->name) == 0) {
-                       spin_unlock_bh(&ip_vs_sched_lock);
+                       mutex_unlock(&ip_vs_sched_mutex);
                        ip_vs_use_count_dec();
                        pr_err("%s(): [%s] scheduler already existed "
                               "in the system\n", __func__, scheduler->name);
@@ -219,7 +214,7 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler)
         *      Add it into the d-linked scheduler list
         */
        list_add(&scheduler->n_list, &ip_vs_schedulers);
-       spin_unlock_bh(&ip_vs_sched_lock);
+       mutex_unlock(&ip_vs_sched_mutex);
 
        pr_info("[%s] scheduler registered.\n", scheduler->name);
 
@@ -237,9 +232,9 @@ int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler)
                return -EINVAL;
        }
 
-       spin_lock_bh(&ip_vs_sched_lock);
+       mutex_lock(&ip_vs_sched_mutex);
        if (list_empty(&scheduler->n_list)) {
-               spin_unlock_bh(&ip_vs_sched_lock);
+               mutex_unlock(&ip_vs_sched_mutex);
                pr_err("%s(): [%s] scheduler is not in the list. failed\n",
                       __func__, scheduler->name);
                return -EINVAL;
@@ -249,7 +244,7 @@ int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler)
         *      Remove it from the d-linked scheduler list
         */
        list_del(&scheduler->n_list);
-       spin_unlock_bh(&ip_vs_sched_lock);
+       mutex_unlock(&ip_vs_sched_mutex);
 
        /* decrease the module use count */
        ip_vs_use_count_dec();
index 89ead246ed3d250aa24bc2e195f4901f7b73b1d3..f3205925359acc19790795fdd67d87970b58e3b7 100644 (file)
@@ -79,7 +79,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
         * new connections.
         */
 
-       list_for_each_entry(dest, &svc->destinations, n_list) {
+       list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
                if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
                    atomic_read(&dest->weight) > 0) {
                        least = dest;
@@ -94,7 +94,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
         *    Find the destination with the least load.
         */
   nextstage:
-       list_for_each_entry_continue(dest, &svc->destinations, n_list) {
+       list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
                doh = ip_vs_sed_dest_overhead(dest);
@@ -134,6 +134,7 @@ static int __init ip_vs_sed_init(void)
 static void __exit ip_vs_sed_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_sed_scheduler);
+       synchronize_rcu();
 }
 
 module_init(ip_vs_sed_init);
index e331269946287a42faad5c78fb0b985dd3e0dcbf..0df269d7c99f6d9513cae41e46a5879f691a423b 100644 (file)
@@ -53,7 +53,7 @@
  *      IPVS SH bucket
  */
 struct ip_vs_sh_bucket {
-       struct ip_vs_dest       *dest;          /* real server (cache) */
+       struct ip_vs_dest __rcu *dest;  /* real server (cache) */
 };
 
 /*
@@ -66,6 +66,10 @@ struct ip_vs_sh_bucket {
 #define IP_VS_SH_TAB_SIZE               (1 << IP_VS_SH_TAB_BITS)
 #define IP_VS_SH_TAB_MASK               (IP_VS_SH_TAB_SIZE - 1)
 
+struct ip_vs_sh_state {
+       struct ip_vs_sh_bucket          buckets[IP_VS_SH_TAB_SIZE];
+       struct rcu_head                 rcu_head;
+};
 
 /*
  *     Returns hash value for IPVS SH entry
@@ -87,10 +91,9 @@ static inline unsigned int ip_vs_sh_hashkey(int af, const union nf_inet_addr *ad
  *      Get ip_vs_dest associated with supplied parameters.
  */
 static inline struct ip_vs_dest *
-ip_vs_sh_get(int af, struct ip_vs_sh_bucket *tbl,
-            const union nf_inet_addr *addr)
+ip_vs_sh_get(int af, struct ip_vs_sh_state *s, const union nf_inet_addr *addr)
 {
-       return (tbl[ip_vs_sh_hashkey(af, addr)]).dest;
+       return rcu_dereference(s->buckets[ip_vs_sh_hashkey(af, addr)].dest);
 }
 
 
@@ -98,27 +101,32 @@ ip_vs_sh_get(int af, struct ip_vs_sh_bucket *tbl,
  *      Assign all the hash buckets of the specified table with the service.
  */
 static int
-ip_vs_sh_assign(struct ip_vs_sh_bucket *tbl, struct ip_vs_service *svc)
+ip_vs_sh_reassign(struct ip_vs_sh_state *s, struct ip_vs_service *svc)
 {
        int i;
        struct ip_vs_sh_bucket *b;
        struct list_head *p;
        struct ip_vs_dest *dest;
        int d_count;
+       bool empty;
 
-       b = tbl;
+       b = &s->buckets[0];
        p = &svc->destinations;
+       empty = list_empty(p);
        d_count = 0;
        for (i=0; i<IP_VS_SH_TAB_SIZE; i++) {
-               if (list_empty(p)) {
-                       b->dest = NULL;
-               } else {
+               dest = rcu_dereference_protected(b->dest, 1);
+               if (dest)
+                       ip_vs_dest_put(dest);
+               if (empty)
+                       RCU_INIT_POINTER(b->dest, NULL);
+               else {
                        if (p == &svc->destinations)
                                p = p->next;
 
                        dest = list_entry(p, struct ip_vs_dest, n_list);
-                       atomic_inc(&dest->refcnt);
-                       b->dest = dest;
+                       ip_vs_dest_hold(dest);
+                       RCU_INIT_POINTER(b->dest, dest);
 
                        IP_VS_DBG_BUF(6, "assigned i: %d dest: %s weight: %d\n",
                                      i, IP_VS_DBG_ADDR(svc->af, &dest->addr),
@@ -140,16 +148,18 @@ ip_vs_sh_assign(struct ip_vs_sh_bucket *tbl, struct ip_vs_service *svc)
 /*
  *      Flush all the hash buckets of the specified table.
  */
-static void ip_vs_sh_flush(struct ip_vs_sh_bucket *tbl)
+static void ip_vs_sh_flush(struct ip_vs_sh_state *s)
 {
        int i;
        struct ip_vs_sh_bucket *b;
+       struct ip_vs_dest *dest;
 
-       b = tbl;
+       b = &s->buckets[0];
        for (i=0; i<IP_VS_SH_TAB_SIZE; i++) {
-               if (b->dest) {
-                       atomic_dec(&b->dest->refcnt);
-                       b->dest = NULL;
+               dest = rcu_dereference_protected(b->dest, 1);
+               if (dest) {
+                       ip_vs_dest_put(dest);
+                       RCU_INIT_POINTER(b->dest, NULL);
                }
                b++;
        }
@@ -158,51 +168,46 @@ static void ip_vs_sh_flush(struct ip_vs_sh_bucket *tbl)
 
 static int ip_vs_sh_init_svc(struct ip_vs_service *svc)
 {
-       struct ip_vs_sh_bucket *tbl;
+       struct ip_vs_sh_state *s;
 
        /* allocate the SH table for this service */
-       tbl = kmalloc(sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE,
-                     GFP_KERNEL);
-       if (tbl == NULL)
+       s = kzalloc(sizeof(struct ip_vs_sh_state), GFP_KERNEL);
+       if (s == NULL)
                return -ENOMEM;
 
-       svc->sched_data = tbl;
+       svc->sched_data = s;
        IP_VS_DBG(6, "SH hash table (memory=%Zdbytes) allocated for "
                  "current service\n",
                  sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE);
 
-       /* assign the hash buckets with the updated service */
-       ip_vs_sh_assign(tbl, svc);
+       /* assign the hash buckets with current dests */
+       ip_vs_sh_reassign(s, svc);
 
        return 0;
 }
 
 
-static int ip_vs_sh_done_svc(struct ip_vs_service *svc)
+static void ip_vs_sh_done_svc(struct ip_vs_service *svc)
 {
-       struct ip_vs_sh_bucket *tbl = svc->sched_data;
+       struct ip_vs_sh_state *s = svc->sched_data;
 
        /* got to clean up hash buckets here */
-       ip_vs_sh_flush(tbl);
+       ip_vs_sh_flush(s);
 
        /* release the table itself */
-       kfree(svc->sched_data);
+       kfree_rcu(s, rcu_head);
        IP_VS_DBG(6, "SH hash table (memory=%Zdbytes) released\n",
                  sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE);
-
-       return 0;
 }
 
 
-static int ip_vs_sh_update_svc(struct ip_vs_service *svc)
+static int ip_vs_sh_dest_changed(struct ip_vs_service *svc,
+                                struct ip_vs_dest *dest)
 {
-       struct ip_vs_sh_bucket *tbl = svc->sched_data;
-
-       /* got to clean up hash buckets here */
-       ip_vs_sh_flush(tbl);
+       struct ip_vs_sh_state *s = svc->sched_data;
 
        /* assign the hash buckets with the updated service */
-       ip_vs_sh_assign(tbl, svc);
+       ip_vs_sh_reassign(s, svc);
 
        return 0;
 }
@@ -225,15 +230,15 @@ static struct ip_vs_dest *
 ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
 {
        struct ip_vs_dest *dest;
-       struct ip_vs_sh_bucket *tbl;
+       struct ip_vs_sh_state *s;
        struct ip_vs_iphdr iph;
 
        ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
 
        IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n");
 
-       tbl = (struct ip_vs_sh_bucket *)svc->sched_data;
-       dest = ip_vs_sh_get(svc->af, tbl, &iph.saddr);
+       s = (struct ip_vs_sh_state *) svc->sched_data;
+       dest = ip_vs_sh_get(svc->af, s, &iph.saddr);
        if (!dest
            || !(dest->flags & IP_VS_DEST_F_AVAILABLE)
            || atomic_read(&dest->weight) <= 0
@@ -262,7 +267,9 @@ static struct ip_vs_scheduler ip_vs_sh_scheduler =
        .n_list  =              LIST_HEAD_INIT(ip_vs_sh_scheduler.n_list),
        .init_service =         ip_vs_sh_init_svc,
        .done_service =         ip_vs_sh_done_svc,
-       .update_service =       ip_vs_sh_update_svc,
+       .add_dest =             ip_vs_sh_dest_changed,
+       .del_dest =             ip_vs_sh_dest_changed,
+       .upd_dest =             ip_vs_sh_dest_changed,
        .schedule =             ip_vs_sh_schedule,
 };
 
@@ -276,6 +283,7 @@ static int __init ip_vs_sh_init(void)
 static void __exit ip_vs_sh_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_sh_scheduler);
+       synchronize_rcu();
 }
 
 
index 44fd10c539ac42dfaa967974051bdfacb17099fb..8e57077e55401907271fbb7349f5d88d153b57bc 100644 (file)
@@ -531,9 +531,9 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
        if (!ip_vs_sync_conn_needed(ipvs, cp, pkts))
                return;
 
-       spin_lock(&ipvs->sync_buff_lock);
+       spin_lock_bh(&ipvs->sync_buff_lock);
        if (!(ipvs->sync_state & IP_VS_STATE_MASTER)) {
-               spin_unlock(&ipvs->sync_buff_lock);
+               spin_unlock_bh(&ipvs->sync_buff_lock);
                return;
        }
 
@@ -552,7 +552,7 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
        if (!buff) {
                buff = ip_vs_sync_buff_create_v0(ipvs);
                if (!buff) {
-                       spin_unlock(&ipvs->sync_buff_lock);
+                       spin_unlock_bh(&ipvs->sync_buff_lock);
                        pr_err("ip_vs_sync_buff_create failed.\n");
                        return;
                }
@@ -590,7 +590,7 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
                sb_queue_tail(ipvs, ms);
                ms->sync_buff = NULL;
        }
-       spin_unlock(&ipvs->sync_buff_lock);
+       spin_unlock_bh(&ipvs->sync_buff_lock);
 
        /* synchronize its controller if it has */
        cp = cp->control;
@@ -641,9 +641,9 @@ sloop:
                pe_name_len = strnlen(cp->pe->name, IP_VS_PENAME_MAXLEN);
        }
 
-       spin_lock(&ipvs->sync_buff_lock);
+       spin_lock_bh(&ipvs->sync_buff_lock);
        if (!(ipvs->sync_state & IP_VS_STATE_MASTER)) {
-               spin_unlock(&ipvs->sync_buff_lock);
+               spin_unlock_bh(&ipvs->sync_buff_lock);
                return;
        }
 
@@ -683,7 +683,7 @@ sloop:
        if (!buff) {
                buff = ip_vs_sync_buff_create(ipvs);
                if (!buff) {
-                       spin_unlock(&ipvs->sync_buff_lock);
+                       spin_unlock_bh(&ipvs->sync_buff_lock);
                        pr_err("ip_vs_sync_buff_create failed.\n");
                        return;
                }
@@ -750,7 +750,7 @@ sloop:
                }
        }
 
-       spin_unlock(&ipvs->sync_buff_lock);
+       spin_unlock_bh(&ipvs->sync_buff_lock);
 
 control:
        /* synchronize its controller if it has */
@@ -843,7 +843,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
                kfree(param->pe_data);
 
                dest = cp->dest;
-               spin_lock(&cp->lock);
+               spin_lock_bh(&cp->lock);
                if ((cp->flags ^ flags) & IP_VS_CONN_F_INACTIVE &&
                    !(flags & IP_VS_CONN_F_TEMPLATE) && dest) {
                        if (flags & IP_VS_CONN_F_INACTIVE) {
@@ -857,24 +857,21 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
                flags &= IP_VS_CONN_F_BACKUP_UPD_MASK;
                flags |= cp->flags & ~IP_VS_CONN_F_BACKUP_UPD_MASK;
                cp->flags = flags;
-               spin_unlock(&cp->lock);
-               if (!dest) {
-                       dest = ip_vs_try_bind_dest(cp);
-                       if (dest)
-                               atomic_dec(&dest->refcnt);
-               }
+               spin_unlock_bh(&cp->lock);
+               if (!dest)
+                       ip_vs_try_bind_dest(cp);
        } else {
                /*
                 * Find the appropriate destination for the connection.
                 * If it is not found the connection will remain unbound
                 * but still handled.
                 */
+               rcu_read_lock();
                dest = ip_vs_find_dest(net, type, daddr, dport, param->vaddr,
                                       param->vport, protocol, fwmark, flags);
 
                cp = ip_vs_conn_new(param, daddr, dport, flags, dest, fwmark);
-               if (dest)
-                       atomic_dec(&dest->refcnt);
+               rcu_read_unlock();
                if (!cp) {
                        if (param->pe_data)
                                kfree(param->pe_data);
@@ -1692,11 +1689,7 @@ static int sync_thread_backup(void *data)
                                break;
                        }
 
-                       /* disable bottom half, because it accesses the data
-                          shared by softirq while getting/creating conns */
-                       local_bh_disable();
                        ip_vs_process_message(tinfo->net, tinfo->buf, len);
-                       local_bh_enable();
                }
        }
 
index bc1bfc48a17fcc2c01024dadc80592ded11b27f3..c60a81c4ce9add5b801fdff53e0596b6c77e036a 100644 (file)
@@ -51,7 +51,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
         * new connections.
         */
 
-       list_for_each_entry(dest, &svc->destinations, n_list) {
+       list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
                if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
                    atomic_read(&dest->weight) > 0) {
                        least = dest;
@@ -66,7 +66,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
         *    Find the destination with the least load.
         */
   nextstage:
-       list_for_each_entry_continue(dest, &svc->destinations, n_list) {
+       list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
                doh = ip_vs_dest_conn_overhead(dest);
@@ -106,6 +106,7 @@ static int __init ip_vs_wlc_init(void)
 static void __exit ip_vs_wlc_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_wlc_scheduler);
+       synchronize_rcu();
 }
 
 module_init(ip_vs_wlc_init);
index 231be7dd547a9cd5fdf1a7742305b85717e99aab..0e68555bceb9122a00283c147c977fbba01e4079 100644 (file)
 
 #include <net/ip_vs.h>
 
+/* The WRR algorithm depends on some caclulations:
+ * - mw: maximum weight
+ * - di: weight step, greatest common divisor from all weights
+ * - cw: current required weight
+ * As result, all weights are in the [di..mw] range with a step=di.
+ *
+ * First, we start with cw = mw and select dests with weight >= cw.
+ * Then cw is reduced with di and all dests are checked again.
+ * Last pass should be with cw = di. We have mw/di passes in total:
+ *
+ * pass 1: cw = max weight
+ * pass 2: cw = max weight - di
+ * pass 3: cw = max weight - 2 * di
+ * ...
+ * last pass: cw = di
+ *
+ * Weights are supposed to be >= di but we run in parallel with
+ * weight changes, it is possible some dest weight to be reduced
+ * below di, bad if it is the only available dest.
+ *
+ * So, we modify how mw is calculated, now it is reduced with (di - 1),
+ * so that last cw is 1 to catch such dests with weight below di:
+ * pass 1: cw = max weight - (di - 1)
+ * pass 2: cw = max weight - di - (di - 1)
+ * pass 3: cw = max weight - 2 * di - (di - 1)
+ * ...
+ * last pass: cw = 1
+ *
+ */
+
 /*
  * current destination pointer for weighted round-robin scheduling
  */
 struct ip_vs_wrr_mark {
-       struct list_head *cl;   /* current list head */
+       struct ip_vs_dest *cl;  /* current dest or head */
        int cw;                 /* current weight */
        int mw;                 /* maximum weight */
        int di;                 /* decreasing interval */
+       struct rcu_head         rcu_head;
 };
 
 
@@ -88,36 +119,41 @@ static int ip_vs_wrr_init_svc(struct ip_vs_service *svc)
        if (mark == NULL)
                return -ENOMEM;
 
-       mark->cl = &svc->destinations;
-       mark->cw = 0;
-       mark->mw = ip_vs_wrr_max_weight(svc);
+       mark->cl = list_entry(&svc->destinations, struct ip_vs_dest, n_list);
        mark->di = ip_vs_wrr_gcd_weight(svc);
+       mark->mw = ip_vs_wrr_max_weight(svc) - (mark->di - 1);
+       mark->cw = mark->mw;
        svc->sched_data = mark;
 
        return 0;
 }
 
 
-static int ip_vs_wrr_done_svc(struct ip_vs_service *svc)
+static void ip_vs_wrr_done_svc(struct ip_vs_service *svc)
 {
+       struct ip_vs_wrr_mark *mark = svc->sched_data;
+
        /*
         *    Release the mark variable
         */
-       kfree(svc->sched_data);
-
-       return 0;
+       kfree_rcu(mark, rcu_head);
 }
 
 
-static int ip_vs_wrr_update_svc(struct ip_vs_service *svc)
+static int ip_vs_wrr_dest_changed(struct ip_vs_service *svc,
+                                 struct ip_vs_dest *dest)
 {
        struct ip_vs_wrr_mark *mark = svc->sched_data;
 
-       mark->cl = &svc->destinations;
-       mark->mw = ip_vs_wrr_max_weight(svc);
+       spin_lock_bh(&svc->sched_lock);
+       mark->cl = list_entry(&svc->destinations, struct ip_vs_dest, n_list);
        mark->di = ip_vs_wrr_gcd_weight(svc);
-       if (mark->cw > mark->mw)
-               mark->cw = 0;
+       mark->mw = ip_vs_wrr_max_weight(svc) - (mark->di - 1);
+       if (mark->cw > mark->mw || !mark->cw)
+               mark->cw = mark->mw;
+       else if (mark->di > 1)
+               mark->cw = (mark->cw / mark->di) * mark->di + 1;
+       spin_unlock_bh(&svc->sched_lock);
        return 0;
 }
 
@@ -128,80 +164,79 @@ static int ip_vs_wrr_update_svc(struct ip_vs_service *svc)
 static struct ip_vs_dest *
 ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
 {
-       struct ip_vs_dest *dest;
+       struct ip_vs_dest *dest, *last, *stop = NULL;
        struct ip_vs_wrr_mark *mark = svc->sched_data;
-       struct list_head *p;
+       bool last_pass = false, restarted = false;
 
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
-       /*
-        * This loop will always terminate, because mark->cw in (0, max_weight]
-        * and at least one server has its weight equal to max_weight.
-        */
-       write_lock(&svc->sched_lock);
-       p = mark->cl;
+       spin_lock_bh(&svc->sched_lock);
+       dest = mark->cl;
+       /* No available dests? */
+       if (mark->mw == 0)
+               goto err_noavail;
+       last = dest;
+       /* Stop only after all dests were checked for weight >= 1 (last pass) */
        while (1) {
-               if (mark->cl == &svc->destinations) {
-                       /* it is at the head of the destination list */
-
-                       if (mark->cl == mark->cl->next) {
-                               /* no dest entry */
-                               ip_vs_scheduler_err(svc,
-                                       "no destination available: "
-                                       "no destinations present");
-                               dest = NULL;
-                               goto out;
-                       }
-
-                       mark->cl = svc->destinations.next;
-                       mark->cw -= mark->di;
-                       if (mark->cw <= 0) {
-                               mark->cw = mark->mw;
-                               /*
-                                * Still zero, which means no available servers.
-                                */
-                               if (mark->cw == 0) {
-                                       mark->cl = &svc->destinations;
-                                       ip_vs_scheduler_err(svc,
-                                               "no destination available");
-                                       dest = NULL;
-                                       goto out;
-                               }
-                       }
-               } else
-                       mark->cl = mark->cl->next;
-
-               if (mark->cl != &svc->destinations) {
-                       /* not at the head of the list */
-                       dest = list_entry(mark->cl, struct ip_vs_dest, n_list);
+               list_for_each_entry_continue_rcu(dest,
+                                                &svc->destinations,
+                                                n_list) {
                        if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
-                           atomic_read(&dest->weight) >= mark->cw) {
-                               /* got it */
-                               break;
-                       }
+                           atomic_read(&dest->weight) >= mark->cw)
+                               goto found;
+                       if (dest == stop)
+                               goto err_over;
                }
-
-               if (mark->cl == p && mark->cw == mark->di) {
-                       /* back to the start, and no dest is found.
-                          It is only possible when all dests are OVERLOADED */
-                       dest = NULL;
-                       ip_vs_scheduler_err(svc,
-                               "no destination available: "
-                               "all destinations are overloaded");
-                       goto out;
+               mark->cw -= mark->di;
+               if (mark->cw <= 0) {
+                       mark->cw = mark->mw;
+                       /* Stop if we tried last pass from first dest:
+                        * 1. last_pass: we started checks when cw > di but
+                        *      then all dests were checked for w >= 1
+                        * 2. last was head: the first and only traversal
+                        *      was for weight >= 1, for all dests.
+                        */
+                       if (last_pass ||
+                           &last->n_list == &svc->destinations)
+                               goto err_over;
+                       restarted = true;
+               }
+               last_pass = mark->cw <= mark->di;
+               if (last_pass && restarted &&
+                   &last->n_list != &svc->destinations) {
+                       /* First traversal was for w >= 1 but only
+                        * for dests after 'last', now do the same
+                        * for all dests up to 'last'.
+                        */
+                       stop = last;
                }
        }
 
+found:
        IP_VS_DBG_BUF(6, "WRR: server %s:%u "
                      "activeconns %d refcnt %d weight %d\n",
                      IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port),
                      atomic_read(&dest->activeconns),
                      atomic_read(&dest->refcnt),
                      atomic_read(&dest->weight));
+       mark->cl = dest;
 
   out:
-       write_unlock(&svc->sched_lock);
+       spin_unlock_bh(&svc->sched_lock);
        return dest;
+
+err_noavail:
+       mark->cl = dest;
+       dest = NULL;
+       ip_vs_scheduler_err(svc, "no destination available");
+       goto out;
+
+err_over:
+       mark->cl = dest;
+       dest = NULL;
+       ip_vs_scheduler_err(svc, "no destination available: "
+                           "all destinations are overloaded");
+       goto out;
 }
 
 
@@ -212,7 +247,9 @@ static struct ip_vs_scheduler ip_vs_wrr_scheduler = {
        .n_list =               LIST_HEAD_INIT(ip_vs_wrr_scheduler.n_list),
        .init_service =         ip_vs_wrr_init_svc,
        .done_service =         ip_vs_wrr_done_svc,
-       .update_service =       ip_vs_wrr_update_svc,
+       .add_dest =             ip_vs_wrr_dest_changed,
+       .del_dest =             ip_vs_wrr_dest_changed,
+       .upd_dest =             ip_vs_wrr_dest_changed,
        .schedule =             ip_vs_wrr_schedule,
 };
 
@@ -224,6 +261,7 @@ static int __init ip_vs_wrr_init(void)
 static void __exit ip_vs_wrr_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_wrr_scheduler);
+       synchronize_rcu();
 }
 
 module_init(ip_vs_wrr_init);
index ee6b7a9f1ec24e4f0a0049ff3af4c6fc73a29752..b75ff6429a04e25d4ba8e6368173ab84a342751c 100644 (file)
@@ -17,6 +17,8 @@
  * - not all connections have destination server, for example,
  * connections in backup server when fwmark is used
  * - bypass connections use daddr from packet
+ * - we can use dst without ref while sending in RCU section, we use
+ * ref when returning NF_ACCEPT for NAT-ed packet via loopback
  * LOCAL_OUT rules:
  * - skb->dev is NULL, skb->protocol is not set (both are set in POST_ROUTING)
  * - skb->pkt_type is not set yet
@@ -51,39 +53,54 @@ enum {
                                      */
        IP_VS_RT_MODE_CONNECT   = 8, /* Always bind route to saddr */
        IP_VS_RT_MODE_KNOWN_NH  = 16,/* Route via remote addr */
+       IP_VS_RT_MODE_TUNNEL    = 32,/* Tunnel mode */
 };
 
+static inline struct ip_vs_dest_dst *ip_vs_dest_dst_alloc(void)
+{
+       return kmalloc(sizeof(struct ip_vs_dest_dst), GFP_ATOMIC);
+}
+
+static inline void ip_vs_dest_dst_free(struct ip_vs_dest_dst *dest_dst)
+{
+       kfree(dest_dst);
+}
+
 /*
  *      Destination cache to speed up outgoing route lookup
  */
 static inline void
-__ip_vs_dst_set(struct ip_vs_dest *dest, u32 rtos, struct dst_entry *dst,
-               u32 dst_cookie)
+__ip_vs_dst_set(struct ip_vs_dest *dest, struct ip_vs_dest_dst *dest_dst,
+               struct dst_entry *dst, u32 dst_cookie)
 {
-       struct dst_entry *old_dst;
+       struct ip_vs_dest_dst *old;
+
+       old = rcu_dereference_protected(dest->dest_dst,
+                                       lockdep_is_held(&dest->dst_lock));
 
-       old_dst = dest->dst_cache;
-       dest->dst_cache = dst;
-       dest->dst_rtos = rtos;
-       dest->dst_cookie = dst_cookie;
-       dst_release(old_dst);
+       if (dest_dst) {
+               dest_dst->dst_cache = dst;
+               dest_dst->dst_cookie = dst_cookie;
+       }
+       rcu_assign_pointer(dest->dest_dst, dest_dst);
+
+       if (old)
+               call_rcu(&old->rcu_head, ip_vs_dest_dst_rcu_free);
 }
 
-static inline struct dst_entry *
-__ip_vs_dst_check(struct ip_vs_dest *dest, u32 rtos)
+static inline struct ip_vs_dest_dst *
+__ip_vs_dst_check(struct ip_vs_dest *dest)
 {
-       struct dst_entry *dst = dest->dst_cache;
+       struct ip_vs_dest_dst *dest_dst = rcu_dereference(dest->dest_dst);
+       struct dst_entry *dst;
 
-       if (!dst)
+       if (!dest_dst)
                return NULL;
-       if ((dst->obsolete || rtos != dest->dst_rtos) &&
-           dst->ops->check(dst, dest->dst_cookie) == NULL) {
-               dest->dst_cache = NULL;
-               dst_release(dst);
+       dst = dest_dst->dst_cache;
+       if (dst->obsolete &&
+           dst->ops->check(dst, dest_dst->dst_cookie) == NULL)
                return NULL;
-       }
-       dst_hold(dst);
-       return dst;
+       return dest_dst;
 }
 
 static inline bool
@@ -104,7 +121,7 @@ __mtu_check_toobig_v6(const struct sk_buff *skb, u32 mtu)
 
 /* Get route to daddr, update *saddr, optionally bind route to saddr */
 static struct rtable *do_output_route4(struct net *net, __be32 daddr,
-                                      u32 rtos, int rt_mode, __be32 *saddr)
+                                      int rt_mode, __be32 *saddr)
 {
        struct flowi4 fl4;
        struct rtable *rt;
@@ -113,7 +130,6 @@ static struct rtable *do_output_route4(struct net *net, __be32 daddr,
        memset(&fl4, 0, sizeof(fl4));
        fl4.daddr = daddr;
        fl4.saddr = (rt_mode & IP_VS_RT_MODE_CONNECT) ? *saddr : 0;
-       fl4.flowi4_tos = rtos;
        fl4.flowi4_flags = (rt_mode & IP_VS_RT_MODE_KNOWN_NH) ?
                           FLOWI_FLAG_KNOWN_NH : 0;
 
@@ -124,7 +140,7 @@ retry:
                if (PTR_ERR(rt) == -EINVAL && *saddr &&
                    rt_mode & IP_VS_RT_MODE_CONNECT && !loop) {
                        *saddr = 0;
-                       flowi4_update_output(&fl4, 0, rtos, daddr, 0);
+                       flowi4_update_output(&fl4, 0, 0, daddr, 0);
                        goto retry;
                }
                IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n", &daddr);
@@ -132,7 +148,7 @@ retry:
        } else if (!*saddr && rt_mode & IP_VS_RT_MODE_CONNECT && fl4.saddr) {
                ip_rt_put(rt);
                *saddr = fl4.saddr;
-               flowi4_update_output(&fl4, 0, rtos, daddr, fl4.saddr);
+               flowi4_update_output(&fl4, 0, 0, daddr, fl4.saddr);
                loop++;
                goto retry;
        }
@@ -141,113 +157,140 @@ retry:
 }
 
 /* Get route to destination or remote server */
-static struct rtable *
+static int
 __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest,
-                  __be32 daddr, u32 rtos, int rt_mode, __be32 *ret_saddr)
+                  __be32 daddr, int rt_mode, __be32 *ret_saddr)
 {
        struct net *net = dev_net(skb_dst(skb)->dev);
+       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct ip_vs_dest_dst *dest_dst;
        struct rtable *rt;                      /* Route to the other host */
        struct rtable *ort;                     /* Original route */
-       int local;
+       struct iphdr *iph;
+       __be16 df;
+       int mtu;
+       int local, noref = 1;
 
        if (dest) {
-               spin_lock(&dest->dst_lock);
-               if (!(rt = (struct rtable *)
-                     __ip_vs_dst_check(dest, rtos))) {
-                       rt = do_output_route4(net, dest->addr.ip, rtos,
-                                             rt_mode, &dest->dst_saddr.ip);
+               dest_dst = __ip_vs_dst_check(dest);
+               if (likely(dest_dst))
+                       rt = (struct rtable *) dest_dst->dst_cache;
+               else {
+                       dest_dst = ip_vs_dest_dst_alloc();
+                       spin_lock_bh(&dest->dst_lock);
+                       if (!dest_dst) {
+                               __ip_vs_dst_set(dest, NULL, NULL, 0);
+                               spin_unlock_bh(&dest->dst_lock);
+                               goto err_unreach;
+                       }
+                       rt = do_output_route4(net, dest->addr.ip, rt_mode,
+                                             &dest_dst->dst_saddr.ip);
                        if (!rt) {
-                               spin_unlock(&dest->dst_lock);
-                               return NULL;
+                               __ip_vs_dst_set(dest, NULL, NULL, 0);
+                               spin_unlock_bh(&dest->dst_lock);
+                               ip_vs_dest_dst_free(dest_dst);
+                               goto err_unreach;
                        }
-                       __ip_vs_dst_set(dest, rtos, dst_clone(&rt->dst), 0);
-                       IP_VS_DBG(10, "new dst %pI4, src %pI4, refcnt=%d, "
-                                 "rtos=%X\n",
-                                 &dest->addr.ip, &dest->dst_saddr.ip,
-                                 atomic_read(&rt->dst.__refcnt), rtos);
+                       __ip_vs_dst_set(dest, dest_dst, &rt->dst, 0);
+                       spin_unlock_bh(&dest->dst_lock);
+                       IP_VS_DBG(10, "new dst %pI4, src %pI4, refcnt=%d\n",
+                                 &dest->addr.ip, &dest_dst->dst_saddr.ip,
+                                 atomic_read(&rt->dst.__refcnt));
                }
                daddr = dest->addr.ip;
                if (ret_saddr)
-                       *ret_saddr = dest->dst_saddr.ip;
-               spin_unlock(&dest->dst_lock);
+                       *ret_saddr = dest_dst->dst_saddr.ip;
        } else {
                __be32 saddr = htonl(INADDR_ANY);
 
+               noref = 0;
+
                /* For such unconfigured boxes avoid many route lookups
                 * for performance reasons because we do not remember saddr
                 */
                rt_mode &= ~IP_VS_RT_MODE_CONNECT;
-               rt = do_output_route4(net, daddr, rtos, rt_mode, &saddr);
+               rt = do_output_route4(net, daddr, rt_mode, &saddr);
                if (!rt)
-                       return NULL;
+                       goto err_unreach;
                if (ret_saddr)
                        *ret_saddr = saddr;
        }
 
-       local = rt->rt_flags & RTCF_LOCAL;
+       local = (rt->rt_flags & RTCF_LOCAL) ? 1 : 0;
        if (!((local ? IP_VS_RT_MODE_LOCAL : IP_VS_RT_MODE_NON_LOCAL) &
              rt_mode)) {
                IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI4\n",
                             (rt->rt_flags & RTCF_LOCAL) ?
                             "local":"non-local", &daddr);
-               ip_rt_put(rt);
-               return NULL;
-       }
-       if (local && !(rt_mode & IP_VS_RT_MODE_RDR) &&
-           !((ort = skb_rtable(skb)) && ort->rt_flags & RTCF_LOCAL)) {
-               IP_VS_DBG_RL("Redirect from non-local address %pI4 to local "
-                            "requires NAT method, dest: %pI4\n",
-                            &ip_hdr(skb)->daddr, &daddr);
-               ip_rt_put(rt);
-               return NULL;
+               goto err_put;
        }
-       if (unlikely(!local && ipv4_is_loopback(ip_hdr(skb)->saddr))) {
-               IP_VS_DBG_RL("Stopping traffic from loopback address %pI4 "
-                            "to non-local address, dest: %pI4\n",
-                            &ip_hdr(skb)->saddr, &daddr);
-               ip_rt_put(rt);
-               return NULL;
+       iph = ip_hdr(skb);
+       if (likely(!local)) {
+               if (unlikely(ipv4_is_loopback(iph->saddr))) {
+                       IP_VS_DBG_RL("Stopping traffic from loopback address "
+                                    "%pI4 to non-local address, dest: %pI4\n",
+                                    &iph->saddr, &daddr);
+                       goto err_put;
+               }
+       } else {
+               ort = skb_rtable(skb);
+               if (!(rt_mode & IP_VS_RT_MODE_RDR) &&
+                   !(ort->rt_flags & RTCF_LOCAL)) {
+                       IP_VS_DBG_RL("Redirect from non-local address %pI4 to "
+                                    "local requires NAT method, dest: %pI4\n",
+                                    &iph->daddr, &daddr);
+                       goto err_put;
+               }
+               /* skb to local stack, preserve old route */
+               if (!noref)
+                       ip_rt_put(rt);
+               return local;
        }
 
-       return rt;
-}
-
-/* Reroute packet to local IPv4 stack after DNAT */
-static int
-__ip_vs_reroute_locally(struct sk_buff *skb)
-{
-       struct rtable *rt = skb_rtable(skb);
-       struct net_device *dev = rt->dst.dev;
-       struct net *net = dev_net(dev);
-       struct iphdr *iph = ip_hdr(skb);
-
-       if (rt_is_input_route(rt)) {
-               unsigned long orefdst = skb->_skb_refdst;
-
-               if (ip_route_input(skb, iph->daddr, iph->saddr,
-                                  iph->tos, skb->dev))
-                       return 0;
-               refdst_drop(orefdst);
+       if (likely(!(rt_mode & IP_VS_RT_MODE_TUNNEL))) {
+               mtu = dst_mtu(&rt->dst);
+               df = iph->frag_off & htons(IP_DF);
        } else {
-               struct flowi4 fl4 = {
-                       .daddr = iph->daddr,
-                       .saddr = iph->saddr,
-                       .flowi4_tos = RT_TOS(iph->tos),
-                       .flowi4_mark = skb->mark,
-               };
-
-               rt = ip_route_output_key(net, &fl4);
-               if (IS_ERR(rt))
-                       return 0;
-               if (!(rt->rt_flags & RTCF_LOCAL)) {
-                       ip_rt_put(rt);
-                       return 0;
+               struct sock *sk = skb->sk;
+
+               mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr);
+               if (mtu < 68) {
+                       IP_VS_DBG_RL("%s(): mtu less than 68\n", __func__);
+                       goto err_put;
                }
-               /* Drop old route. */
-               skb_dst_drop(skb);
-               skb_dst_set(skb, &rt->dst);
+               ort = skb_rtable(skb);
+               if (!skb->dev && sk && sk->sk_state != TCP_TIME_WAIT)
+                       ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu);
+               /* MTU check allowed? */
+               df = sysctl_pmtu_disc(ipvs) ? iph->frag_off & htons(IP_DF) : 0;
        }
-       return 1;
+
+       /* MTU checking */
+       if (unlikely(df && skb->len > mtu && !skb_is_gso(skb))) {
+               icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+               IP_VS_DBG(1, "frag needed for %pI4\n", &iph->saddr);
+               goto err_put;
+       }
+
+       skb_dst_drop(skb);
+       if (noref) {
+               if (!local)
+                       skb_dst_set_noref_force(skb, &rt->dst);
+               else
+                       skb_dst_set(skb, dst_clone(&rt->dst));
+       } else
+               skb_dst_set(skb, &rt->dst);
+
+       return local;
+
+err_put:
+       if (!noref)
+               ip_rt_put(rt);
+       return -1;
+
+err_unreach:
+       dst_link_failure(skb);
+       return -1;
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -294,44 +337,57 @@ out_err:
 /*
  * Get route to destination or remote server
  */
-static struct rt6_info *
+static int
 __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest,
                      struct in6_addr *daddr, struct in6_addr *ret_saddr,
-                     int do_xfrm, int rt_mode)
+                     struct ip_vs_iphdr *ipvsh, int do_xfrm, int rt_mode)
 {
        struct net *net = dev_net(skb_dst(skb)->dev);
+       struct ip_vs_dest_dst *dest_dst;
        struct rt6_info *rt;                    /* Route to the other host */
        struct rt6_info *ort;                   /* Original route */
        struct dst_entry *dst;
-       int local;
+       int mtu;
+       int local, noref = 1;
 
        if (dest) {
-               spin_lock(&dest->dst_lock);
-               rt = (struct rt6_info *)__ip_vs_dst_check(dest, 0);
-               if (!rt) {
+               dest_dst = __ip_vs_dst_check(dest);
+               if (likely(dest_dst))
+                       rt = (struct rt6_info *) dest_dst->dst_cache;
+               else {
                        u32 cookie;
 
+                       dest_dst = ip_vs_dest_dst_alloc();
+                       spin_lock_bh(&dest->dst_lock);
+                       if (!dest_dst) {
+                               __ip_vs_dst_set(dest, NULL, NULL, 0);
+                               spin_unlock_bh(&dest->dst_lock);
+                               goto err_unreach;
+                       }
                        dst = __ip_vs_route_output_v6(net, &dest->addr.in6,
-                                                     &dest->dst_saddr.in6,
+                                                     &dest_dst->dst_saddr.in6,
                                                      do_xfrm);
                        if (!dst) {
-                               spin_unlock(&dest->dst_lock);
-                               return NULL;
+                               __ip_vs_dst_set(dest, NULL, NULL, 0);
+                               spin_unlock_bh(&dest->dst_lock);
+                               ip_vs_dest_dst_free(dest_dst);
+                               goto err_unreach;
                        }
                        rt = (struct rt6_info *) dst;
                        cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
-                       __ip_vs_dst_set(dest, 0, dst_clone(&rt->dst), cookie);
+                       __ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie);
+                       spin_unlock_bh(&dest->dst_lock);
                        IP_VS_DBG(10, "new dst %pI6, src %pI6, refcnt=%d\n",
-                                 &dest->addr.in6, &dest->dst_saddr.in6,
+                                 &dest->addr.in6, &dest_dst->dst_saddr.in6,
                                  atomic_read(&rt->dst.__refcnt));
                }
                if (ret_saddr)
-                       *ret_saddr = dest->dst_saddr.in6;
-               spin_unlock(&dest->dst_lock);
+                       *ret_saddr = dest_dst->dst_saddr.in6;
        } else {
+               noref = 0;
                dst = __ip_vs_route_output_v6(net, daddr, ret_saddr, do_xfrm);
                if (!dst)
-                       return NULL;
+                       goto err_unreach;
                rt = (struct rt6_info *) dst;
        }
 
@@ -340,86 +396,137 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest,
              rt_mode)) {
                IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI6c\n",
                             local ? "local":"non-local", daddr);
-               dst_release(&rt->dst);
-               return NULL;
+               goto err_put;
        }
-       if (local && !(rt_mode & IP_VS_RT_MODE_RDR) &&
-           !((ort = (struct rt6_info *) skb_dst(skb)) &&
-             __ip_vs_is_local_route6(ort))) {
-               IP_VS_DBG_RL("Redirect from non-local address %pI6c to local "
-                            "requires NAT method, dest: %pI6c\n",
-                            &ipv6_hdr(skb)->daddr, daddr);
-               dst_release(&rt->dst);
-               return NULL;
+       if (likely(!local)) {
+               if (unlikely((!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
+                            ipv6_addr_type(&ipv6_hdr(skb)->saddr) &
+                                           IPV6_ADDR_LOOPBACK)) {
+                       IP_VS_DBG_RL("Stopping traffic from loopback address "
+                                    "%pI6c to non-local address, "
+                                    "dest: %pI6c\n",
+                                    &ipv6_hdr(skb)->saddr, daddr);
+                       goto err_put;
+               }
+       } else {
+               ort = (struct rt6_info *) skb_dst(skb);
+               if (!(rt_mode & IP_VS_RT_MODE_RDR) &&
+                   !__ip_vs_is_local_route6(ort)) {
+                       IP_VS_DBG_RL("Redirect from non-local address %pI6c "
+                                    "to local requires NAT method, "
+                                    "dest: %pI6c\n",
+                                    &ipv6_hdr(skb)->daddr, daddr);
+                       goto err_put;
+               }
+               /* skb to local stack, preserve old route */
+               if (!noref)
+                       dst_release(&rt->dst);
+               return local;
        }
-       if (unlikely(!local && (!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
-                    ipv6_addr_type(&ipv6_hdr(skb)->saddr) &
-                                   IPV6_ADDR_LOOPBACK)) {
-               IP_VS_DBG_RL("Stopping traffic from loopback address %pI6c "
-                            "to non-local address, dest: %pI6c\n",
-                            &ipv6_hdr(skb)->saddr, daddr);
-               dst_release(&rt->dst);
-               return NULL;
+
+       /* MTU checking */
+       if (likely(!(rt_mode & IP_VS_RT_MODE_TUNNEL)))
+               mtu = dst_mtu(&rt->dst);
+       else {
+               struct sock *sk = skb->sk;
+
+               mtu = dst_mtu(&rt->dst) - sizeof(struct ipv6hdr);
+               if (mtu < IPV6_MIN_MTU) {
+                       IP_VS_DBG_RL("%s(): mtu less than %d\n", __func__,
+                                    IPV6_MIN_MTU);
+                       goto err_put;
+               }
+               ort = (struct rt6_info *) skb_dst(skb);
+               if (!skb->dev && sk && sk->sk_state != TCP_TIME_WAIT)
+                       ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu);
        }
 
-       return rt;
+       if (unlikely(__mtu_check_toobig_v6(skb, mtu))) {
+               if (!skb->dev)
+                       skb->dev = net->loopback_dev;
+               /* only send ICMP too big on first fragment */
+               if (!ipvsh->fragoffs)
+                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+               IP_VS_DBG(1, "frag needed for %pI6c\n", &ipv6_hdr(skb)->saddr);
+               goto err_put;
+       }
+
+       skb_dst_drop(skb);
+       if (noref) {
+               if (!local)
+                       skb_dst_set_noref_force(skb, &rt->dst);
+               else
+                       skb_dst_set(skb, dst_clone(&rt->dst));
+       } else
+               skb_dst_set(skb, &rt->dst);
+
+       return local;
+
+err_put:
+       if (!noref)
+               dst_release(&rt->dst);
+       return -1;
+
+err_unreach:
+       dst_link_failure(skb);
+       return -1;
 }
 #endif
 
 
-/*
- *     Release dest->dst_cache before a dest is removed
- */
-void
-ip_vs_dst_reset(struct ip_vs_dest *dest)
+/* return NF_ACCEPT to allow forwarding or other NF_xxx on error */
+static inline int ip_vs_tunnel_xmit_prepare(struct sk_buff *skb,
+                                           struct ip_vs_conn *cp)
 {
-       struct dst_entry *old_dst;
+       int ret = NF_ACCEPT;
+
+       skb->ipvs_property = 1;
+       if (unlikely(cp->flags & IP_VS_CONN_F_NFCT))
+               ret = ip_vs_confirm_conntrack(skb);
+       if (ret == NF_ACCEPT) {
+               nf_reset(skb);
+               skb_forward_csum(skb);
+       }
+       return ret;
+}
+
+/* return NF_STOLEN (sent) or NF_ACCEPT if local=1 (not sent) */
+static inline int ip_vs_nat_send_or_cont(int pf, struct sk_buff *skb,
+                                        struct ip_vs_conn *cp, int local)
+{
+       int ret = NF_STOLEN;
 
-       old_dst = dest->dst_cache;
-       dest->dst_cache = NULL;
-       dst_release(old_dst);
-       dest->dst_saddr.ip = 0;
+       skb->ipvs_property = 1;
+       if (likely(!(cp->flags & IP_VS_CONN_F_NFCT)))
+               ip_vs_notrack(skb);
+       else
+               ip_vs_update_conntrack(skb, cp, 1);
+       if (!local) {
+               skb_forward_csum(skb);
+               NF_HOOK(pf, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev,
+                       dst_output);
+       } else
+               ret = NF_ACCEPT;
+       return ret;
 }
 
-#define IP_VS_XMIT_TUNNEL(skb, cp)                             \
-({                                                             \
-       int __ret = NF_ACCEPT;                                  \
-                                                               \
-       (skb)->ipvs_property = 1;                               \
-       if (unlikely((cp)->flags & IP_VS_CONN_F_NFCT))          \
-               __ret = ip_vs_confirm_conntrack(skb);           \
-       if (__ret == NF_ACCEPT) {                               \
-               nf_reset(skb);                                  \
-               skb_forward_csum(skb);                          \
-       }                                                       \
-       __ret;                                                  \
-})
-
-#define IP_VS_XMIT_NAT(pf, skb, cp, local)             \
-do {                                                   \
-       (skb)->ipvs_property = 1;                       \
-       if (likely(!((cp)->flags & IP_VS_CONN_F_NFCT))) \
-               ip_vs_notrack(skb);                     \
-       else                                            \
-               ip_vs_update_conntrack(skb, cp, 1);     \
-       if (local)                                      \
-               return NF_ACCEPT;                       \
-       skb_forward_csum(skb);                          \
-       NF_HOOK(pf, NF_INET_LOCAL_OUT, (skb), NULL,     \
-               skb_dst(skb)->dev, dst_output);         \
-} while (0)
-
-#define IP_VS_XMIT(pf, skb, cp, local)                 \
-do {                                                   \
-       (skb)->ipvs_property = 1;                       \
-       if (likely(!((cp)->flags & IP_VS_CONN_F_NFCT))) \
-               ip_vs_notrack(skb);                     \
-       if (local)                                      \
-               return NF_ACCEPT;                       \
-       skb_forward_csum(skb);                          \
-       NF_HOOK(pf, NF_INET_LOCAL_OUT, (skb), NULL,     \
-               skb_dst(skb)->dev, dst_output);         \
-} while (0)
+/* return NF_STOLEN (sent) or NF_ACCEPT if local=1 (not sent) */
+static inline int ip_vs_send_or_cont(int pf, struct sk_buff *skb,
+                                    struct ip_vs_conn *cp, int local)
+{
+       int ret = NF_STOLEN;
+
+       skb->ipvs_property = 1;
+       if (likely(!(cp->flags & IP_VS_CONN_F_NFCT)))
+               ip_vs_notrack(skb);
+       if (!local) {
+               skb_forward_csum(skb);
+               NF_HOOK(pf, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev,
+                       dst_output);
+       } else
+               ret = NF_ACCEPT;
+       return ret;
+}
 
 
 /*
@@ -430,7 +537,7 @@ ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
        /* we do not touch skb and do not need pskb ptr */
-       IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 1);
+       return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1);
 }
 
 
@@ -443,52 +550,29 @@ int
 ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                  struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
-       struct rtable *rt;                      /* Route to the other host */
        struct iphdr  *iph = ip_hdr(skb);
-       int    mtu;
 
        EnterFunction(10);
 
-       if (!(rt = __ip_vs_get_out_rt(skb, NULL, iph->daddr, RT_TOS(iph->tos),
-                                     IP_VS_RT_MODE_NON_LOCAL, NULL)))
-               goto tx_error_icmp;
-
-       /* MTU checking */
-       mtu = dst_mtu(&rt->dst);
-       if ((skb->len > mtu) && (iph->frag_off & htons(IP_DF)) &&
-           !skb_is_gso(skb)) {
-               ip_rt_put(rt);
-               icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
-               IP_VS_DBG_RL("%s(): frag needed\n", __func__);
+       rcu_read_lock();
+       if (__ip_vs_get_out_rt(skb, NULL, iph->daddr, IP_VS_RT_MODE_NON_LOCAL,
+                              NULL) < 0)
                goto tx_error;
-       }
 
-       /*
-        * Call ip_send_check because we are not sure it is called
-        * after ip_defrag. Is copy-on-write needed?
-        */
-       if (unlikely((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)) {
-               ip_rt_put(rt);
-               return NF_STOLEN;
-       }
-       ip_send_check(ip_hdr(skb));
-
-       /* drop old route */
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
+       ip_send_check(iph);
 
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 0);
+       ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0);
+       rcu_read_unlock();
 
        LeaveFunction(10);
        return NF_STOLEN;
 
- tx_error_icmp:
-       dst_link_failure(skb);
  tx_error:
        kfree_skb(skb);
+       rcu_read_unlock();
        LeaveFunction(10);
        return NF_STOLEN;
 }
@@ -496,60 +580,27 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
 #ifdef CONFIG_IP_VS_IPV6
 int
 ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
-                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
-       struct rt6_info *rt;                    /* Route to the other host */
-       int    mtu;
-
        EnterFunction(10);
 
-       rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph->daddr.in6, NULL, 0,
-                                  IP_VS_RT_MODE_NON_LOCAL);
-       if (!rt)
-               goto tx_error_icmp;
-
-       /* MTU checking */
-       mtu = dst_mtu(&rt->dst);
-       if (__mtu_check_toobig_v6(skb, mtu)) {
-               if (!skb->dev) {
-                       struct net *net = dev_net(skb_dst(skb)->dev);
-
-                       skb->dev = net->loopback_dev;
-               }
-               /* only send ICMP too big on first fragment */
-               if (!iph->fragoffs)
-                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-               dst_release(&rt->dst);
-               IP_VS_DBG_RL("%s(): frag needed\n", __func__);
+       rcu_read_lock();
+       if (__ip_vs_get_out_rt_v6(skb, NULL, &ipvsh->daddr.in6, NULL,
+                                 ipvsh, 0, IP_VS_RT_MODE_NON_LOCAL) < 0)
                goto tx_error;
-       }
-
-       /*
-        * Call ip_send_check because we are not sure it is called
-        * after ip_defrag. Is copy-on-write needed?
-        */
-       skb = skb_share_check(skb, GFP_ATOMIC);
-       if (unlikely(skb == NULL)) {
-               dst_release(&rt->dst);
-               return NF_STOLEN;
-       }
-
-       /* drop old route */
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
 
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       IP_VS_XMIT(NFPROTO_IPV6, skb, cp, 0);
+       ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0);
+       rcu_read_unlock();
 
        LeaveFunction(10);
        return NF_STOLEN;
 
- tx_error_icmp:
-       dst_link_failure(skb);
  tx_error:
        kfree_skb(skb);
+       rcu_read_unlock();
        LeaveFunction(10);
        return NF_STOLEN;
 }
@@ -564,29 +615,30 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
               struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
        struct rtable *rt;              /* Route to the other host */
-       int mtu;
-       struct iphdr *iph = ip_hdr(skb);
-       int local;
+       int local, rc, was_input;
 
        EnterFunction(10);
 
+       rcu_read_lock();
        /* check if it is a connection of no-client-port */
        if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) {
                __be16 _pt, *p;
-               p = skb_header_pointer(skb, iph->ihl*4, sizeof(_pt), &_pt);
+
+               p = skb_header_pointer(skb, ipvsh->len, sizeof(_pt), &_pt);
                if (p == NULL)
                        goto tx_error;
                ip_vs_conn_fill_cport(cp, *p);
                IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
        }
 
-       if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip,
-                                     RT_TOS(iph->tos),
-                                     IP_VS_RT_MODE_LOCAL |
-                                       IP_VS_RT_MODE_NON_LOCAL |
-                                       IP_VS_RT_MODE_RDR, NULL)))
-               goto tx_error_icmp;
-       local = rt->rt_flags & RTCF_LOCAL;
+       was_input = rt_is_input_route(skb_rtable(skb));
+       local = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip,
+                                  IP_VS_RT_MODE_LOCAL |
+                                  IP_VS_RT_MODE_NON_LOCAL |
+                                  IP_VS_RT_MODE_RDR, NULL);
+       if (local < 0)
+               goto tx_error;
+       rt = skb_rtable(skb);
        /*
         * Avoid duplicate tuple in reply direction for NAT traffic
         * to local address when connection is sync-ed
@@ -600,57 +652,31 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                        IP_VS_DBG_RL_PKT(10, AF_INET, pp, skb, 0,
                                         "ip_vs_nat_xmit(): "
                                         "stopping DNAT to local address");
-                       goto tx_error_put;
+                       goto tx_error;
                }
        }
 #endif
 
        /* From world but DNAT to loopback address? */
-       if (local && ipv4_is_loopback(cp->daddr.ip) &&
-           rt_is_input_route(skb_rtable(skb))) {
+       if (local && ipv4_is_loopback(cp->daddr.ip) && was_input) {
                IP_VS_DBG_RL_PKT(1, AF_INET, pp, skb, 0, "ip_vs_nat_xmit(): "
                                 "stopping DNAT to loopback address");
-               goto tx_error_put;
-       }
-
-       /* MTU checking */
-       mtu = dst_mtu(&rt->dst);
-       if ((skb->len > mtu) && (iph->frag_off & htons(IP_DF)) &&
-           !skb_is_gso(skb)) {
-               icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
-               IP_VS_DBG_RL_PKT(0, AF_INET, pp, skb, 0,
-                                "ip_vs_nat_xmit(): frag needed for");
-               goto tx_error_put;
+               goto tx_error;
        }
 
        /* copy-on-write the packet before mangling it */
        if (!skb_make_writable(skb, sizeof(struct iphdr)))
-               goto tx_error_put;
+               goto tx_error;
 
        if (skb_cow(skb, rt->dst.dev->hard_header_len))
-               goto tx_error_put;
+               goto tx_error;
 
        /* mangle the packet */
        if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, ipvsh))
-               goto tx_error_put;
+               goto tx_error;
        ip_hdr(skb)->daddr = cp->daddr.ip;
        ip_send_check(ip_hdr(skb));
 
-       if (!local) {
-               /* drop old route */
-               skb_dst_drop(skb);
-               skb_dst_set(skb, &rt->dst);
-       } else {
-               ip_rt_put(rt);
-               /*
-                * Some IPv4 replies get local address from routes,
-                * not from iph, so while we DNAT after routing
-                * we need this second input/output route.
-                */
-               if (!__ip_vs_reroute_locally(skb))
-                       goto tx_error;
-       }
-
        IP_VS_DBG_PKT(10, AF_INET, pp, skb, 0, "After DNAT");
 
        /* FIXME: when application helper enlarges the packet and the length
@@ -660,49 +686,48 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       IP_VS_XMIT_NAT(NFPROTO_IPV4, skb, cp, local);
+       rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local);
+       rcu_read_unlock();
 
        LeaveFunction(10);
-       return NF_STOLEN;
+       return rc;
 
-  tx_error_icmp:
-       dst_link_failure(skb);
   tx_error:
        kfree_skb(skb);
+       rcu_read_unlock();
        LeaveFunction(10);
        return NF_STOLEN;
-  tx_error_put:
-       ip_rt_put(rt);
-       goto tx_error;
 }
 
 #ifdef CONFIG_IP_VS_IPV6
 int
 ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
-                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
        struct rt6_info *rt;            /* Route to the other host */
-       int mtu;
-       int local;
+       int local, rc;
 
        EnterFunction(10);
 
+       rcu_read_lock();
        /* check if it is a connection of no-client-port */
-       if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT && !iph->fragoffs)) {
+       if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT && !ipvsh->fragoffs)) {
                __be16 _pt, *p;
-               p = skb_header_pointer(skb, iph->len, sizeof(_pt), &_pt);
+               p = skb_header_pointer(skb, ipvsh->len, sizeof(_pt), &_pt);
                if (p == NULL)
                        goto tx_error;
                ip_vs_conn_fill_cport(cp, *p);
                IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
        }
 
-       if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL,
-                                        0, (IP_VS_RT_MODE_LOCAL |
-                                            IP_VS_RT_MODE_NON_LOCAL |
-                                            IP_VS_RT_MODE_RDR))))
-               goto tx_error_icmp;
-       local = __ip_vs_is_local_route6(rt);
+       local = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL,
+                                     ipvsh, 0,
+                                     IP_VS_RT_MODE_LOCAL |
+                                     IP_VS_RT_MODE_NON_LOCAL |
+                                     IP_VS_RT_MODE_RDR);
+       if (local < 0)
+               goto tx_error;
+       rt = (struct rt6_info *) skb_dst(skb);
        /*
         * Avoid duplicate tuple in reply direction for NAT traffic
         * to local address when connection is sync-ed
@@ -716,7 +741,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                        IP_VS_DBG_RL_PKT(10, AF_INET6, pp, skb, 0,
                                         "ip_vs_nat_xmit_v6(): "
                                         "stopping DNAT to local address");
-                       goto tx_error_put;
+                       goto tx_error;
                }
        }
 #endif
@@ -727,46 +752,21 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                IP_VS_DBG_RL_PKT(1, AF_INET6, pp, skb, 0,
                                 "ip_vs_nat_xmit_v6(): "
                                 "stopping DNAT to loopback address");
-               goto tx_error_put;
-       }
-
-       /* MTU checking */
-       mtu = dst_mtu(&rt->dst);
-       if (__mtu_check_toobig_v6(skb, mtu)) {
-               if (!skb->dev) {
-                       struct net *net = dev_net(skb_dst(skb)->dev);
-
-                       skb->dev = net->loopback_dev;
-               }
-               /* only send ICMP too big on first fragment */
-               if (!iph->fragoffs)
-                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-               IP_VS_DBG_RL_PKT(0, AF_INET6, pp, skb, 0,
-                                "ip_vs_nat_xmit_v6(): frag needed for");
-               goto tx_error_put;
+               goto tx_error;
        }
 
        /* copy-on-write the packet before mangling it */
        if (!skb_make_writable(skb, sizeof(struct ipv6hdr)))
-               goto tx_error_put;
+               goto tx_error;
 
        if (skb_cow(skb, rt->dst.dev->hard_header_len))
-               goto tx_error_put;
+               goto tx_error;
 
        /* mangle the packet */
-       if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, iph))
+       if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, ipvsh))
                goto tx_error;
        ipv6_hdr(skb)->daddr = cp->daddr.in6;
 
-       if (!local || !skb->dev) {
-               /* drop the old route when skb is not shared */
-               skb_dst_drop(skb);
-               skb_dst_set(skb, &rt->dst);
-       } else {
-               /* destined to loopback, do we need to change route? */
-               dst_release(&rt->dst);
-       }
-
        IP_VS_DBG_PKT(10, AF_INET6, pp, skb, 0, "After DNAT");
 
        /* FIXME: when application helper enlarges the packet and the length
@@ -776,20 +776,17 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       IP_VS_XMIT_NAT(NFPROTO_IPV6, skb, cp, local);
+       rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local);
+       rcu_read_unlock();
 
        LeaveFunction(10);
-       return NF_STOLEN;
+       return rc;
 
-tx_error_icmp:
-       dst_link_failure(skb);
 tx_error:
        LeaveFunction(10);
        kfree_skb(skb);
+       rcu_read_unlock();
        return NF_STOLEN;
-tx_error_put:
-       dst_release(&rt->dst);
-       goto tx_error;
 }
 #endif
 
@@ -826,56 +823,40 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        __be16 df;
        struct iphdr  *iph;                     /* Our new IP header */
        unsigned int max_headroom;              /* The extra header space needed */
-       int    mtu;
-       int ret;
+       int ret, local;
 
        EnterFunction(10);
 
-       if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip,
-                                     RT_TOS(tos), IP_VS_RT_MODE_LOCAL |
-                                                  IP_VS_RT_MODE_NON_LOCAL |
-                                                  IP_VS_RT_MODE_CONNECT,
-                                                  &saddr)))
-               goto tx_error_icmp;
-       if (rt->rt_flags & RTCF_LOCAL) {
-               ip_rt_put(rt);
-               IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 1);
+       rcu_read_lock();
+       local = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip,
+                                  IP_VS_RT_MODE_LOCAL |
+                                  IP_VS_RT_MODE_NON_LOCAL |
+                                  IP_VS_RT_MODE_CONNECT |
+                                  IP_VS_RT_MODE_TUNNEL, &saddr);
+       if (local < 0)
+               goto tx_error;
+       if (local) {
+               rcu_read_unlock();
+               return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1);
        }
 
+       rt = skb_rtable(skb);
        tdev = rt->dst.dev;
 
-       mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr);
-       if (mtu < 68) {
-               IP_VS_DBG_RL("%s(): mtu less than 68\n", __func__);
-               goto tx_error_put;
-       }
-       if (rt_is_output_route(skb_rtable(skb)))
-               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
-
        /* Copy DF, reset fragment offset and MF */
        df = sysctl_pmtu_disc(ipvs) ? old_iph->frag_off & htons(IP_DF) : 0;
 
-       if (df && mtu < ntohs(old_iph->tot_len) && !skb_is_gso(skb)) {
-               icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
-               IP_VS_DBG_RL("%s(): frag needed\n", __func__);
-               goto tx_error_put;
-       }
-
        /*
         * Okay, now see if we can stuff it in the buffer as-is.
         */
        max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr);
 
-       if (skb_headroom(skb) < max_headroom
-           || skb_cloned(skb) || skb_shared(skb)) {
+       if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) {
                struct sk_buff *new_skb =
                        skb_realloc_headroom(skb, max_headroom);
-               if (!new_skb) {
-                       ip_rt_put(rt);
-                       kfree_skb(skb);
-                       IP_VS_ERR_RL("%s(): no memory\n", __func__);
-                       return NF_STOLEN;
-               }
+
+               if (!new_skb)
+                       goto tx_error;
                consume_skb(skb);
                skb = new_skb;
                old_iph = ip_hdr(skb);
@@ -890,10 +871,6 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        skb_reset_network_header(skb);
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 
-       /* drop old route */
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
        /*
         *      Push down and install the IPIP header.
         */
@@ -911,25 +888,22 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       ret = IP_VS_XMIT_TUNNEL(skb, cp);
+       ret = ip_vs_tunnel_xmit_prepare(skb, cp);
        if (ret == NF_ACCEPT)
                ip_local_out(skb);
        else if (ret == NF_DROP)
                kfree_skb(skb);
+       rcu_read_unlock();
 
        LeaveFunction(10);
 
        return NF_STOLEN;
 
-  tx_error_icmp:
-       dst_link_failure(skb);
   tx_error:
        kfree_skb(skb);
+       rcu_read_unlock();
        LeaveFunction(10);
        return NF_STOLEN;
-tx_error_put:
-       ip_rt_put(rt);
-       goto tx_error;
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -943,60 +917,37 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        struct ipv6hdr  *old_iph = ipv6_hdr(skb);
        struct ipv6hdr  *iph;           /* Our new IP header */
        unsigned int max_headroom;      /* The extra header space needed */
-       int    mtu;
-       int ret;
+       int ret, local;
 
        EnterFunction(10);
 
-       if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6,
-                                        &saddr, 1, (IP_VS_RT_MODE_LOCAL |
-                                                    IP_VS_RT_MODE_NON_LOCAL))))
-               goto tx_error_icmp;
-       if (__ip_vs_is_local_route6(rt)) {
-               dst_release(&rt->dst);
-               IP_VS_XMIT(NFPROTO_IPV6, skb, cp, 1);
+       rcu_read_lock();
+       local = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6,
+                                     &saddr, ipvsh, 1,
+                                     IP_VS_RT_MODE_LOCAL |
+                                     IP_VS_RT_MODE_NON_LOCAL |
+                                     IP_VS_RT_MODE_TUNNEL);
+       if (local < 0)
+               goto tx_error;
+       if (local) {
+               rcu_read_unlock();
+               return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1);
        }
 
+       rt = (struct rt6_info *) skb_dst(skb);
        tdev = rt->dst.dev;
 
-       mtu = dst_mtu(&rt->dst) - sizeof(struct ipv6hdr);
-       if (mtu < IPV6_MIN_MTU) {
-               IP_VS_DBG_RL("%s(): mtu less than %d\n", __func__,
-                            IPV6_MIN_MTU);
-               goto tx_error_put;
-       }
-       if (skb_dst(skb))
-               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
-
-       /* MTU checking: Notice that 'mtu' have been adjusted before hand */
-       if (__mtu_check_toobig_v6(skb, mtu)) {
-               if (!skb->dev) {
-                       struct net *net = dev_net(skb_dst(skb)->dev);
-
-                       skb->dev = net->loopback_dev;
-               }
-               /* only send ICMP too big on first fragment */
-               if (!ipvsh->fragoffs)
-                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-               IP_VS_DBG_RL("%s(): frag needed\n", __func__);
-               goto tx_error_put;
-       }
-
        /*
         * Okay, now see if we can stuff it in the buffer as-is.
         */
        max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr);
 
-       if (skb_headroom(skb) < max_headroom
-           || skb_cloned(skb) || skb_shared(skb)) {
+       if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) {
                struct sk_buff *new_skb =
                        skb_realloc_headroom(skb, max_headroom);
-               if (!new_skb) {
-                       dst_release(&rt->dst);
-                       kfree_skb(skb);
-                       IP_VS_ERR_RL("%s(): no memory\n", __func__);
-                       return NF_STOLEN;
-               }
+
+               if (!new_skb)
+                       goto tx_error;
                consume_skb(skb);
                skb = new_skb;
                old_iph = ipv6_hdr(skb);
@@ -1008,10 +959,6 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        skb_reset_network_header(skb);
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 
-       /* drop old route */
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
        /*
         *      Push down and install the IPIP header.
         */
@@ -1029,25 +976,22 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       ret = IP_VS_XMIT_TUNNEL(skb, cp);
+       ret = ip_vs_tunnel_xmit_prepare(skb, cp);
        if (ret == NF_ACCEPT)
                ip6_local_out(skb);
        else if (ret == NF_DROP)
                kfree_skb(skb);
+       rcu_read_unlock();
 
        LeaveFunction(10);
 
        return NF_STOLEN;
 
-tx_error_icmp:
-       dst_link_failure(skb);
 tx_error:
        kfree_skb(skb);
+       rcu_read_unlock();
        LeaveFunction(10);
        return NF_STOLEN;
-tx_error_put:
-       dst_release(&rt->dst);
-       goto tx_error;
 }
 #endif
 
@@ -1060,59 +1004,36 @@ int
 ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
              struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
-       struct rtable *rt;                      /* Route to the other host */
-       struct iphdr  *iph = ip_hdr(skb);
-       int    mtu;
+       int local;
 
        EnterFunction(10);
 
-       if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip,
-                                     RT_TOS(iph->tos),
-                                     IP_VS_RT_MODE_LOCAL |
-                                     IP_VS_RT_MODE_NON_LOCAL |
-                                     IP_VS_RT_MODE_KNOWN_NH, NULL)))
-               goto tx_error_icmp;
-       if (rt->rt_flags & RTCF_LOCAL) {
-               ip_rt_put(rt);
-               IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 1);
-       }
-
-       /* MTU checking */
-       mtu = dst_mtu(&rt->dst);
-       if ((iph->frag_off & htons(IP_DF)) && skb->len > mtu &&
-           !skb_is_gso(skb)) {
-               icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
-               ip_rt_put(rt);
-               IP_VS_DBG_RL("%s(): frag needed\n", __func__);
+       rcu_read_lock();
+       local = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip,
+                                  IP_VS_RT_MODE_LOCAL |
+                                  IP_VS_RT_MODE_NON_LOCAL |
+                                  IP_VS_RT_MODE_KNOWN_NH, NULL);
+       if (local < 0)
                goto tx_error;
+       if (local) {
+               rcu_read_unlock();
+               return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1);
        }
 
-       /*
-        * Call ip_send_check because we are not sure it is called
-        * after ip_defrag. Is copy-on-write needed?
-        */
-       if (unlikely((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)) {
-               ip_rt_put(rt);
-               return NF_STOLEN;
-       }
        ip_send_check(ip_hdr(skb));
 
-       /* drop old route */
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 0);
+       ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0);
+       rcu_read_unlock();
 
        LeaveFunction(10);
        return NF_STOLEN;
 
-  tx_error_icmp:
-       dst_link_failure(skb);
   tx_error:
        kfree_skb(skb);
+       rcu_read_unlock();
        LeaveFunction(10);
        return NF_STOLEN;
 }
@@ -1120,64 +1041,36 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
 #ifdef CONFIG_IP_VS_IPV6
 int
 ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
-                struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
+                struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
-       struct rt6_info *rt;                    /* Route to the other host */
-       int    mtu;
+       int local;
 
        EnterFunction(10);
 
-       if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL,
-                                        0, (IP_VS_RT_MODE_LOCAL |
-                                            IP_VS_RT_MODE_NON_LOCAL))))
-               goto tx_error_icmp;
-       if (__ip_vs_is_local_route6(rt)) {
-               dst_release(&rt->dst);
-               IP_VS_XMIT(NFPROTO_IPV6, skb, cp, 1);
-       }
-
-       /* MTU checking */
-       mtu = dst_mtu(&rt->dst);
-       if (__mtu_check_toobig_v6(skb, mtu)) {
-               if (!skb->dev) {
-                       struct net *net = dev_net(skb_dst(skb)->dev);
-
-                       skb->dev = net->loopback_dev;
-               }
-               /* only send ICMP too big on first fragment */
-               if (!iph->fragoffs)
-                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-               dst_release(&rt->dst);
-               IP_VS_DBG_RL("%s(): frag needed\n", __func__);
+       rcu_read_lock();
+       local = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL,
+                                     ipvsh, 0,
+                                     IP_VS_RT_MODE_LOCAL |
+                                     IP_VS_RT_MODE_NON_LOCAL);
+       if (local < 0)
                goto tx_error;
+       if (local) {
+               rcu_read_unlock();
+               return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1);
        }
 
-       /*
-        * Call ip_send_check because we are not sure it is called
-        * after ip_defrag. Is copy-on-write needed?
-        */
-       skb = skb_share_check(skb, GFP_ATOMIC);
-       if (unlikely(skb == NULL)) {
-               dst_release(&rt->dst);
-               return NF_STOLEN;
-       }
-
-       /* drop old route */
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       IP_VS_XMIT(NFPROTO_IPV6, skb, cp, 0);
+       ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0);
+       rcu_read_unlock();
 
        LeaveFunction(10);
        return NF_STOLEN;
 
-tx_error_icmp:
-       dst_link_failure(skb);
 tx_error:
        kfree_skb(skb);
+       rcu_read_unlock();
        LeaveFunction(10);
        return NF_STOLEN;
 }
@@ -1194,10 +1087,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                struct ip_vs_iphdr *iph)
 {
        struct rtable   *rt;    /* Route to the other host */
-       int mtu;
        int rc;
        int local;
-       int rt_mode;
+       int rt_mode, was_input;
 
        EnterFunction(10);
 
@@ -1217,16 +1109,17 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        /*
         * mangle and send the packet here (only for VS/NAT)
         */
+       was_input = rt_is_input_route(skb_rtable(skb));
 
        /* LOCALNODE from FORWARD hook is not supported */
        rt_mode = (hooknum != NF_INET_FORWARD) ?
                  IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
                  IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
-       if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip,
-                                     RT_TOS(ip_hdr(skb)->tos),
-                                     rt_mode, NULL)))
-               goto tx_error_icmp;
-       local = rt->rt_flags & RTCF_LOCAL;
+       rcu_read_lock();
+       local = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip, rt_mode, NULL);
+       if (local < 0)
+               goto tx_error;
+       rt = skb_rtable(skb);
 
        /*
         * Avoid duplicate tuple in reply direction for NAT traffic
@@ -1241,82 +1134,51 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                        IP_VS_DBG(10, "%s(): "
                                  "stopping DNAT to local address %pI4\n",
                                  __func__, &cp->daddr.ip);
-                       goto tx_error_put;
+                       goto tx_error;
                }
        }
 #endif
 
        /* From world but DNAT to loopback address? */
-       if (local && ipv4_is_loopback(cp->daddr.ip) &&
-           rt_is_input_route(skb_rtable(skb))) {
+       if (local && ipv4_is_loopback(cp->daddr.ip) && was_input) {
                IP_VS_DBG(1, "%s(): "
                          "stopping DNAT to loopback %pI4\n",
                          __func__, &cp->daddr.ip);
-               goto tx_error_put;
-       }
-
-       /* MTU checking */
-       mtu = dst_mtu(&rt->dst);
-       if ((skb->len > mtu) && (ip_hdr(skb)->frag_off & htons(IP_DF)) &&
-           !skb_is_gso(skb)) {
-               icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
-               IP_VS_DBG_RL("%s(): frag needed\n", __func__);
-               goto tx_error_put;
+               goto tx_error;
        }
 
        /* copy-on-write the packet before mangling it */
        if (!skb_make_writable(skb, offset))
-               goto tx_error_put;
+               goto tx_error;
 
        if (skb_cow(skb, rt->dst.dev->hard_header_len))
-               goto tx_error_put;
+               goto tx_error;
 
        ip_vs_nat_icmp(skb, pp, cp, 0);
 
-       if (!local) {
-               /* drop the old route when skb is not shared */
-               skb_dst_drop(skb);
-               skb_dst_set(skb, &rt->dst);
-       } else {
-               ip_rt_put(rt);
-               /*
-                * Some IPv4 replies get local address from routes,
-                * not from iph, so while we DNAT after routing
-                * we need this second input/output route.
-                */
-               if (!__ip_vs_reroute_locally(skb))
-                       goto tx_error;
-       }
-
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       IP_VS_XMIT_NAT(NFPROTO_IPV4, skb, cp, local);
-
-       rc = NF_STOLEN;
+       rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local);
+       rcu_read_unlock();
        goto out;
 
-  tx_error_icmp:
-       dst_link_failure(skb);
   tx_error:
-       dev_kfree_skb(skb);
+       kfree_skb(skb);
+       rcu_read_unlock();
        rc = NF_STOLEN;
   out:
        LeaveFunction(10);
        return rc;
-  tx_error_put:
-       ip_rt_put(rt);
-       goto tx_error;
 }
 
 #ifdef CONFIG_IP_VS_IPV6
 int
 ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
-               struct ip_vs_iphdr *iph)
+               struct ip_vs_iphdr *ipvsh)
 {
        struct rt6_info *rt;    /* Route to the other host */
-       int mtu;
        int rc;
        int local;
        int rt_mode;
@@ -1328,7 +1190,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
           translate address/port back */
        if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
                if (cp->packet_xmit)
-                       rc = cp->packet_xmit(skb, cp, pp, iph);
+                       rc = cp->packet_xmit(skb, cp, pp, ipvsh);
                else
                        rc = NF_ACCEPT;
                /* do not touch skb anymore */
@@ -1344,11 +1206,12 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        rt_mode = (hooknum != NF_INET_FORWARD) ?
                  IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
                  IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
-       if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL,
-                                        0, rt_mode)))
-               goto tx_error_icmp;
-
-       local = __ip_vs_is_local_route6(rt);
+       rcu_read_lock();
+       local = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL,
+                                     ipvsh, 0, rt_mode);
+       if (local < 0)
+               goto tx_error;
+       rt = (struct rt6_info *) skb_dst(skb);
        /*
         * Avoid duplicate tuple in reply direction for NAT traffic
         * to local address when connection is sync-ed
@@ -1362,7 +1225,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                        IP_VS_DBG(10, "%s(): "
                                  "stopping DNAT to local address %pI6\n",
                                  __func__, &cp->daddr.in6);
-                       goto tx_error_put;
+                       goto tx_error;
                }
        }
 #endif
@@ -1373,60 +1236,31 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                IP_VS_DBG(1, "%s(): "
                          "stopping DNAT to loopback %pI6\n",
                          __func__, &cp->daddr.in6);
-               goto tx_error_put;
-       }
-
-       /* MTU checking */
-       mtu = dst_mtu(&rt->dst);
-       if (__mtu_check_toobig_v6(skb, mtu)) {
-               if (!skb->dev) {
-                       struct net *net = dev_net(skb_dst(skb)->dev);
-
-                       skb->dev = net->loopback_dev;
-               }
-               /* only send ICMP too big on first fragment */
-               if (!iph->fragoffs)
-                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-               IP_VS_DBG_RL("%s(): frag needed\n", __func__);
-               goto tx_error_put;
+               goto tx_error;
        }
 
        /* copy-on-write the packet before mangling it */
        if (!skb_make_writable(skb, offset))
-               goto tx_error_put;
+               goto tx_error;
 
        if (skb_cow(skb, rt->dst.dev->hard_header_len))
-               goto tx_error_put;
+               goto tx_error;
 
        ip_vs_nat_icmp_v6(skb, pp, cp, 0);
 
-       if (!local || !skb->dev) {
-               /* drop the old route when skb is not shared */
-               skb_dst_drop(skb);
-               skb_dst_set(skb, &rt->dst);
-       } else {
-               /* destined to loopback, do we need to change route? */
-               dst_release(&rt->dst);
-       }
-
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;
 
-       IP_VS_XMIT_NAT(NFPROTO_IPV6, skb, cp, local);
-
-       rc = NF_STOLEN;
+       rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local);
+       rcu_read_unlock();
        goto out;
 
-tx_error_icmp:
-       dst_link_failure(skb);
 tx_error:
-       dev_kfree_skb(skb);
+       kfree_skb(skb);
+       rcu_read_unlock();
        rc = NF_STOLEN;
 out:
        LeaveFunction(10);
        return rc;
-tx_error_put:
-       dst_release(&rt->dst);
-       goto tx_error;
 }
 #endif
index dbdaa11492609373522ab52d192b65451258ea73..b8b95f4027caf8b177d4e5550047fc11e05d8f69 100644 (file)
@@ -2,6 +2,7 @@
  *
  * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
  * based on HW's ip_conntrack_irc.c as well as other modules
+ * (C) 2006 Patrick McHardy <kaber@trash.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index c8e001a9c45b15a8fe4038845f2111c6470c67b7..ebb81d64436c947577f1021df981b390787ff117 100644 (file)
@@ -5,6 +5,7 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ * (C) 2005-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -48,6 +49,7 @@
 #include <net/netfilter/nf_conntrack_labels.h>
 #include <net/netfilter/nf_nat.h>
 #include <net/netfilter/nf_nat_core.h>
+#include <net/netfilter/nf_nat_helper.h>
 
 #define NF_CONNTRACK_VERSION   "0.5.0"
 
@@ -1259,7 +1261,7 @@ void nf_ct_iterate_cleanup(struct net *net,
 EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup);
 
 struct __nf_ct_flush_report {
-       u32 pid;
+       u32 portid;
        int report;
 };
 
@@ -1274,7 +1276,7 @@ static int kill_report(struct nf_conn *i, void *data)
 
        /* If we fail to deliver the event, death_by_timeout() will retry */
        if (nf_conntrack_event_report(IPCT_DESTROY, i,
-                                     fr->pid, fr->report) < 0)
+                                     fr->portid, fr->report) < 0)
                return 1;
 
        /* Avoid the delivery of the destroy event in death_by_timeout(). */
@@ -1297,10 +1299,10 @@ void nf_ct_free_hashtable(void *hash, unsigned int size)
 }
 EXPORT_SYMBOL_GPL(nf_ct_free_hashtable);
 
-void nf_conntrack_flush_report(struct net *net, u32 pid, int report)
+void nf_conntrack_flush_report(struct net *net, u32 portid, int report)
 {
        struct __nf_ct_flush_report fr = {
-               .pid    = pid,
+               .portid = portid,
                .report = report,
        };
        nf_ct_iterate_cleanup(net, kill_report, &fr);
@@ -1364,30 +1366,48 @@ void nf_conntrack_cleanup_end(void)
  */
 void nf_conntrack_cleanup_net(struct net *net)
 {
+       LIST_HEAD(single);
+
+       list_add(&net->exit_list, &single);
+       nf_conntrack_cleanup_net_list(&single);
+}
+
+void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list)
+{
+       int busy;
+       struct net *net;
+
        /*
         * This makes sure all current packets have passed through
         *  netfilter framework.  Roll on, two-stage module
         *  delete...
         */
        synchronize_net();
- i_see_dead_people:
-       nf_ct_iterate_cleanup(net, kill_all, NULL);
-       nf_ct_release_dying_list(net);
-       if (atomic_read(&net->ct.count) != 0) {
+i_see_dead_people:
+       busy = 0;
+       list_for_each_entry(net, net_exit_list, exit_list) {
+               nf_ct_iterate_cleanup(net, kill_all, NULL);
+               nf_ct_release_dying_list(net);
+               if (atomic_read(&net->ct.count) != 0)
+                       busy = 1;
+       }
+       if (busy) {
                schedule();
                goto i_see_dead_people;
        }
 
-       nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
-       nf_conntrack_proto_pernet_fini(net);
-       nf_conntrack_helper_pernet_fini(net);
-       nf_conntrack_ecache_pernet_fini(net);
-       nf_conntrack_tstamp_pernet_fini(net);
-       nf_conntrack_acct_pernet_fini(net);
-       nf_conntrack_expect_pernet_fini(net);
-       kmem_cache_destroy(net->ct.nf_conntrack_cachep);
-       kfree(net->ct.slabname);
-       free_percpu(net->ct.stat);
+       list_for_each_entry(net, net_exit_list, exit_list) {
+               nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
+               nf_conntrack_proto_pernet_fini(net);
+               nf_conntrack_helper_pernet_fini(net);
+               nf_conntrack_ecache_pernet_fini(net);
+               nf_conntrack_tstamp_pernet_fini(net);
+               nf_conntrack_acct_pernet_fini(net);
+               nf_conntrack_expect_pernet_fini(net);
+               kmem_cache_destroy(net->ct.nf_conntrack_cachep);
+               kfree(net->ct.slabname);
+               free_percpu(net->ct.stat);
+       }
 }
 
 void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
index b5d2eb8bf0d58f98f2d9ea3a4e467e8b2861a7cf..1df176146567aba5fbf922794022e04c0f93a83f 100644 (file)
@@ -1,8 +1,10 @@
 /* Event cache for netfilter. */
 
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
- * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+/*
+ * (C) 2005 Harald Welte <laforge@gnumonks.org>
+ * (C) 2005 Patrick McHardy <kaber@trash.net>
+ * (C) 2005-2006 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2005 USAGI/WIDE Project <http://www.linux-ipv6.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
index 8c10e3db3d9bfab6de1adec992dd90483122c3d6..c63b618cd619eb01d0fe99413162a84edd09ac3c 100644 (file)
@@ -3,6 +3,7 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ * (c) 2005-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -40,7 +41,7 @@ static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
 
 /* nf_conntrack_expect helper functions */
 void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
-                               u32 pid, int report)
+                               u32 portid, int report)
 {
        struct nf_conn_help *master_help = nfct_help(exp->master);
        struct net *net = nf_ct_exp_net(exp);
@@ -54,7 +55,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
        hlist_del(&exp->lnode);
        master_help->expecting[exp->class]--;
 
-       nf_ct_expect_event_report(IPEXP_DESTROY, exp, pid, report);
+       nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report);
        nf_ct_expect_put(exp);
 
        NF_CT_STAT_INC(net, expect_delete);
@@ -412,7 +413,7 @@ out:
 }
 
 int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, 
-                               u32 pid, int report)
+                               u32 portid, int report)
 {
        int ret;
 
@@ -425,7 +426,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
        if (ret < 0)
                goto out;
        spin_unlock_bh(&nf_conntrack_lock);
-       nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
+       nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
        return ret;
 out:
        spin_unlock_bh(&nf_conntrack_lock);
index 62fb8faedb8014a8efff4fe59d43b5f98cb88e2b..6b217074237b85ba1b4420ba889f6cfc155246eb 100644 (file)
@@ -3,6 +3,7 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
index 7df7b36d2e2430628dde4bc53c5ca1442f45f92b..bdebd03bc8cd448a75ca94db1c88ea461f72093a 100644 (file)
@@ -2,6 +2,7 @@
  * H.323 connection tracking helper
  *
  * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
+ * Copyright (c) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * This source code is licensed under General Public License version 2.
  *
index 94b4b9853f60488a1aa9b12ff9e96cac3c90d4d2..974a2a4adefa739c729692e0df3ad3142f83d0d4 100644 (file)
@@ -3,6 +3,7 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -353,7 +354,7 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
        /* rcu_read_lock()ed by nf_hook_slow */
        helper = rcu_dereference(help->helper);
 
-       nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
+       nf_log_packet(nf_ct_net(ct), nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
                      "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);
 
        va_end(args);
index 70985c5d0ffa390ed1c3290ad50dd25066c07485..0fd2976db7ee9a7bdb5db99c912ce707116a8283 100644 (file)
@@ -1,6 +1,7 @@
 /* IRC extension for IP connection tracking, Version 1.21
  * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
  * based on RR's ip_conntrack_ftp.c
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index 9904b15f600e96f6d0285cff241339a364dc6200..6d0f8a17c5b77d6c02b4df99ceddb02f48954b3e 100644 (file)
@@ -2409,6 +2409,92 @@ out:
        return skb->len;
 }
 
+static int
+ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct nf_conntrack_expect *exp, *last;
+       struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+       struct nf_conn *ct = cb->data;
+       struct nf_conn_help *help = nfct_help(ct);
+       u_int8_t l3proto = nfmsg->nfgen_family;
+
+       if (cb->args[0])
+               return 0;
+
+       rcu_read_lock();
+       last = (struct nf_conntrack_expect *)cb->args[1];
+restart:
+       hlist_for_each_entry(exp, &help->expectations, lnode) {
+               if (l3proto && exp->tuple.src.l3num != l3proto)
+                       continue;
+               if (cb->args[1]) {
+                       if (exp != last)
+                               continue;
+                       cb->args[1] = 0;
+               }
+               if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).portid,
+                                           cb->nlh->nlmsg_seq,
+                                           IPCTNL_MSG_EXP_NEW,
+                                           exp) < 0) {
+                       if (!atomic_inc_not_zero(&exp->use))
+                               continue;
+                       cb->args[1] = (unsigned long)exp;
+                       goto out;
+               }
+       }
+       if (cb->args[1]) {
+               cb->args[1] = 0;
+               goto restart;
+       }
+       cb->args[0] = 1;
+out:
+       rcu_read_unlock();
+       if (last)
+               nf_ct_expect_put(last);
+
+       return skb->len;
+}
+
+static int ctnetlink_dump_exp_ct(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;
+       struct nf_conntrack_tuple_hash *h;
+       struct nf_conn *ct;
+       u16 zone = 0;
+       struct netlink_dump_control c = {
+               .dump = ctnetlink_exp_ct_dump_table,
+               .done = ctnetlink_exp_done,
+       };
+
+       err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER, u3);
+       if (err < 0)
+               return err;
+
+       if (cda[CTA_EXPECT_ZONE]) {
+               err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone);
+               if (err < 0)
+                       return err;
+       }
+
+       h = nf_conntrack_find_get(net, zone, &tuple);
+       if (!h)
+               return -ENOENT;
+
+       ct = nf_ct_tuplehash_to_ctrack(h);
+       c.data = ct;
+
+       err = netlink_dump_start(ctnl, skb, nlh, &c);
+       nf_ct_put(ct);
+
+       return err;
+}
+
 static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
        [CTA_EXPECT_MASTER]     = { .type = NLA_NESTED },
        [CTA_EXPECT_TUPLE]      = { .type = NLA_NESTED },
@@ -2439,11 +2525,15 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
        int err;
 
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
-               struct netlink_dump_control c = {
-                       .dump = ctnetlink_exp_dump_table,
-                       .done = ctnetlink_exp_done,
-               };
-               return netlink_dump_start(ctnl, skb, nlh, &c);
+               if (cda[CTA_EXPECT_MASTER])
+                       return ctnetlink_dump_exp_ct(ctnl, skb, nlh, cda);
+               else {
+                       struct netlink_dump_control c = {
+                               .dump = ctnetlink_exp_dump_table,
+                               .done = ctnetlink_exp_done,
+                       };
+                       return netlink_dump_start(ctnl, skb, nlh, &c);
+               }
        }
 
        err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone);
index e6678d2b624e2c371a7be589117660e68c38adbe..7bd03decd36cef207bc418e80361ed5341b349bc 100644 (file)
@@ -11,6 +11,8 @@
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  *
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
+ *
  * Limitations:
  *      - We blindly assume that control connections are always
  *        established in PNS->PAC direction.  This is a violation
index 58ab4050830cbadc5e1b817ea600f60bacff29d0..0ab9636ac57e03308ee1f316b0f11ca49d4e826d 100644 (file)
@@ -3,6 +3,7 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
index ba65b2041eb4b7f747f7df605a049e7922b32a55..a99b6c3427b0ce3e06458cb1fd37bc3bfac98723 100644 (file)
@@ -456,7 +456,8 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
 
 out_invalid:
        if (LOG_INVALID(net, IPPROTO_DCCP))
-               nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL, msg);
+               nf_log_packet(net, nf_ct_l3num(ct), 0, skb, NULL, NULL,
+                             NULL, msg);
        return false;
 }
 
@@ -542,13 +543,13 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
 
                spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_DCCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_dccp: invalid packet ignored ");
                return NF_ACCEPT;
        case CT_DCCP_INVALID:
                spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_DCCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_dccp: invalid state transition ");
                return -NF_ACCEPT;
        }
@@ -613,7 +614,7 @@ static int dccp_error(struct net *net, struct nf_conn *tmpl,
 
 out_invalid:
        if (LOG_INVALID(net, IPPROTO_DCCP))
-               nf_log_packet(pf, 0, skb, NULL, NULL, NULL, msg);
+               nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, msg);
        return -NF_ACCEPT;
 }
 
index 155ce9f8a0db047d0e100b1d0283b8b36cd6e082..9d9c0dade602542bfe82b81aafef050baf5d4d21 100644 (file)
@@ -21,6 +21,7 @@
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  *
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  */
 
 #include <linux/module.h>
index ec83536def9ab89aca0678ac7b7e02a5509b4f01..1314d33f6bcf4c59e0394569a055392cc9a57335 100644 (file)
@@ -1,6 +1,9 @@
 /*
  * Connection tracking protocol helper module for SCTP.
  *
+ * Copyright (c) 2004 Kiran Kumar Immidi <immidi_kiran@yahoo.com>
+ * Copyright (c) 2004-2012 Patrick McHardy <kaber@trash.net>
+ *
  * SCTP is defined in RFC 2960. References to various sections in this code
  * are to this RFC.
  *
index 83876e9877f1457e675dadb65a3d299e71e84165..4d4d8f1d01fcbfb3e42c9e2cf7432578df4df9da 100644 (file)
@@ -1,5 +1,7 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2002-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -720,7 +722,7 @@ static bool tcp_in_window(const struct nf_conn *ct,
                    tn->tcp_be_liberal)
                        res = true;
                if (!res && LOG_INVALID(net, IPPROTO_TCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                        "nf_ct_tcp: %s ",
                        before(seq, sender->td_maxend + 1) ?
                        after(end, sender->td_end - receiver->td_maxwin - 1) ?
@@ -772,7 +774,7 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl,
        th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
        if (th == NULL) {
                if (LOG_INVALID(net, IPPROTO_TCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                "nf_ct_tcp: short packet ");
                return -NF_ACCEPT;
        }
@@ -780,7 +782,7 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl,
        /* Not whole TCP header or malformed packet */
        if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {
                if (LOG_INVALID(net, IPPROTO_TCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                "nf_ct_tcp: truncated/malformed packet ");
                return -NF_ACCEPT;
        }
@@ -793,7 +795,7 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl,
        if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
            nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) {
                if (LOG_INVALID(net, IPPROTO_TCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                  "nf_ct_tcp: bad TCP checksum ");
                return -NF_ACCEPT;
        }
@@ -802,7 +804,7 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl,
        tcpflags = (tcp_flag_byte(th) & ~(TCPHDR_ECE|TCPHDR_CWR|TCPHDR_PSH));
        if (!tcp_valid_flags[tcpflags]) {
                if (LOG_INVALID(net, IPPROTO_TCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                  "nf_ct_tcp: invalid TCP flag combination ");
                return -NF_ACCEPT;
        }
@@ -949,7 +951,7 @@ static int tcp_packet(struct nf_conn *ct,
                }
                spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_TCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                  "nf_ct_tcp: invalid packet ignored in "
                                  "state %s ", tcp_conntrack_names[old_state]);
                return NF_ACCEPT;
@@ -959,7 +961,7 @@ static int tcp_packet(struct nf_conn *ct,
                         dir, get_conntrack_index(th), old_state);
                spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_TCP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                  "nf_ct_tcp: invalid state ");
                return -NF_ACCEPT;
        case TCP_CONNTRACK_CLOSE:
@@ -969,8 +971,8 @@ static int tcp_packet(struct nf_conn *ct,
                        /* Invalid RST  */
                        spin_unlock_bh(&ct->lock);
                        if (LOG_INVALID(net, IPPROTO_TCP))
-                               nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
-                                         "nf_ct_tcp: invalid RST ");
+                               nf_log_packet(net, pf, 0, skb, NULL, NULL,
+                                             NULL, "nf_ct_tcp: invalid RST ");
                        return -NF_ACCEPT;
                }
                if (index == TCP_RST_SET
index 59623cc56e8df57ddf791d382b1cd24c1cffc142..9d7721cbce4bffbf56058a71b8eec807cb8ff289 100644 (file)
@@ -1,5 +1,6 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -119,7 +120,7 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
        hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
        if (hdr == NULL) {
                if (LOG_INVALID(net, IPPROTO_UDP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_udp: short packet ");
                return -NF_ACCEPT;
        }
@@ -127,7 +128,7 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
        /* Truncated/malformed packets */
        if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) {
                if (LOG_INVALID(net, IPPROTO_UDP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                "nf_ct_udp: truncated/malformed packet ");
                return -NF_ACCEPT;
        }
@@ -143,7 +144,7 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
        if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
            nf_checksum(skb, hooknum, dataoff, IPPROTO_UDP, pf)) {
                if (LOG_INVALID(net, IPPROTO_UDP))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                "nf_ct_udp: bad UDP checksum ");
                return -NF_ACCEPT;
        }
index ca969f6273f77a58851858357c25285b9f22e10e..2750e6c69f825e01c848e26873785e2c81099370 100644 (file)
@@ -131,7 +131,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
        hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
        if (hdr == NULL) {
                if (LOG_INVALID(net, IPPROTO_UDPLITE))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_udplite: short packet ");
                return -NF_ACCEPT;
        }
@@ -141,7 +141,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
                cscov = udplen;
        else if (cscov < sizeof(*hdr) || cscov > udplen) {
                if (LOG_INVALID(net, IPPROTO_UDPLITE))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                "nf_ct_udplite: invalid checksum coverage ");
                return -NF_ACCEPT;
        }
@@ -149,7 +149,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
        /* UDPLITE mandates checksums */
        if (!hdr->check) {
                if (LOG_INVALID(net, IPPROTO_UDPLITE))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_udplite: checksum missing ");
                return -NF_ACCEPT;
        }
@@ -159,7 +159,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
            nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP,
                                pf)) {
                if (LOG_INVALID(net, IPPROTO_UDPLITE))
-                       nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                       nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_udplite: bad UDPLite checksum ");
                return -NF_ACCEPT;
        }
index fedee394366187cf35bfa242c9cd1584f0eb0108..bd700b4013c1fd86adc3ea5a64f29c652330e31a 100644 (file)
@@ -1,5 +1,6 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2005-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
@@ -545,16 +546,20 @@ out_init:
        return ret;
 }
 
-static void nf_conntrack_pernet_exit(struct net *net)
+static void nf_conntrack_pernet_exit(struct list_head *net_exit_list)
 {
-       nf_conntrack_standalone_fini_sysctl(net);
-       nf_conntrack_standalone_fini_proc(net);
-       nf_conntrack_cleanup_net(net);
+       struct net *net;
+
+       list_for_each_entry(net, net_exit_list, exit_list) {
+               nf_conntrack_standalone_fini_sysctl(net);
+               nf_conntrack_standalone_fini_proc(net);
+       }
+       nf_conntrack_cleanup_net_list(net_exit_list);
 }
 
 static struct pernet_operations nf_conntrack_net_ops = {
-       .init = nf_conntrack_pernet_init,
-       .exit = nf_conntrack_pernet_exit,
+       .init           = nf_conntrack_pernet_init,
+       .exit_batch     = nf_conntrack_pernet_exit,
 };
 
 static int __init nf_conntrack_standalone_init(void)
index e9936c830208a6b6ae842ce094579213d150f76d..e68ab4fbd71fe7e5fd146fae9950983ddcb27e76 100644 (file)
@@ -1,5 +1,5 @@
 /* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
- *
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  * 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.
index 9e312695c81813b0f9e854df721cbf49bc029f79..388656d5a9ec45a6049af9e2915aaa7df1eefbbe 100644 (file)
@@ -16,7 +16,6 @@
 #define NF_LOG_PREFIXLEN               128
 #define NFLOGGER_NAME_LEN              64
 
-static const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO] __read_mostly;
 static struct list_head nf_loggers_l[NFPROTO_NUMPROTO] __read_mostly;
 static DEFINE_MUTEX(nf_log_mutex);
 
@@ -32,13 +31,46 @@ static struct nf_logger *__find_logger(int pf, const char *str_logger)
        return NULL;
 }
 
+void nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger)
+{
+       const struct nf_logger *log;
+
+       if (pf == NFPROTO_UNSPEC)
+               return;
+
+       mutex_lock(&nf_log_mutex);
+       log = rcu_dereference_protected(net->nf.nf_loggers[pf],
+                                       lockdep_is_held(&nf_log_mutex));
+       if (log == NULL)
+               rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
+
+       mutex_unlock(&nf_log_mutex);
+}
+EXPORT_SYMBOL(nf_log_set);
+
+void nf_log_unset(struct net *net, const struct nf_logger *logger)
+{
+       int i;
+       const struct nf_logger *log;
+
+       mutex_lock(&nf_log_mutex);
+       for (i = 0; i < NFPROTO_NUMPROTO; i++) {
+               log = rcu_dereference_protected(net->nf.nf_loggers[i],
+                               lockdep_is_held(&nf_log_mutex));
+               if (log == logger)
+                       RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL);
+       }
+       mutex_unlock(&nf_log_mutex);
+       synchronize_rcu();
+}
+EXPORT_SYMBOL(nf_log_unset);
+
 /* return EEXIST if the same logger is registered, 0 on success. */
 int nf_log_register(u_int8_t pf, struct nf_logger *logger)
 {
-       const struct nf_logger *llog;
        int i;
 
-       if (pf >= ARRAY_SIZE(nf_loggers))
+       if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers))
                return -EINVAL;
 
        for (i = 0; i < ARRAY_SIZE(logger->list); i++)
@@ -52,10 +84,6 @@ int nf_log_register(u_int8_t pf, struct nf_logger *logger)
        } else {
                /* register at end of list to honor first register win */
                list_add_tail(&logger->list[pf], &nf_loggers_l[pf]);
-               llog = rcu_dereference_protected(nf_loggers[pf],
-                                                lockdep_is_held(&nf_log_mutex));
-               if (llog == NULL)
-                       rcu_assign_pointer(nf_loggers[pf], logger);
        }
 
        mutex_unlock(&nf_log_mutex);
@@ -66,49 +94,43 @@ EXPORT_SYMBOL(nf_log_register);
 
 void nf_log_unregister(struct nf_logger *logger)
 {
-       const struct nf_logger *c_logger;
        int i;
 
        mutex_lock(&nf_log_mutex);
-       for (i = 0; i < ARRAY_SIZE(nf_loggers); i++) {
-               c_logger = rcu_dereference_protected(nf_loggers[i],
-                                                    lockdep_is_held(&nf_log_mutex));
-               if (c_logger == logger)
-                       RCU_INIT_POINTER(nf_loggers[i], NULL);
+       for (i = 0; i < NFPROTO_NUMPROTO; i++)
                list_del(&logger->list[i]);
-       }
        mutex_unlock(&nf_log_mutex);
-
-       synchronize_rcu();
 }
 EXPORT_SYMBOL(nf_log_unregister);
 
-int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger)
+int nf_log_bind_pf(struct net *net, u_int8_t pf,
+                  const struct nf_logger *logger)
 {
-       if (pf >= ARRAY_SIZE(nf_loggers))
+       if (pf >= ARRAY_SIZE(net->nf.nf_loggers))
                return -EINVAL;
        mutex_lock(&nf_log_mutex);
        if (__find_logger(pf, logger->name) == NULL) {
                mutex_unlock(&nf_log_mutex);
                return -ENOENT;
        }
-       rcu_assign_pointer(nf_loggers[pf], logger);
+       rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
        mutex_unlock(&nf_log_mutex);
        return 0;
 }
 EXPORT_SYMBOL(nf_log_bind_pf);
 
-void nf_log_unbind_pf(u_int8_t pf)
+void nf_log_unbind_pf(struct net *net, u_int8_t pf)
 {
-       if (pf >= ARRAY_SIZE(nf_loggers))
+       if (pf >= ARRAY_SIZE(net->nf.nf_loggers))
                return;
        mutex_lock(&nf_log_mutex);
-       RCU_INIT_POINTER(nf_loggers[pf], NULL);
+       RCU_INIT_POINTER(net->nf.nf_loggers[pf], NULL);
        mutex_unlock(&nf_log_mutex);
 }
 EXPORT_SYMBOL(nf_log_unbind_pf);
 
-void nf_log_packet(u_int8_t pf,
+void nf_log_packet(struct net *net,
+                  u_int8_t pf,
                   unsigned int hooknum,
                   const struct sk_buff *skb,
                   const struct net_device *in,
@@ -121,7 +143,7 @@ void nf_log_packet(u_int8_t pf,
        const struct nf_logger *logger;
 
        rcu_read_lock();
-       logger = rcu_dereference(nf_loggers[pf]);
+       logger = rcu_dereference(net->nf.nf_loggers[pf]);
        if (logger) {
                va_start(args, fmt);
                vsnprintf(prefix, sizeof(prefix), fmt, args);
@@ -135,9 +157,11 @@ EXPORT_SYMBOL(nf_log_packet);
 #ifdef CONFIG_PROC_FS
 static void *seq_start(struct seq_file *seq, loff_t *pos)
 {
+       struct net *net = seq_file_net(seq);
+
        mutex_lock(&nf_log_mutex);
 
-       if (*pos >= ARRAY_SIZE(nf_loggers))
+       if (*pos >= ARRAY_SIZE(net->nf.nf_loggers))
                return NULL;
 
        return pos;
@@ -145,9 +169,11 @@ static void *seq_start(struct seq_file *seq, loff_t *pos)
 
 static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
+       struct net *net = seq_file_net(s);
+
        (*pos)++;
 
-       if (*pos >= ARRAY_SIZE(nf_loggers))
+       if (*pos >= ARRAY_SIZE(net->nf.nf_loggers))
                return NULL;
 
        return pos;
@@ -164,8 +190,9 @@ static int seq_show(struct seq_file *s, void *v)
        const struct nf_logger *logger;
        struct nf_logger *t;
        int ret;
+       struct net *net = seq_file_net(s);
 
-       logger = rcu_dereference_protected(nf_loggers[*pos],
+       logger = rcu_dereference_protected(net->nf.nf_loggers[*pos],
                                           lockdep_is_held(&nf_log_mutex));
 
        if (!logger)
@@ -199,7 +226,8 @@ static const struct seq_operations nflog_seq_ops = {
 
 static int nflog_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &nflog_seq_ops);
+       return seq_open_net(inode, file, &nflog_seq_ops,
+                           sizeof(struct seq_net_private));
 }
 
 static const struct file_operations nflog_file_ops = {
@@ -207,7 +235,7 @@ static const struct file_operations nflog_file_ops = {
        .open    = nflog_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
-       .release = seq_release,
+       .release = seq_release_net,
 };
 
 
@@ -216,7 +244,6 @@ static const struct file_operations nflog_file_ops = {
 #ifdef CONFIG_SYSCTL
 static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3];
 static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1];
-static struct ctl_table_header *nf_log_dir_header;
 
 static int nf_log_proc_dostring(ctl_table *table, int write,
                         void __user *buffer, size_t *lenp, loff_t *ppos)
@@ -226,6 +253,7 @@ static int nf_log_proc_dostring(ctl_table *table, int write,
        size_t size = *lenp;
        int r = 0;
        int tindex = (unsigned long)table->extra1;
+       struct net *net = current->nsproxy->net_ns;
 
        if (write) {
                if (size > sizeof(buf))
@@ -234,7 +262,7 @@ static int nf_log_proc_dostring(ctl_table *table, int write,
                        return -EFAULT;
 
                if (!strcmp(buf, "NONE")) {
-                       nf_log_unbind_pf(tindex);
+                       nf_log_unbind_pf(net, tindex);
                        return 0;
                }
                mutex_lock(&nf_log_mutex);
@@ -243,11 +271,11 @@ static int nf_log_proc_dostring(ctl_table *table, int write,
                        mutex_unlock(&nf_log_mutex);
                        return -ENOENT;
                }
-               rcu_assign_pointer(nf_loggers[tindex], logger);
+               rcu_assign_pointer(net->nf.nf_loggers[tindex], logger);
                mutex_unlock(&nf_log_mutex);
        } else {
                mutex_lock(&nf_log_mutex);
-               logger = rcu_dereference_protected(nf_loggers[tindex],
+               logger = rcu_dereference_protected(net->nf.nf_loggers[tindex],
                                                   lockdep_is_held(&nf_log_mutex));
                if (!logger)
                        table->data = "NONE";
@@ -260,49 +288,111 @@ static int nf_log_proc_dostring(ctl_table *table, int write,
        return r;
 }
 
-static __init int netfilter_log_sysctl_init(void)
+static int netfilter_log_sysctl_init(struct net *net)
 {
        int i;
-
-       for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
-               snprintf(nf_log_sysctl_fnames[i-NFPROTO_UNSPEC], 3, "%d", i);
-               nf_log_sysctl_table[i].procname =
-                       nf_log_sysctl_fnames[i-NFPROTO_UNSPEC];
-               nf_log_sysctl_table[i].data = NULL;
-               nf_log_sysctl_table[i].maxlen =
-                       NFLOGGER_NAME_LEN * sizeof(char);
-               nf_log_sysctl_table[i].mode = 0644;
-               nf_log_sysctl_table[i].proc_handler = nf_log_proc_dostring;
-               nf_log_sysctl_table[i].extra1 = (void *)(unsigned long) i;
+       struct ctl_table *table;
+
+       table = nf_log_sysctl_table;
+       if (!net_eq(net, &init_net)) {
+               table = kmemdup(nf_log_sysctl_table,
+                                sizeof(nf_log_sysctl_table),
+                                GFP_KERNEL);
+               if (!table)
+                       goto err_alloc;
+       } else {
+               for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
+                       snprintf(nf_log_sysctl_fnames[i],
+                                3, "%d", i);
+                       nf_log_sysctl_table[i].procname =
+                               nf_log_sysctl_fnames[i];
+                       nf_log_sysctl_table[i].data = NULL;
+                       nf_log_sysctl_table[i].maxlen =
+                               NFLOGGER_NAME_LEN * sizeof(char);
+                       nf_log_sysctl_table[i].mode = 0644;
+                       nf_log_sysctl_table[i].proc_handler =
+                               nf_log_proc_dostring;
+                       nf_log_sysctl_table[i].extra1 =
+                               (void *)(unsigned long) i;
+               }
        }
 
-       nf_log_dir_header = register_net_sysctl(&init_net, "net/netfilter/nf_log",
-                                      nf_log_sysctl_table);
-       if (!nf_log_dir_header)
-               return -ENOMEM;
+       net->nf.nf_log_dir_header = register_net_sysctl(net,
+                                               "net/netfilter/nf_log",
+                                               table);
+       if (!net->nf.nf_log_dir_header)
+               goto err_reg;
 
        return 0;
+
+err_reg:
+       if (!net_eq(net, &init_net))
+               kfree(table);
+err_alloc:
+       return -ENOMEM;
+}
+
+static void netfilter_log_sysctl_exit(struct net *net)
+{
+       struct ctl_table *table;
+
+       table = net->nf.nf_log_dir_header->ctl_table_arg;
+       unregister_net_sysctl_table(net->nf.nf_log_dir_header);
+       if (!net_eq(net, &init_net))
+               kfree(table);
 }
 #else
-static __init int netfilter_log_sysctl_init(void)
+static int netfilter_log_sysctl_init(struct net *net)
 {
        return 0;
 }
+
+static void netfilter_log_sysctl_exit(struct net *net)
+{
+}
 #endif /* CONFIG_SYSCTL */
 
-int __init netfilter_log_init(void)
+static int __net_init nf_log_net_init(struct net *net)
 {
-       int i, r;
+       int ret = -ENOMEM;
+
 #ifdef CONFIG_PROC_FS
        if (!proc_create("nf_log", S_IRUGO,
-                        proc_net_netfilter, &nflog_file_ops))
-               return -1;
+                        net->nf.proc_netfilter, &nflog_file_ops))
+               return ret;
 #endif
+       ret = netfilter_log_sysctl_init(net);
+       if (ret < 0)
+               goto out_sysctl;
+
+       return 0;
 
-       /* Errors will trigger panic, unroll on error is unnecessary. */
-       r = netfilter_log_sysctl_init();
-       if (r < 0)
-               return r;
+out_sysctl:
+       /* For init_net: errors will trigger panic, don't unroll on error. */
+       if (!net_eq(net, &init_net))
+               remove_proc_entry("nf_log", net->nf.proc_netfilter);
+
+       return ret;
+}
+
+static void __net_exit nf_log_net_exit(struct net *net)
+{
+       netfilter_log_sysctl_exit(net);
+       remove_proc_entry("nf_log", net->nf.proc_netfilter);
+}
+
+static struct pernet_operations nf_log_net_ops = {
+       .init = nf_log_net_init,
+       .exit = nf_log_net_exit,
+};
+
+int __init netfilter_log_init(void)
+{
+       int i, ret;
+
+       ret = register_pernet_subsys(&nf_log_net_ops);
+       if (ret < 0)
+               return ret;
 
        for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
                INIT_LIST_HEAD(&(nf_loggers_l[i]));
index 3b67c9d112730af30ed541f56b39aa41f1ffa058..eb772380a202f0037745a4357cf2c00658181bcd 100644 (file)
@@ -1,6 +1,7 @@
 /* Amanda extension for TCP NAT alteration.
  * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
  * based on a copy of HW's ip_nat_irc.c as well as other modules
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
index ad24be070e53c0fe8c27e49a6e2cb93a39c32062..2e469ca2ca553acd9076c0ee3b9c35fc3b561262 100644 (file)
@@ -87,9 +87,10 @@ int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family)
        struct flowi fl;
        unsigned int hh_len;
        struct dst_entry *dst;
+       int err;
 
-       if (xfrm_decode_session(skb, &fl, family) < 0)
-               return -1;
+       err = xfrm_decode_session(skb, &fl, family);
+               return err;
 
        dst = skb_dst(skb);
        if (dst->xfrm)
@@ -98,7 +99,7 @@ int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family)
 
        dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0);
        if (IS_ERR(dst))
-               return -1;
+               return PTR_ERR(dst);
 
        skb_dst_drop(skb);
        skb_dst_set(skb, dst);
@@ -107,7 +108,7 @@ int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family)
        hh_len = skb_dst(skb)->dev->hard_header_len;
        if (skb_headroom(skb) < hh_len &&
            pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC))
-               return -1;
+               return -ENOMEM;
        return 0;
 }
 EXPORT_SYMBOL(nf_xfrm_me_harder);
index 23c2b38676a6b6bd8cc4e1cd0fd0baa63ccd94e3..5fea563afe30421ccfb5d1dcd294a055144623e0 100644 (file)
@@ -2,6 +2,7 @@
  *
  * (C) 2000-2002 Harald Welte <laforge@netfilter.org>
  * (C) 2003-2006 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2007-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
index d812c1235b30372b35f7b2866b113a9878b27147..5ccf01e353902be140df21551382948b0cb41f43 100644 (file)
@@ -1,3 +1,8 @@
+/*
+ * Rusty Russell (C)2000 -- This code is GPL.
+ * Patrick McHardy (c) 2006-2012
+ */
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/init.h>
index 0b1b32cda3072d2867edd82c3154538194f606d6..572d87dc116ffa838d2f9f8838129156add7284e 100644 (file)
 #include <linux/skbuff.h>
 #include <asm/uaccess.h>
 #include <net/sock.h>
-#include <net/netlink.h>
 #include <linux/init.h>
 
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <linux/netfilter/nfnetlink.h>
 
 MODULE_LICENSE("GPL");
@@ -113,22 +112,30 @@ int nfnetlink_has_listeners(struct net *net, unsigned int group)
 }
 EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
 
-int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid,
+struct sk_buff *nfnetlink_alloc_skb(struct net *net, unsigned int size,
+                                   u32 dst_portid, gfp_t gfp_mask)
+{
+       return netlink_alloc_skb(net->nfnl, size, dst_portid, gfp_mask);
+}
+EXPORT_SYMBOL_GPL(nfnetlink_alloc_skb);
+
+int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid,
                   unsigned int group, int echo, gfp_t flags)
 {
-       return nlmsg_notify(net->nfnl, skb, pid, group, echo, flags);
+       return nlmsg_notify(net->nfnl, skb, portid, group, echo, flags);
 }
 EXPORT_SYMBOL_GPL(nfnetlink_send);
 
-int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error)
+int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error)
 {
-       return netlink_set_err(net->nfnl, pid, group, error);
+       return netlink_set_err(net->nfnl, portid, group, error);
 }
 EXPORT_SYMBOL_GPL(nfnetlink_set_err);
 
-int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags)
+int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
+                     int flags)
 {
-       return netlink_unicast(net->nfnl, skb, pid, flags);
+       return netlink_unicast(net->nfnl, skb, portid, flags);
 }
 EXPORT_SYMBOL_GPL(nfnetlink_unicast);
 
@@ -144,7 +151,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                return -EPERM;
 
        /* All the messages must at least contain nfgenmsg */
-       if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg)))
+       if (nlmsg_len(nlh) < sizeof(struct nfgenmsg))
                return 0;
 
        type = nlh->nlmsg_type;
@@ -172,7 +179,7 @@ replay:
        }
 
        {
-               int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
+               int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
                u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
                struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
                struct nlattr *attr = (void *)nlh + min_len;
index f248db572972f71c3a40431e31fc1201eee5b78c..faf1e9300d8adc358fa857ff4296892351060193 100644 (file)
@@ -3,6 +3,7 @@
  * nfetlink.
  *
  * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * Based on the old ipv4-only ipt_ULOG.c:
  * (C) 2000-2004 by Harald Welte <laforge@netfilter.org>
@@ -19,7 +20,7 @@
 #include <linux/ipv6.h>
 #include <linux/netdevice.h>
 #include <linux/netfilter.h>
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nfnetlink_log.h>
 #include <linux/spinlock.h>
@@ -32,6 +33,7 @@
 #include <linux/slab.h>
 #include <net/sock.h>
 #include <net/netfilter/nf_log.h>
+#include <net/netns/generic.h>
 #include <net/netfilter/nfnetlink_log.h>
 
 #include <linux/atomic.h>
@@ -56,6 +58,7 @@ struct nfulnl_instance {
        unsigned int qlen;              /* number of nlmsgs in skb */
        struct sk_buff *skb;            /* pre-allocatd skb */
        struct timer_list timer;
+       struct net *net;
        struct user_namespace *peer_user_ns;    /* User namespace of the peer process */
        int peer_portid;                        /* PORTID of the peer process */
 
@@ -71,25 +74,34 @@ struct nfulnl_instance {
        struct rcu_head rcu;
 };
 
-static DEFINE_SPINLOCK(instances_lock);
-static atomic_t global_seq;
-
 #define INSTANCE_BUCKETS       16
-static struct hlist_head instance_table[INSTANCE_BUCKETS];
 static unsigned int hash_init;
 
+static int nfnl_log_net_id __read_mostly;
+
+struct nfnl_log_net {
+       spinlock_t instances_lock;
+       struct hlist_head instance_table[INSTANCE_BUCKETS];
+       atomic_t global_seq;
+};
+
+static struct nfnl_log_net *nfnl_log_pernet(struct net *net)
+{
+       return net_generic(net, nfnl_log_net_id);
+}
+
 static inline u_int8_t instance_hashfn(u_int16_t group_num)
 {
        return ((group_num & 0xff) % INSTANCE_BUCKETS);
 }
 
 static struct nfulnl_instance *
-__instance_lookup(u_int16_t group_num)
+__instance_lookup(struct nfnl_log_net *log, u_int16_t group_num)
 {
        struct hlist_head *head;
        struct nfulnl_instance *inst;
 
-       head = &instance_table[instance_hashfn(group_num)];
+       head = &log->instance_table[instance_hashfn(group_num)];
        hlist_for_each_entry_rcu(inst, head, hlist) {
                if (inst->group_num == group_num)
                        return inst;
@@ -104,12 +116,12 @@ instance_get(struct nfulnl_instance *inst)
 }
 
 static struct nfulnl_instance *
-instance_lookup_get(u_int16_t group_num)
+instance_lookup_get(struct nfnl_log_net *log, u_int16_t group_num)
 {
        struct nfulnl_instance *inst;
 
        rcu_read_lock_bh();
-       inst = __instance_lookup(group_num);
+       inst = __instance_lookup(log, group_num);
        if (inst && !atomic_inc_not_zero(&inst->use))
                inst = NULL;
        rcu_read_unlock_bh();
@@ -119,7 +131,11 @@ instance_lookup_get(u_int16_t group_num)
 
 static void nfulnl_instance_free_rcu(struct rcu_head *head)
 {
-       kfree(container_of(head, struct nfulnl_instance, rcu));
+       struct nfulnl_instance *inst =
+               container_of(head, struct nfulnl_instance, rcu);
+
+       put_net(inst->net);
+       kfree(inst);
        module_put(THIS_MODULE);
 }
 
@@ -133,13 +149,15 @@ instance_put(struct nfulnl_instance *inst)
 static void nfulnl_timer(unsigned long data);
 
 static struct nfulnl_instance *
-instance_create(u_int16_t group_num, int portid, struct user_namespace *user_ns)
+instance_create(struct net *net, u_int16_t group_num,
+               int portid, struct user_namespace *user_ns)
 {
        struct nfulnl_instance *inst;
+       struct nfnl_log_net *log = nfnl_log_pernet(net);
        int err;
 
-       spin_lock_bh(&instances_lock);
-       if (__instance_lookup(group_num)) {
+       spin_lock_bh(&log->instances_lock);
+       if (__instance_lookup(log, group_num)) {
                err = -EEXIST;
                goto out_unlock;
        }
@@ -163,6 +181,7 @@ instance_create(u_int16_t group_num, int portid, struct user_namespace *user_ns)
 
        setup_timer(&inst->timer, nfulnl_timer, (unsigned long)inst);
 
+       inst->net = get_net(net);
        inst->peer_user_ns = user_ns;
        inst->peer_portid = portid;
        inst->group_num = group_num;
@@ -174,14 +193,15 @@ instance_create(u_int16_t group_num, int portid, struct user_namespace *user_ns)
        inst->copy_range        = NFULNL_COPY_RANGE_MAX;
 
        hlist_add_head_rcu(&inst->hlist,
-                      &instance_table[instance_hashfn(group_num)]);
+                      &log->instance_table[instance_hashfn(group_num)]);
+
 
-       spin_unlock_bh(&instances_lock);
+       spin_unlock_bh(&log->instances_lock);
 
        return inst;
 
 out_unlock:
-       spin_unlock_bh(&instances_lock);
+       spin_unlock_bh(&log->instances_lock);
        return ERR_PTR(err);
 }
 
@@ -210,11 +230,12 @@ __instance_destroy(struct nfulnl_instance *inst)
 }
 
 static inline void
-instance_destroy(struct nfulnl_instance *inst)
+instance_destroy(struct nfnl_log_net *log,
+                struct nfulnl_instance *inst)
 {
-       spin_lock_bh(&instances_lock);
+       spin_lock_bh(&log->instances_lock);
        __instance_destroy(inst);
-       spin_unlock_bh(&instances_lock);
+       spin_unlock_bh(&log->instances_lock);
 }
 
 static int
@@ -298,7 +319,7 @@ nfulnl_set_flags(struct nfulnl_instance *inst, u_int16_t flags)
 }
 
 static struct sk_buff *
-nfulnl_alloc_skb(unsigned int inst_size, unsigned int pkt_size)
+nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size)
 {
        struct sk_buff *skb;
        unsigned int n;
@@ -307,13 +328,14 @@ nfulnl_alloc_skb(unsigned int inst_size, unsigned int pkt_size)
         * message.  WARNING: has to be <= 128k due to slab restrictions */
 
        n = max(inst_size, pkt_size);
-       skb = alloc_skb(n, GFP_ATOMIC);
+       skb = nfnetlink_alloc_skb(&init_net, n, peer_portid, GFP_ATOMIC);
        if (!skb) {
                if (n > pkt_size) {
                        /* try to allocate only as much as we need for current
                         * packet */
 
-                       skb = alloc_skb(pkt_size, GFP_ATOMIC);
+                       skb = nfnetlink_alloc_skb(&init_net, pkt_size,
+                                                 peer_portid, GFP_ATOMIC);
                        if (!skb)
                                pr_err("nfnetlink_log: can't even alloc %u bytes\n",
                                       pkt_size);
@@ -336,7 +358,7 @@ __nfulnl_send(struct nfulnl_instance *inst)
                if (!nlh)
                        goto out;
        }
-       status = nfnetlink_unicast(inst->skb, &init_net, inst->peer_portid,
+       status = nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid,
                                   MSG_DONTWAIT);
 
        inst->qlen = 0;
@@ -370,7 +392,8 @@ nfulnl_timer(unsigned long data)
 /* This is an inline function, we don't really care about a long
  * list of arguments */
 static inline int
-__build_packet_message(struct nfulnl_instance *inst,
+__build_packet_message(struct nfnl_log_net *log,
+                       struct nfulnl_instance *inst,
                        const struct sk_buff *skb,
                        unsigned int data_len,
                        u_int8_t pf,
@@ -536,7 +559,7 @@ __build_packet_message(struct nfulnl_instance *inst,
        /* global sequence number */
        if ((inst->flags & NFULNL_CFG_F_SEQ_GLOBAL) &&
            nla_put_be32(inst->skb, NFULA_SEQ_GLOBAL,
-                        htonl(atomic_inc_return(&global_seq))))
+                        htonl(atomic_inc_return(&log->global_seq))))
                goto nla_put_failure;
 
        if (data_len) {
@@ -592,13 +615,15 @@ nfulnl_log_packet(u_int8_t pf,
        const struct nf_loginfo *li;
        unsigned int qthreshold;
        unsigned int plen;
+       struct net *net = dev_net(in ? in : out);
+       struct nfnl_log_net *log = nfnl_log_pernet(net);
 
        if (li_user && li_user->type == NF_LOG_TYPE_ULOG)
                li = li_user;
        else
                li = &default_loginfo;
 
-       inst = instance_lookup_get(li->u.ulog.group);
+       inst = instance_lookup_get(log, li->u.ulog.group);
        if (!inst)
                return;
 
@@ -609,7 +634,7 @@ nfulnl_log_packet(u_int8_t pf,
        /* FIXME: do we want to make the size calculation conditional based on
         * what is actually present?  way more branches and checks, but more
         * memory efficient... */
-       size =    NLMSG_SPACE(sizeof(struct nfgenmsg))
+       size =    nlmsg_total_size(sizeof(struct nfgenmsg))
                + nla_total_size(sizeof(struct nfulnl_msg_packet_hdr))
                + nla_total_size(sizeof(u_int32_t))     /* ifindex */
                + nla_total_size(sizeof(u_int32_t))     /* ifindex */
@@ -673,14 +698,15 @@ nfulnl_log_packet(u_int8_t pf,
        }
 
        if (!inst->skb) {
-               inst->skb = nfulnl_alloc_skb(inst->nlbufsiz, size);
+               inst->skb = nfulnl_alloc_skb(inst->peer_portid, inst->nlbufsiz,
+                                            size);
                if (!inst->skb)
                        goto alloc_failure;
        }
 
        inst->qlen++;
 
-       __build_packet_message(inst, skb, data_len, pf,
+       __build_packet_message(log, inst, skb, data_len, pf,
                                hooknum, in, out, prefix, plen);
 
        if (inst->qlen >= qthreshold)
@@ -709,24 +735,24 @@ nfulnl_rcv_nl_event(struct notifier_block *this,
                   unsigned long event, void *ptr)
 {
        struct netlink_notify *n = ptr;
+       struct nfnl_log_net *log = nfnl_log_pernet(n->net);
 
        if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) {
                int i;
 
                /* destroy all instances for this portid */
-               spin_lock_bh(&instances_lock);
+               spin_lock_bh(&log->instances_lock);
                for  (i = 0; i < INSTANCE_BUCKETS; i++) {
                        struct hlist_node *t2;
                        struct nfulnl_instance *inst;
-                       struct hlist_head *head = &instance_table[i];
+                       struct hlist_head *head = &log->instance_table[i];
 
                        hlist_for_each_entry_safe(inst, t2, head, hlist) {
-                               if ((net_eq(n->net, &init_net)) &&
-                                   (n->portid == inst->peer_portid))
+                               if (n->portid == inst->peer_portid)
                                        __instance_destroy(inst);
                        }
                }
-               spin_unlock_bh(&instances_lock);
+               spin_unlock_bh(&log->instances_lock);
        }
        return NOTIFY_DONE;
 }
@@ -767,6 +793,8 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
        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;
 
        if (nfula[NFULA_CFG_CMD]) {
@@ -776,14 +804,14 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
                /* Commands without queue context */
                switch (cmd->command) {
                case NFULNL_CFG_CMD_PF_BIND:
-                       return nf_log_bind_pf(pf, &nfulnl_logger);
+                       return nf_log_bind_pf(net, pf, &nfulnl_logger);
                case NFULNL_CFG_CMD_PF_UNBIND:
-                       nf_log_unbind_pf(pf);
+                       nf_log_unbind_pf(net, pf);
                        return 0;
                }
        }
 
-       inst = instance_lookup_get(group_num);
+       inst = instance_lookup_get(log, group_num);
        if (inst && inst->peer_portid != NETLINK_CB(skb).portid) {
                ret = -EPERM;
                goto out_put;
@@ -797,9 +825,9 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
                                goto out_put;
                        }
 
-                       inst = instance_create(group_num,
+                       inst = instance_create(net, group_num,
                                               NETLINK_CB(skb).portid,
-                                              sk_user_ns(NETLINK_CB(skb).ssk));
+                                              sk_user_ns(NETLINK_CB(skb).sk));
                        if (IS_ERR(inst)) {
                                ret = PTR_ERR(inst);
                                goto out;
@@ -811,7 +839,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
                                goto out;
                        }
 
-                       instance_destroy(inst);
+                       instance_destroy(log, inst);
                        goto out_put;
                default:
                        ret = -ENOTSUPP;
@@ -894,55 +922,68 @@ static const struct nfnetlink_subsystem nfulnl_subsys = {
 
 #ifdef CONFIG_PROC_FS
 struct iter_state {
+       struct seq_net_private p;
        unsigned int bucket;
 };
 
-static struct hlist_node *get_first(struct iter_state *st)
+static struct hlist_node *get_first(struct net *net, struct iter_state *st)
 {
+       struct nfnl_log_net *log;
        if (!st)
                return NULL;
 
+       log = nfnl_log_pernet(net);
+
        for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) {
-               if (!hlist_empty(&instance_table[st->bucket]))
-                       return rcu_dereference_bh(hlist_first_rcu(&instance_table[st->bucket]));
+               struct hlist_head *head = &log->instance_table[st->bucket];
+
+               if (!hlist_empty(head))
+                       return rcu_dereference_bh(hlist_first_rcu(head));
        }
        return NULL;
 }
 
-static struct hlist_node *get_next(struct iter_state *st, struct hlist_node *h)
+static struct hlist_node *get_next(struct net *net, struct iter_state *st,
+                                  struct hlist_node *h)
 {
        h = rcu_dereference_bh(hlist_next_rcu(h));
        while (!h) {
+               struct nfnl_log_net *log;
+               struct hlist_head *head;
+
                if (++st->bucket >= INSTANCE_BUCKETS)
                        return NULL;
 
-               h = rcu_dereference_bh(hlist_first_rcu(&instance_table[st->bucket]));
+               log = nfnl_log_pernet(net);
+               head = &log->instance_table[st->bucket];
+               h = rcu_dereference_bh(hlist_first_rcu(head));
        }
        return h;
 }
 
-static struct hlist_node *get_idx(struct iter_state *st, loff_t pos)
+static struct hlist_node *get_idx(struct net *net, struct iter_state *st,
+                                 loff_t pos)
 {
        struct hlist_node *head;
-       head = get_first(st);
+       head = get_first(net, st);
 
        if (head)
-               while (pos && (head = get_next(st, head)))
+               while (pos && (head = get_next(net, st, head)))
                        pos--;
        return pos ? NULL : head;
 }
 
-static void *seq_start(struct seq_file *seq, loff_t *pos)
+static void *seq_start(struct seq_file *s, loff_t *pos)
        __acquires(rcu_bh)
 {
        rcu_read_lock_bh();
-       return get_idx(seq->private, *pos);
+       return get_idx(seq_file_net(s), s->private, *pos);
 }
 
 static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
        (*pos)++;
-       return get_next(s->private, v);
+       return get_next(seq_file_net(s), s->private, v);
 }
 
 static void seq_stop(struct seq_file *s, void *v)
@@ -971,8 +1012,8 @@ static const struct seq_operations nful_seq_ops = {
 
 static int nful_open(struct inode *inode, struct file *file)
 {
-       return seq_open_private(file, &nful_seq_ops,
-                       sizeof(struct iter_state));
+       return seq_open_net(inode, file, &nful_seq_ops,
+                           sizeof(struct iter_state));
 }
 
 static const struct file_operations nful_file_ops = {
@@ -980,17 +1021,43 @@ static const struct file_operations nful_file_ops = {
        .open    = nful_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
-       .release = seq_release_private,
+       .release = seq_release_net,
 };
 
 #endif /* PROC_FS */
 
-static int __init nfnetlink_log_init(void)
+static int __net_init nfnl_log_net_init(struct net *net)
 {
-       int i, status = -ENOMEM;
+       unsigned int i;
+       struct nfnl_log_net *log = nfnl_log_pernet(net);
 
        for (i = 0; i < INSTANCE_BUCKETS; i++)
-               INIT_HLIST_HEAD(&instance_table[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))
+               return -ENOMEM;
+#endif
+       return 0;
+}
+
+static void __net_exit nfnl_log_net_exit(struct net *net)
+{
+       remove_proc_entry("nfnetlink_log", net->nf.proc_netfilter);
+}
+
+static struct pernet_operations nfnl_log_net_ops = {
+       .init   = nfnl_log_net_init,
+       .exit   = nfnl_log_net_exit,
+       .id     = &nfnl_log_net_id,
+       .size   = sizeof(struct nfnl_log_net),
+};
+
+static int __init nfnetlink_log_init(void)
+{
+       int status = -ENOMEM;
 
        /* it's not really all that important to have a random value, so
         * we can do this from the init function, even if there hasn't
@@ -1000,29 +1067,25 @@ static int __init nfnetlink_log_init(void)
        netlink_register_notifier(&nfulnl_rtnl_notifier);
        status = nfnetlink_subsys_register(&nfulnl_subsys);
        if (status < 0) {
-               printk(KERN_ERR "log: failed to create netlink socket\n");
+               pr_err("log: failed to create netlink socket\n");
                goto cleanup_netlink_notifier;
        }
 
        status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger);
        if (status < 0) {
-               printk(KERN_ERR "log: failed to register logger\n");
+               pr_err("log: failed to register logger\n");
                goto cleanup_subsys;
        }
 
-#ifdef CONFIG_PROC_FS
-       if (!proc_create("nfnetlink_log", 0440,
-                        proc_net_netfilter, &nful_file_ops)) {
-               status = -ENOMEM;
+       status = register_pernet_subsys(&nfnl_log_net_ops);
+       if (status < 0) {
+               pr_err("log: failed to register pernet ops\n");
                goto cleanup_logger;
        }
-#endif
        return status;
 
-#ifdef CONFIG_PROC_FS
 cleanup_logger:
        nf_log_unregister(&nfulnl_logger);
-#endif
 cleanup_subsys:
        nfnetlink_subsys_unregister(&nfulnl_subsys);
 cleanup_netlink_notifier:
@@ -1032,10 +1095,8 @@ cleanup_netlink_notifier:
 
 static void __exit nfnetlink_log_fini(void)
 {
+       unregister_pernet_subsys(&nfnl_log_net_ops);
        nf_log_unregister(&nfulnl_logger);
-#ifdef CONFIG_PROC_FS
-       remove_proc_entry("nfnetlink_log", proc_net_netfilter);
-#endif
        nfnetlink_subsys_unregister(&nfulnl_subsys);
        netlink_unregister_notifier(&nfulnl_rtnl_notifier);
 }
index 42680b2baa118d7d09697718077df29d994a10a3..ef3cdb4bfeeae68556d292da2a1c626b7f467fd2 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/list.h>
 #include <net/sock.h>
 #include <net/netfilter/nf_queue.h>
+#include <net/netns/generic.h>
 #include <net/netfilter/nfnetlink_queue.h>
 
 #include <linux/atomic.h>
@@ -66,23 +67,31 @@ struct nfqnl_instance {
 
 typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long);
 
-static DEFINE_SPINLOCK(instances_lock);
+static int nfnl_queue_net_id __read_mostly;
 
 #define INSTANCE_BUCKETS       16
-static struct hlist_head instance_table[INSTANCE_BUCKETS] __read_mostly;
+struct nfnl_queue_net {
+       spinlock_t instances_lock;
+       struct hlist_head instance_table[INSTANCE_BUCKETS];
+};
+
+static struct nfnl_queue_net *nfnl_queue_pernet(struct net *net)
+{
+       return net_generic(net, nfnl_queue_net_id);
+}
 
 static inline u_int8_t instance_hashfn(u_int16_t queue_num)
 {
-       return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS;
+       return ((queue_num >> 8) ^ queue_num) % INSTANCE_BUCKETS;
 }
 
 static struct nfqnl_instance *
-instance_lookup(u_int16_t queue_num)
+instance_lookup(struct nfnl_queue_net *q, u_int16_t queue_num)
 {
        struct hlist_head *head;
        struct nfqnl_instance *inst;
 
-       head = &instance_table[instance_hashfn(queue_num)];
+       head = &q->instance_table[instance_hashfn(queue_num)];
        hlist_for_each_entry_rcu(inst, head, hlist) {
                if (inst->queue_num == queue_num)
                        return inst;
@@ -91,14 +100,15 @@ instance_lookup(u_int16_t queue_num)
 }
 
 static struct nfqnl_instance *
-instance_create(u_int16_t queue_num, int portid)
+instance_create(struct nfnl_queue_net *q, u_int16_t queue_num,
+               int portid)
 {
        struct nfqnl_instance *inst;
        unsigned int h;
        int err;
 
-       spin_lock(&instances_lock);
-       if (instance_lookup(queue_num)) {
+       spin_lock(&q->instances_lock);
+       if (instance_lookup(q, queue_num)) {
                err = -EEXIST;
                goto out_unlock;
        }
@@ -123,16 +133,16 @@ instance_create(u_int16_t queue_num, int portid)
        }
 
        h = instance_hashfn(queue_num);
-       hlist_add_head_rcu(&inst->hlist, &instance_table[h]);
+       hlist_add_head_rcu(&inst->hlist, &q->instance_table[h]);
 
-       spin_unlock(&instances_lock);
+       spin_unlock(&q->instances_lock);
 
        return inst;
 
 out_free:
        kfree(inst);
 out_unlock:
-       spin_unlock(&instances_lock);
+       spin_unlock(&q->instances_lock);
        return ERR_PTR(err);
 }
 
@@ -158,11 +168,11 @@ __instance_destroy(struct nfqnl_instance *inst)
 }
 
 static void
-instance_destroy(struct nfqnl_instance *inst)
+instance_destroy(struct nfnl_queue_net *q, struct nfqnl_instance *inst)
 {
-       spin_lock(&instances_lock);
+       spin_lock(&q->instances_lock);
        __instance_destroy(inst);
-       spin_unlock(&instances_lock);
+       spin_unlock(&q->instances_lock);
 }
 
 static inline void
@@ -217,14 +227,59 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data)
        spin_unlock_bh(&queue->lock);
 }
 
+static void
+nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
+{
+       int i, j = 0;
+       int plen = 0; /* length of skb->head fragment */
+       struct page *page;
+       unsigned int offset;
+
+       /* dont bother with small payloads */
+       if (len <= skb_tailroom(to)) {
+               skb_copy_bits(from, 0, skb_put(to, len), len);
+               return;
+       }
+
+       if (hlen) {
+               skb_copy_bits(from, 0, skb_put(to, hlen), hlen);
+               len -= hlen;
+       } else {
+               plen = min_t(int, skb_headlen(from), len);
+               if (plen) {
+                       page = virt_to_head_page(from->head);
+                       offset = from->data - (unsigned char *)page_address(page);
+                       __skb_fill_page_desc(to, 0, page, offset, plen);
+                       get_page(page);
+                       j = 1;
+                       len -= plen;
+               }
+       }
+
+       to->truesize += len + plen;
+       to->len += len + plen;
+       to->data_len += len + plen;
+
+       for (i = 0; i < skb_shinfo(from)->nr_frags; i++) {
+               if (!len)
+                       break;
+               skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i];
+               skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len);
+               len -= skb_shinfo(to)->frags[j].size;
+               skb_frag_ref(to, j);
+               j++;
+       }
+       skb_shinfo(to)->nr_frags = j;
+}
+
 static struct sk_buff *
 nfqnl_build_packet_message(struct nfqnl_instance *queue,
                           struct nf_queue_entry *entry,
                           __be32 **packet_id_ptr)
 {
-       sk_buff_data_t old_tail;
        size_t size;
        size_t data_len = 0, cap_len = 0;
+       int hlen = 0;
        struct sk_buff *skb;
        struct nlattr *nla;
        struct nfqnl_msg_packet_hdr *pmsg;
@@ -236,7 +291,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        struct nf_conn *ct = NULL;
        enum ip_conntrack_info uninitialized_var(ctinfo);
 
-       size =    NLMSG_SPACE(sizeof(struct nfgenmsg))
+       size =    nlmsg_total_size(sizeof(struct nfgenmsg))
                + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr))
                + nla_total_size(sizeof(u_int32_t))     /* ifindex */
                + nla_total_size(sizeof(u_int32_t))     /* ifindex */
@@ -246,8 +301,10 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
 #endif
                + nla_total_size(sizeof(u_int32_t))     /* mark */
                + nla_total_size(sizeof(struct nfqnl_msg_packet_hw))
-               + nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)
-               + nla_total_size(sizeof(u_int32_t)));   /* cap_len */
+               + nla_total_size(sizeof(u_int32_t));    /* cap_len */
+
+       if (entskb->tstamp.tv64)
+               size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp));
 
        outdev = entry->outdev;
 
@@ -265,7 +322,16 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
                if (data_len == 0 || data_len > entskb->len)
                        data_len = entskb->len;
 
-               size += nla_total_size(data_len);
+
+               if (!entskb->head_frag ||
+                   skb_headlen(entskb) < L1_CACHE_BYTES ||
+                   skb_shinfo(entskb)->nr_frags >= MAX_SKB_FRAGS)
+                       hlen = skb_headlen(entskb);
+
+               if (skb_has_frag_list(entskb))
+                       hlen = entskb->len;
+               hlen = min_t(int, data_len, hlen);
+               size += sizeof(struct nlattr) + hlen;
                cap_len = entskb->len;
                break;
        }
@@ -273,11 +339,11 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        if (queue->flags & NFQA_CFG_F_CONNTRACK)
                ct = nfqnl_ct_get(entskb, &size, &ctinfo);
 
-       skb = alloc_skb(size, GFP_ATOMIC);
+       skb = nfnetlink_alloc_skb(&init_net, size, queue->peer_portid,
+                                 GFP_ATOMIC);
        if (!skb)
                return NULL;
 
-       old_tail = skb->tail;
        nlh = nlmsg_put(skb, 0, 0,
                        NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET,
                        sizeof(struct nfgenmsg), 0);
@@ -382,31 +448,26 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
                        goto nla_put_failure;
        }
 
+       if (ct && nfqnl_ct_put(skb, ct, ctinfo) < 0)
+               goto nla_put_failure;
+
+       if (cap_len > 0 && nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len)))
+               goto nla_put_failure;
+
        if (data_len) {
                struct nlattr *nla;
-               int sz = nla_attr_size(data_len);
 
-               if (skb_tailroom(skb) < nla_total_size(data_len)) {
-                       printk(KERN_WARNING "nf_queue: no tailroom!\n");
-                       kfree_skb(skb);
-                       return NULL;
-               }
+               if (skb_tailroom(skb) < sizeof(*nla) + hlen)
+                       goto nla_put_failure;
 
-               nla = (struct nlattr *)skb_put(skb, nla_total_size(data_len));
+               nla = (struct nlattr *)skb_put(skb, sizeof(*nla));
                nla->nla_type = NFQA_PAYLOAD;
-               nla->nla_len = sz;
+               nla->nla_len = nla_attr_size(data_len);
 
-               if (skb_copy_bits(entskb, 0, nla_data(nla), data_len))
-                       BUG();
+               nfqnl_zcopy(skb, entskb, data_len, hlen);
        }
 
-       if (ct && nfqnl_ct_put(skb, ct, ctinfo) < 0)
-               goto nla_put_failure;
-
-       if (cap_len > 0 && nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len)))
-               goto nla_put_failure;
-
-       nlh->nlmsg_len = skb->tail - old_tail;
+       nlh->nlmsg_len = skb->len;
        return skb;
 
 nla_put_failure:
@@ -423,9 +484,12 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
        int err = -ENOBUFS;
        __be32 *packet_id_ptr;
        int failopen = 0;
+       struct net *net = dev_net(entry->indev ?
+                                 entry->indev : entry->outdev);
+       struct nfnl_queue_net *q = nfnl_queue_pernet(net);
 
        /* rcu_read_lock()ed by nf_hook_slow() */
-       queue = instance_lookup(queuenum);
+       queue = instance_lookup(q, queuenum);
        if (!queue) {
                err = -ESRCH;
                goto err_out;
@@ -462,7 +526,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
        *packet_id_ptr = htonl(entry->id);
 
        /* nfnetlink_unicast will either free the nskb or add it to a socket */
-       err = nfnetlink_unicast(nskb, &init_net, queue->peer_portid, MSG_DONTWAIT);
+       err = nfnetlink_unicast(nskb, net, queue->peer_portid, MSG_DONTWAIT);
        if (err < 0) {
                queue->queue_user_dropped++;
                goto err_out_unlock;
@@ -575,15 +639,16 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
 /* drop all packets with either indev or outdev == ifindex from all queue
  * instances */
 static void
-nfqnl_dev_drop(int ifindex)
+nfqnl_dev_drop(struct net *net, int ifindex)
 {
        int i;
+       struct nfnl_queue_net *q = nfnl_queue_pernet(net);
 
        rcu_read_lock();
 
        for (i = 0; i < INSTANCE_BUCKETS; i++) {
                struct nfqnl_instance *inst;
-               struct hlist_head *head = &instance_table[i];
+               struct hlist_head *head = &q->instance_table[i];
 
                hlist_for_each_entry_rcu(inst, head, hlist)
                        nfqnl_flush(inst, dev_cmp, ifindex);
@@ -600,12 +665,9 @@ nfqnl_rcv_dev_event(struct notifier_block *this,
 {
        struct net_device *dev = ptr;
 
-       if (!net_eq(dev_net(dev), &init_net))
-               return NOTIFY_DONE;
-
        /* Drop any packets associated with the downed device */
        if (event == NETDEV_DOWN)
-               nfqnl_dev_drop(dev->ifindex);
+               nfqnl_dev_drop(dev_net(dev), dev->ifindex);
        return NOTIFY_DONE;
 }
 
@@ -618,24 +680,24 @@ nfqnl_rcv_nl_event(struct notifier_block *this,
                   unsigned long event, void *ptr)
 {
        struct netlink_notify *n = ptr;
+       struct nfnl_queue_net *q = nfnl_queue_pernet(n->net);
 
        if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) {
                int i;
 
                /* destroy all instances for this portid */
-               spin_lock(&instances_lock);
+               spin_lock(&q->instances_lock);
                for (i = 0; i < INSTANCE_BUCKETS; i++) {
                        struct hlist_node *t2;
                        struct nfqnl_instance *inst;
-                       struct hlist_head *head = &instance_table[i];
+                       struct hlist_head *head = &q->instance_table[i];
 
                        hlist_for_each_entry_safe(inst, t2, head, hlist) {
-                               if ((n->net == &init_net) &&
-                                   (n->portid == inst->peer_portid))
+                               if (n->portid == inst->peer_portid)
                                        __instance_destroy(inst);
                        }
                }
-               spin_unlock(&instances_lock);
+               spin_unlock(&q->instances_lock);
        }
        return NOTIFY_DONE;
 }
@@ -656,11 +718,12 @@ static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = {
        [NFQA_MARK]             = { .type = NLA_U32 },
 };
 
-static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlportid)
+static struct nfqnl_instance *
+verdict_instance_lookup(struct nfnl_queue_net *q, u16 queue_num, int nlportid)
 {
        struct nfqnl_instance *queue;
 
-       queue = instance_lookup(queue_num);
+       queue = instance_lookup(q, queue_num);
        if (!queue)
                return ERR_PTR(-ENODEV);
 
@@ -704,7 +767,11 @@ nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb,
        LIST_HEAD(batch_list);
        u16 queue_num = ntohs(nfmsg->res_id);
 
-       queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).portid);
+       struct net *net = sock_net(ctnl);
+       struct nfnl_queue_net *q = nfnl_queue_pernet(net);
+
+       queue = verdict_instance_lookup(q, queue_num,
+                                       NETLINK_CB(skb).portid);
        if (IS_ERR(queue))
                return PTR_ERR(queue);
 
@@ -752,10 +819,13 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
        enum ip_conntrack_info uninitialized_var(ctinfo);
        struct nf_conn *ct = NULL;
 
-       queue = instance_lookup(queue_num);
-       if (!queue)
+       struct net *net = sock_net(ctnl);
+       struct nfnl_queue_net *q = nfnl_queue_pernet(net);
 
-       queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).portid);
+       queue = instance_lookup(q, queue_num);
+       if (!queue)
+               queue = verdict_instance_lookup(q, queue_num,
+                                               NETLINK_CB(skb).portid);
        if (IS_ERR(queue))
                return PTR_ERR(queue);
 
@@ -819,6 +889,8 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
        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);
        int ret = 0;
 
        if (nfqa[NFQA_CFG_CMD]) {
@@ -832,7 +904,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
        }
 
        rcu_read_lock();
-       queue = instance_lookup(queue_num);
+       queue = instance_lookup(q, queue_num);
        if (queue && queue->peer_portid != NETLINK_CB(skb).portid) {
                ret = -EPERM;
                goto err_out_unlock;
@@ -845,7 +917,8 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
                                ret = -EBUSY;
                                goto err_out_unlock;
                        }
-                       queue = instance_create(queue_num, NETLINK_CB(skb).portid);
+                       queue = instance_create(q, queue_num,
+                                               NETLINK_CB(skb).portid);
                        if (IS_ERR(queue)) {
                                ret = PTR_ERR(queue);
                                goto err_out_unlock;
@@ -856,7 +929,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
                                ret = -ENODEV;
                                goto err_out_unlock;
                        }
-                       instance_destroy(queue);
+                       instance_destroy(q, queue);
                        break;
                case NFQNL_CFG_CMD_PF_BIND:
                case NFQNL_CFG_CMD_PF_UNBIND:
@@ -950,19 +1023,24 @@ static const struct nfnetlink_subsystem nfqnl_subsys = {
 
 #ifdef CONFIG_PROC_FS
 struct iter_state {
+       struct seq_net_private p;
        unsigned int bucket;
 };
 
 static struct hlist_node *get_first(struct seq_file *seq)
 {
        struct iter_state *st = seq->private;
+       struct net *net;
+       struct nfnl_queue_net *q;
 
        if (!st)
                return NULL;
 
+       net = seq_file_net(seq);
+       q = nfnl_queue_pernet(net);
        for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) {
-               if (!hlist_empty(&instance_table[st->bucket]))
-                       return instance_table[st->bucket].first;
+               if (!hlist_empty(&q->instance_table[st->bucket]))
+                       return q->instance_table[st->bucket].first;
        }
        return NULL;
 }
@@ -970,13 +1048,17 @@ static struct hlist_node *get_first(struct seq_file *seq)
 static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h)
 {
        struct iter_state *st = seq->private;
+       struct net *net = seq_file_net(seq);
 
        h = h->next;
        while (!h) {
+               struct nfnl_queue_net *q;
+
                if (++st->bucket >= INSTANCE_BUCKETS)
                        return NULL;
 
-               h = instance_table[st->bucket].first;
+               q = nfnl_queue_pernet(net);
+               h = q->instance_table[st->bucket].first;
        }
        return h;
 }
@@ -992,11 +1074,11 @@ static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos)
        return pos ? NULL : head;
 }
 
-static void *seq_start(struct seq_file *seq, loff_t *pos)
-       __acquires(instances_lock)
+static void *seq_start(struct seq_file *s, loff_t *pos)
+       __acquires(nfnl_queue_pernet(seq_file_net(s))->instances_lock)
 {
-       spin_lock(&instances_lock);
-       return get_idx(seq, *pos);
+       spin_lock(&nfnl_queue_pernet(seq_file_net(s))->instances_lock);
+       return get_idx(s, *pos);
 }
 
 static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
@@ -1006,9 +1088,9 @@ static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
 }
 
 static void seq_stop(struct seq_file *s, void *v)
-       __releases(instances_lock)
+       __releases(nfnl_queue_pernet(seq_file_net(s))->instances_lock)
 {
-       spin_unlock(&instances_lock);
+       spin_unlock(&nfnl_queue_pernet(seq_file_net(s))->instances_lock);
 }
 
 static int seq_show(struct seq_file *s, void *v)
@@ -1032,7 +1114,7 @@ static const struct seq_operations nfqnl_seq_ops = {
 
 static int nfqnl_open(struct inode *inode, struct file *file)
 {
-       return seq_open_private(file, &nfqnl_seq_ops,
+       return seq_open_net(inode, file, &nfqnl_seq_ops,
                        sizeof(struct iter_state));
 }
 
@@ -1041,41 +1123,63 @@ static const struct file_operations nfqnl_file_ops = {
        .open    = nfqnl_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
-       .release = seq_release_private,
+       .release = seq_release_net,
 };
 
 #endif /* PROC_FS */
 
-static int __init nfnetlink_queue_init(void)
+static int __net_init nfnl_queue_net_init(struct net *net)
 {
-       int i, status = -ENOMEM;
+       unsigned int i;
+       struct nfnl_queue_net *q = nfnl_queue_pernet(net);
 
        for (i = 0; i < INSTANCE_BUCKETS; i++)
-               INIT_HLIST_HEAD(&instance_table[i]);
+               INIT_HLIST_HEAD(&q->instance_table[i]);
+
+       spin_lock_init(&q->instances_lock);
+
+#ifdef CONFIG_PROC_FS
+       if (!proc_create("nfnetlink_queue", 0440,
+                        net->nf.proc_netfilter, &nfqnl_file_ops))
+               return -ENOMEM;
+#endif
+       return 0;
+}
+
+static void __net_exit nfnl_queue_net_exit(struct net *net)
+{
+       remove_proc_entry("nfnetlink_queue", net->nf.proc_netfilter);
+}
+
+static struct pernet_operations nfnl_queue_net_ops = {
+       .init   = nfnl_queue_net_init,
+       .exit   = nfnl_queue_net_exit,
+       .id     = &nfnl_queue_net_id,
+       .size   = sizeof(struct nfnl_queue_net),
+};
+
+static int __init nfnetlink_queue_init(void)
+{
+       int status = -ENOMEM;
 
        netlink_register_notifier(&nfqnl_rtnl_notifier);
        status = nfnetlink_subsys_register(&nfqnl_subsys);
        if (status < 0) {
-               printk(KERN_ERR "nf_queue: failed to create netlink socket\n");
+               pr_err("nf_queue: failed to create netlink socket\n");
                goto cleanup_netlink_notifier;
        }
 
-#ifdef CONFIG_PROC_FS
-       if (!proc_create("nfnetlink_queue", 0440,
-                        proc_net_netfilter, &nfqnl_file_ops)) {
-               status = -ENOMEM;
+       status = register_pernet_subsys(&nfnl_queue_net_ops);
+       if (status < 0) {
+               pr_err("nf_queue: failed to register pernet ops\n");
                goto cleanup_subsys;
        }
-#endif
-
        register_netdevice_notifier(&nfqnl_dev_notifier);
        nf_register_queue_handler(&nfqh);
        return status;
 
-#ifdef CONFIG_PROC_FS
 cleanup_subsys:
        nfnetlink_subsys_unregister(&nfqnl_subsys);
-#endif
 cleanup_netlink_notifier:
        netlink_unregister_notifier(&nfqnl_rtnl_notifier);
        return status;
@@ -1085,9 +1189,7 @@ static void __exit nfnetlink_queue_fini(void)
 {
        nf_unregister_queue_handler();
        unregister_netdevice_notifier(&nfqnl_dev_notifier);
-#ifdef CONFIG_PROC_FS
-       remove_proc_entry("nfnetlink_queue", proc_net_netfilter);
-#endif
+       unregister_pernet_subsys(&nfnl_queue_net_ops);
        nfnetlink_subsys_unregister(&nfqnl_subsys);
        netlink_unregister_notifier(&nfqnl_rtnl_notifier);
 
index 686c7715d777ba2b6792641b644b2ed7f51c1f9e..1a73b18683b6d9f037d891a830b302613a974c47 100644 (file)
@@ -2,6 +2,7 @@
  * x_tables core - Backend for {ip,ip6,arp}_tables
  *
  * Copyright (C) 2006-2006 Harald Welte <laforge@netfilter.org>
+ * Copyright (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * Based on existing ip_tables code which is
  *   Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
index fa40096940a1712be7ac0c517ea5f1d7af4dfd56..fe573f6c9e9161e4201e6cbc8698c3b58b0583b2 100644 (file)
@@ -474,7 +474,14 @@ ipt_log_packet(u_int8_t pf,
               const struct nf_loginfo *loginfo,
               const char *prefix)
 {
-       struct sbuff *m = sb_open();
+       struct sbuff *m;
+       struct net *net = dev_net(in ? in : out);
+
+       /* FIXME: Disabled from containers until syslog ns is supported */
+       if (!net_eq(net, &init_net))
+               return;
+
+       m = sb_open();
 
        if (!loginfo)
                loginfo = &default_loginfo;
@@ -798,7 +805,14 @@ ip6t_log_packet(u_int8_t pf,
                const struct nf_loginfo *loginfo,
                const char *prefix)
 {
-       struct sbuff *m = sb_open();
+       struct sbuff *m;
+       struct net *net = dev_net(in ? in : out);
+
+       /* FIXME: Disabled from containers until syslog ns is supported */
+       if (!net_eq(net, &init_net))
+               return;
+
+       m = sb_open();
 
        if (!loginfo)
                loginfo = &default_loginfo;
@@ -893,23 +907,55 @@ static struct nf_logger ip6t_log_logger __read_mostly = {
 };
 #endif
 
+static int __net_init log_net_init(struct net *net)
+{
+       nf_log_set(net, NFPROTO_IPV4, &ipt_log_logger);
+#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
+       nf_log_set(net, NFPROTO_IPV6, &ip6t_log_logger);
+#endif
+       return 0;
+}
+
+static void __net_exit log_net_exit(struct net *net)
+{
+       nf_log_unset(net, &ipt_log_logger);
+#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
+       nf_log_unset(net, &ip6t_log_logger);
+#endif
+}
+
+static struct pernet_operations log_net_ops = {
+       .init = log_net_init,
+       .exit = log_net_exit,
+};
+
 static int __init log_tg_init(void)
 {
        int ret;
 
+       ret = register_pernet_subsys(&log_net_ops);
+       if (ret < 0)
+               goto err_pernet;
+
        ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs));
        if (ret < 0)
-               return ret;
+               goto err_target;
 
        nf_log_register(NFPROTO_IPV4, &ipt_log_logger);
 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
        nf_log_register(NFPROTO_IPV6, &ip6t_log_logger);
 #endif
        return 0;
+
+err_target:
+       unregister_pernet_subsys(&log_net_ops);
+err_pernet:
+       return ret;
 }
 
 static void __exit log_tg_exit(void)
 {
+       unregister_pernet_subsys(&log_net_ops);
        nf_log_unregister(&ipt_log_logger);
 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
        nf_log_unregister(&ip6t_log_logger);
index 817f9e9f2b16c70930df5e51d62f5fd606e76f69..1e2fae32f81b9118f50a7c0d403cee2a324af252 100644 (file)
@@ -76,22 +76,31 @@ static u32 hash_v6(const struct sk_buff *skb)
 }
 #endif
 
-static unsigned int
-nfqueue_tg_v1(struct sk_buff *skb, const struct xt_action_param *par)
+static u32
+nfqueue_hash(const struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_NFQ_info_v1 *info = par->targinfo;
        u32 queue = info->queuenum;
 
-       if (info->queues_total > 1) {
-               if (par->family == NFPROTO_IPV4)
-                       queue = (((u64) hash_v4(skb) * info->queues_total) >>
-                                32) + queue;
+       if (par->family == NFPROTO_IPV4)
+               queue += ((u64) hash_v4(skb) * info->queues_total) >> 32;
 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
-               else if (par->family == NFPROTO_IPV6)
-                       queue = (((u64) hash_v6(skb) * info->queues_total) >>
-                                32) + queue;
+       else if (par->family == NFPROTO_IPV6)
+               queue += ((u64) hash_v6(skb) * info->queues_total) >> 32;
 #endif
-       }
+
+       return queue;
+}
+
+static unsigned int
+nfqueue_tg_v1(struct sk_buff *skb, const struct xt_action_param *par)
+{
+       const struct xt_NFQ_info_v1 *info = par->targinfo;
+       u32 queue = info->queuenum;
+
+       if (info->queues_total > 1)
+               queue = nfqueue_hash(skb, par);
+
        return NF_QUEUE_NR(queue);
 }
 
@@ -108,7 +117,7 @@ nfqueue_tg_v2(struct sk_buff *skb, const struct xt_action_param *par)
 
 static int nfqueue_tg_check(const struct xt_tgchk_param *par)
 {
-       const struct xt_NFQ_info_v2 *info = par->targinfo;
+       const struct xt_NFQ_info_v3 *info = par->targinfo;
        u32 maxid;
 
        if (unlikely(!rnd_inited)) {
@@ -125,11 +134,32 @@ static int nfqueue_tg_check(const struct xt_tgchk_param *par)
                       info->queues_total, maxid);
                return -ERANGE;
        }
-       if (par->target->revision == 2 && info->bypass > 1)
+       if (par->target->revision == 2 && info->flags > 1)
                return -EINVAL;
+       if (par->target->revision == 3 && info->flags & ~NFQ_FLAG_MASK)
+               return -EINVAL;
+
        return 0;
 }
 
+static unsigned int
+nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
+{
+       const struct xt_NFQ_info_v3 *info = par->targinfo;
+       u32 queue = info->queuenum;
+
+       if (info->queues_total > 1) {
+               if (info->flags & NFQ_FLAG_CPU_FANOUT) {
+                       int cpu = smp_processor_id();
+
+                       queue = info->queuenum + cpu % info->queues_total;
+               } else
+                       queue = nfqueue_hash(skb, par);
+       }
+
+       return NF_QUEUE_NR(queue);
+}
+
 static struct xt_target nfqueue_tg_reg[] __read_mostly = {
        {
                .name           = "NFQUEUE",
@@ -156,6 +186,15 @@ static struct xt_target nfqueue_tg_reg[] __read_mostly = {
                .targetsize     = sizeof(struct xt_NFQ_info_v2),
                .me             = THIS_MODULE,
        },
+       {
+               .name           = "NFQUEUE",
+               .revision       = 3,
+               .family         = NFPROTO_UNSPEC,
+               .checkentry     = nfqueue_tg_check,
+               .target         = nfqueue_tg_v3,
+               .targetsize     = sizeof(struct xt_NFQ_info_v3),
+               .me             = THIS_MODULE,
+       },
 };
 
 static int __init nfqueue_tg_init(void)
index 71a266de5fb47352209d7380dcd75dff841e067e..a75240f0d42b8da245035a8f8ee59b51895c445e 100644 (file)
@@ -2,6 +2,7 @@
  * This is a module which is used for setting the MSS option in TCP packets.
  *
  * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
+ * Copyright (C) 2007 Patrick McHardy <kaber@trash.net>
  *
  * 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
index 61805d7b38aa62e880c1e8f4e32f79d1bf5ffdc2..188404b9b002f1a8ef875ceb3a7aebddb13c6c8e 100644 (file)
@@ -3,6 +3,7 @@
  *     information. (Superset of Rusty's minimalistic state match.)
  *
  *     (C) 2001  Marc Boucher (marc@mbsi.ca).
+ *     (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *     Copyright Â© CC Computer Consultants GmbH, 2007 - 2008
  *
  *     This program is free software; you can redistribute it and/or modify
index f330e8beaf69830c274257d48e2eb5cf91ffd834..0199e7bb8f818ec856f6413f44e43e2a780b0bd7 100644 (file)
@@ -3,6 +3,7 @@
  *     separately for each hashbucket (sourceip/sourceport/dstip/dstport)
  *
  *     (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
+ *     (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *     Copyright Â© CC Computer Consultants GmbH, 2007 - 2008
  *
  * Development of this code was funded by Astaro AG, http://www.astaro.com/
index a4c1e4528cac7e3e820f1cecfb8ec22e8dcf5966..bef85059655892982da73cc694183afc7399782c 100644 (file)
@@ -1,5 +1,6 @@
 /* (C) 1999 Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
  * (C) 1999 Hervé Eychenne <eychenne@info.enserb.u-bordeaux.fr>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  *
  * 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
index a5e673d32bdaec2dec9fb7fce26905fff1459b77..647d989a01e6b740f2d6c5b3d4f463259cecf643 100644 (file)
@@ -201,6 +201,7 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
        unsigned char opts[MAX_IPOPTLEN];
        const struct xt_osf_finger *kf;
        const struct xt_osf_user_finger *f;
+       struct net *net = dev_net(p->in ? p->in : p->out);
 
        if (!info)
                return false;
@@ -325,7 +326,7 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
                        fcount++;
 
                        if (info->flags & XT_OSF_LOG)
-                               nf_log_packet(p->family, p->hooknum, skb,
+                               nf_log_packet(net, p->family, p->hooknum, skb,
                                        p->in, p->out, NULL,
                                        "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n",
                                        f->genre, f->version, f->subtype,
@@ -341,7 +342,8 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
        rcu_read_unlock();
 
        if (!fcount && (info->flags & XT_OSF_LOG))
-               nf_log_packet(p->family, p->hooknum, skb, p->in, p->out, NULL,
+               nf_log_packet(net, p->family, p->hooknum, skb, p->in,
+                             p->out, NULL,
                        "Remote OS is not known: %pI4:%u -> %pI4:%u\n",
                                &ip->saddr, ntohs(tcp->source),
                                &ip->daddr, ntohs(tcp->dest));
diff --git a/net/netlink/Kconfig b/net/netlink/Kconfig
new file mode 100644 (file)
index 0000000..5d6e8c0
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Netlink Sockets
+#
+
+config NETLINK_DIAG
+       tristate "NETLINK: socket monitoring interface"
+       default n
+       ---help---
+         Support for NETLINK socket monitoring interface used by the ss tool.
+         If unsure, say Y.
index bdd6ddf4e95beb401ddd883f7caa00b9be389b9d..e837917f6c03e919dbaa555e4a984a1a3fc0ad58 100644 (file)
@@ -3,3 +3,6 @@
 #
 
 obj-y                                  := af_netlink.o genetlink.o
+
+obj-$(CONFIG_NETLINK_DIAG)     += netlink_diag.o
+netlink_diag-y                 := diag.o
index 1e3fd5bfcd86a995fcbfd4277452738a7a823727..2a3e9ba814c4125d877554b80bc9e7353e7a21e9 100644 (file)
@@ -3,6 +3,7 @@
  *
  *             Authors:        Alan Cox <alan@lxorguk.ukuu.org.uk>
  *                             Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *                             Patrick McHardy <kaber@trash.net>
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
 #include <linux/types.h>
 #include <linux/audit.h>
 #include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <asm/cacheflush.h>
 
 #include <net/net_namespace.h>
 #include <net/sock.h>
 #include <net/scm.h>
 #include <net/netlink.h>
 
-#define NLGRPSZ(x)     (ALIGN(x, sizeof(unsigned long) * 8) / 8)
-#define NLGRPLONGS(x)  (NLGRPSZ(x)/sizeof(unsigned long))
-
-struct netlink_sock {
-       /* struct sock has to be the first member of netlink_sock */
-       struct sock             sk;
-       u32                     portid;
-       u32                     dst_portid;
-       u32                     dst_group;
-       u32                     flags;
-       u32                     subscriptions;
-       u32                     ngroups;
-       unsigned long           *groups;
-       unsigned long           state;
-       wait_queue_head_t       wait;
-       struct netlink_callback *cb;
-       struct mutex            *cb_mutex;
-       struct mutex            cb_def_mutex;
-       void                    (*netlink_rcv)(struct sk_buff *skb);
-       void                    (*netlink_bind)(int group);
-       struct module           *module;
-};
+#include "af_netlink.h"
 
 struct listeners {
        struct rcu_head         rcu;
        unsigned long           masks[0];
 };
 
+/* state bits */
+#define NETLINK_CONGESTED      0x0
+
+/* flags */
 #define NETLINK_KERNEL_SOCKET  0x1
 #define NETLINK_RECV_PKTINFO   0x2
 #define NETLINK_BROADCAST_SEND_ERROR   0x4
 #define NETLINK_RECV_NO_ENOBUFS        0x8
 
-static inline struct netlink_sock *nlk_sk(struct sock *sk)
-{
-       return container_of(sk, struct netlink_sock, sk);
-}
-
 static inline int netlink_is_kernel(struct sock *sk)
 {
        return nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET;
 }
 
-struct nl_portid_hash {
-       struct hlist_head       *table;
-       unsigned long           rehash_time;
-
-       unsigned int            mask;
-       unsigned int            shift;
-
-       unsigned int            entries;
-       unsigned int            max_shift;
-
-       u32                     rnd;
-};
-
-struct netlink_table {
-       struct nl_portid_hash   hash;
-       struct hlist_head       mc_list;
-       struct listeners __rcu  *listeners;
-       unsigned int            flags;
-       unsigned int            groups;
-       struct mutex            *cb_mutex;
-       struct module           *module;
-       void                    (*bind)(int group);
-       int                     registered;
-};
-
-static struct netlink_table *nl_table;
+struct netlink_table *nl_table;
+EXPORT_SYMBOL_GPL(nl_table);
 
 static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait);
 
 static int netlink_dump(struct sock *sk);
+static void netlink_skb_destructor(struct sk_buff *skb);
 
-static DEFINE_RWLOCK(nl_table_lock);
+DEFINE_RWLOCK(nl_table_lock);
+EXPORT_SYMBOL_GPL(nl_table_lock);
 static atomic_t nl_table_users = ATOMIC_INIT(0);
 
 #define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock));
@@ -152,6 +111,599 @@ static inline struct hlist_head *nl_portid_hashfn(struct nl_portid_hash *hash, u
        return &hash->table[jhash_1word(portid, hash->rnd) & hash->mask];
 }
 
+static void netlink_overrun(struct sock *sk)
+{
+       struct netlink_sock *nlk = nlk_sk(sk);
+
+       if (!(nlk->flags & NETLINK_RECV_NO_ENOBUFS)) {
+               if (!test_and_set_bit(NETLINK_CONGESTED, &nlk_sk(sk)->state)) {
+                       sk->sk_err = ENOBUFS;
+                       sk->sk_error_report(sk);
+               }
+       }
+       atomic_inc(&sk->sk_drops);
+}
+
+static void netlink_rcv_wake(struct sock *sk)
+{
+       struct netlink_sock *nlk = nlk_sk(sk);
+
+       if (skb_queue_empty(&sk->sk_receive_queue))
+               clear_bit(NETLINK_CONGESTED, &nlk->state);
+       if (!test_bit(NETLINK_CONGESTED, &nlk->state))
+               wake_up_interruptible(&nlk->wait);
+}
+
+#ifdef CONFIG_NETLINK_MMAP
+static bool netlink_skb_is_mmaped(const struct sk_buff *skb)
+{
+       return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED;
+}
+
+static bool netlink_rx_is_mmaped(struct sock *sk)
+{
+       return nlk_sk(sk)->rx_ring.pg_vec != NULL;
+}
+
+static bool netlink_tx_is_mmaped(struct sock *sk)
+{
+       return nlk_sk(sk)->tx_ring.pg_vec != NULL;
+}
+
+static __pure struct page *pgvec_to_page(const void *addr)
+{
+       if (is_vmalloc_addr(addr))
+               return vmalloc_to_page(addr);
+       else
+               return virt_to_page(addr);
+}
+
+static void free_pg_vec(void **pg_vec, unsigned int order, unsigned int len)
+{
+       unsigned int i;
+
+       for (i = 0; i < len; i++) {
+               if (pg_vec[i] != NULL) {
+                       if (is_vmalloc_addr(pg_vec[i]))
+                               vfree(pg_vec[i]);
+                       else
+                               free_pages((unsigned long)pg_vec[i], order);
+               }
+       }
+       kfree(pg_vec);
+}
+
+static void *alloc_one_pg_vec_page(unsigned long order)
+{
+       void *buffer;
+       gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | __GFP_ZERO |
+                         __GFP_NOWARN | __GFP_NORETRY;
+
+       buffer = (void *)__get_free_pages(gfp_flags, order);
+       if (buffer != NULL)
+               return buffer;
+
+       buffer = vzalloc((1 << order) * PAGE_SIZE);
+       if (buffer != NULL)
+               return buffer;
+
+       gfp_flags &= ~__GFP_NORETRY;
+       return (void *)__get_free_pages(gfp_flags, order);
+}
+
+static void **alloc_pg_vec(struct netlink_sock *nlk,
+                          struct nl_mmap_req *req, unsigned int order)
+{
+       unsigned int block_nr = req->nm_block_nr;
+       unsigned int i;
+       void **pg_vec, *ptr;
+
+       pg_vec = kcalloc(block_nr, sizeof(void *), GFP_KERNEL);
+       if (pg_vec == NULL)
+               return NULL;
+
+       for (i = 0; i < block_nr; i++) {
+               pg_vec[i] = ptr = alloc_one_pg_vec_page(order);
+               if (pg_vec[i] == NULL)
+                       goto err1;
+       }
+
+       return pg_vec;
+err1:
+       free_pg_vec(pg_vec, order, block_nr);
+       return NULL;
+}
+
+static int netlink_set_ring(struct sock *sk, struct nl_mmap_req *req,
+                           bool closing, bool tx_ring)
+{
+       struct netlink_sock *nlk = nlk_sk(sk);
+       struct netlink_ring *ring;
+       struct sk_buff_head *queue;
+       void **pg_vec = NULL;
+       unsigned int order = 0;
+       int err;
+
+       ring  = tx_ring ? &nlk->tx_ring : &nlk->rx_ring;
+       queue = tx_ring ? &sk->sk_write_queue : &sk->sk_receive_queue;
+
+       if (!closing) {
+               if (atomic_read(&nlk->mapped))
+                       return -EBUSY;
+               if (atomic_read(&ring->pending))
+                       return -EBUSY;
+       }
+
+       if (req->nm_block_nr) {
+               if (ring->pg_vec != NULL)
+                       return -EBUSY;
+
+               if ((int)req->nm_block_size <= 0)
+                       return -EINVAL;
+               if (!IS_ALIGNED(req->nm_block_size, PAGE_SIZE))
+                       return -EINVAL;
+               if (req->nm_frame_size < NL_MMAP_HDRLEN)
+                       return -EINVAL;
+               if (!IS_ALIGNED(req->nm_frame_size, NL_MMAP_MSG_ALIGNMENT))
+                       return -EINVAL;
+
+               ring->frames_per_block = req->nm_block_size /
+                                        req->nm_frame_size;
+               if (ring->frames_per_block == 0)
+                       return -EINVAL;
+               if (ring->frames_per_block * req->nm_block_nr !=
+                   req->nm_frame_nr)
+                       return -EINVAL;
+
+               order = get_order(req->nm_block_size);
+               pg_vec = alloc_pg_vec(nlk, req, order);
+               if (pg_vec == NULL)
+                       return -ENOMEM;
+       } else {
+               if (req->nm_frame_nr)
+                       return -EINVAL;
+       }
+
+       err = -EBUSY;
+       mutex_lock(&nlk->pg_vec_lock);
+       if (closing || atomic_read(&nlk->mapped) == 0) {
+               err = 0;
+               spin_lock_bh(&queue->lock);
+
+               ring->frame_max         = req->nm_frame_nr - 1;
+               ring->head              = 0;
+               ring->frame_size        = req->nm_frame_size;
+               ring->pg_vec_pages      = req->nm_block_size / PAGE_SIZE;
+
+               swap(ring->pg_vec_len, req->nm_block_nr);
+               swap(ring->pg_vec_order, order);
+               swap(ring->pg_vec, pg_vec);
+
+               __skb_queue_purge(queue);
+               spin_unlock_bh(&queue->lock);
+
+               WARN_ON(atomic_read(&nlk->mapped));
+       }
+       mutex_unlock(&nlk->pg_vec_lock);
+
+       if (pg_vec)
+               free_pg_vec(pg_vec, order, req->nm_block_nr);
+       return err;
+}
+
+static void netlink_mm_open(struct vm_area_struct *vma)
+{
+       struct file *file = vma->vm_file;
+       struct socket *sock = file->private_data;
+       struct sock *sk = sock->sk;
+
+       if (sk)
+               atomic_inc(&nlk_sk(sk)->mapped);
+}
+
+static void netlink_mm_close(struct vm_area_struct *vma)
+{
+       struct file *file = vma->vm_file;
+       struct socket *sock = file->private_data;
+       struct sock *sk = sock->sk;
+
+       if (sk)
+               atomic_dec(&nlk_sk(sk)->mapped);
+}
+
+static const struct vm_operations_struct netlink_mmap_ops = {
+       .open   = netlink_mm_open,
+       .close  = netlink_mm_close,
+};
+
+static int netlink_mmap(struct file *file, struct socket *sock,
+                       struct vm_area_struct *vma)
+{
+       struct sock *sk = sock->sk;
+       struct netlink_sock *nlk = nlk_sk(sk);
+       struct netlink_ring *ring;
+       unsigned long start, size, expected;
+       unsigned int i;
+       int err = -EINVAL;
+
+       if (vma->vm_pgoff)
+               return -EINVAL;
+
+       mutex_lock(&nlk->pg_vec_lock);
+
+       expected = 0;
+       for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) {
+               if (ring->pg_vec == NULL)
+                       continue;
+               expected += ring->pg_vec_len * ring->pg_vec_pages * PAGE_SIZE;
+       }
+
+       if (expected == 0)
+               goto out;
+
+       size = vma->vm_end - vma->vm_start;
+       if (size != expected)
+               goto out;
+
+       start = vma->vm_start;
+       for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) {
+               if (ring->pg_vec == NULL)
+                       continue;
+
+               for (i = 0; i < ring->pg_vec_len; i++) {
+                       struct page *page;
+                       void *kaddr = ring->pg_vec[i];
+                       unsigned int pg_num;
+
+                       for (pg_num = 0; pg_num < ring->pg_vec_pages; pg_num++) {
+                               page = pgvec_to_page(kaddr);
+                               err = vm_insert_page(vma, start, page);
+                               if (err < 0)
+                                       goto out;
+                               start += PAGE_SIZE;
+                               kaddr += PAGE_SIZE;
+                       }
+               }
+       }
+
+       atomic_inc(&nlk->mapped);
+       vma->vm_ops = &netlink_mmap_ops;
+       err = 0;
+out:
+       mutex_unlock(&nlk->pg_vec_lock);
+       return 0;
+}
+
+static void netlink_frame_flush_dcache(const struct nl_mmap_hdr *hdr)
+{
+#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE == 1
+       struct page *p_start, *p_end;
+
+       /* First page is flushed through netlink_{get,set}_status */
+       p_start = pgvec_to_page(hdr + PAGE_SIZE);
+       p_end   = pgvec_to_page((void *)hdr + NL_MMAP_MSG_HDRLEN + hdr->nm_len - 1);
+       while (p_start <= p_end) {
+               flush_dcache_page(p_start);
+               p_start++;
+       }
+#endif
+}
+
+static enum nl_mmap_status netlink_get_status(const struct nl_mmap_hdr *hdr)
+{
+       smp_rmb();
+       flush_dcache_page(pgvec_to_page(hdr));
+       return hdr->nm_status;
+}
+
+static void netlink_set_status(struct nl_mmap_hdr *hdr,
+                              enum nl_mmap_status status)
+{
+       hdr->nm_status = status;
+       flush_dcache_page(pgvec_to_page(hdr));
+       smp_wmb();
+}
+
+static struct nl_mmap_hdr *
+__netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos)
+{
+       unsigned int pg_vec_pos, frame_off;
+
+       pg_vec_pos = pos / ring->frames_per_block;
+       frame_off  = pos % ring->frames_per_block;
+
+       return ring->pg_vec[pg_vec_pos] + (frame_off * ring->frame_size);
+}
+
+static struct nl_mmap_hdr *
+netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos,
+                    enum nl_mmap_status status)
+{
+       struct nl_mmap_hdr *hdr;
+
+       hdr = __netlink_lookup_frame(ring, pos);
+       if (netlink_get_status(hdr) != status)
+               return NULL;
+
+       return hdr;
+}
+
+static struct nl_mmap_hdr *
+netlink_current_frame(const struct netlink_ring *ring,
+                     enum nl_mmap_status status)
+{
+       return netlink_lookup_frame(ring, ring->head, status);
+}
+
+static struct nl_mmap_hdr *
+netlink_previous_frame(const struct netlink_ring *ring,
+                      enum nl_mmap_status status)
+{
+       unsigned int prev;
+
+       prev = ring->head ? ring->head - 1 : ring->frame_max;
+       return netlink_lookup_frame(ring, prev, status);
+}
+
+static void netlink_increment_head(struct netlink_ring *ring)
+{
+       ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0;
+}
+
+static void netlink_forward_ring(struct netlink_ring *ring)
+{
+       unsigned int head = ring->head, pos = head;
+       const struct nl_mmap_hdr *hdr;
+
+       do {
+               hdr = __netlink_lookup_frame(ring, pos);
+               if (hdr->nm_status == NL_MMAP_STATUS_UNUSED)
+                       break;
+               if (hdr->nm_status != NL_MMAP_STATUS_SKIP)
+                       break;
+               netlink_increment_head(ring);
+       } while (ring->head != head);
+}
+
+static bool netlink_dump_space(struct netlink_sock *nlk)
+{
+       struct netlink_ring *ring = &nlk->rx_ring;
+       struct nl_mmap_hdr *hdr;
+       unsigned int n;
+
+       hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
+       if (hdr == NULL)
+               return false;
+
+       n = ring->head + ring->frame_max / 2;
+       if (n > ring->frame_max)
+               n -= ring->frame_max;
+
+       hdr = __netlink_lookup_frame(ring, n);
+
+       return hdr->nm_status == NL_MMAP_STATUS_UNUSED;
+}
+
+static unsigned int netlink_poll(struct file *file, struct socket *sock,
+                                poll_table *wait)
+{
+       struct sock *sk = sock->sk;
+       struct netlink_sock *nlk = nlk_sk(sk);
+       unsigned int mask;
+       int err;
+
+       if (nlk->rx_ring.pg_vec != NULL) {
+               /* Memory mapped sockets don't call recvmsg(), so flow control
+                * for dumps is performed here. A dump is allowed to continue
+                * if at least half the ring is unused.
+                */
+               while (nlk->cb != NULL && netlink_dump_space(nlk)) {
+                       err = netlink_dump(sk);
+                       if (err < 0) {
+                               sk->sk_err = err;
+                               sk->sk_error_report(sk);
+                               break;
+                       }
+               }
+               netlink_rcv_wake(sk);
+       }
+
+       mask = datagram_poll(file, sock, wait);
+
+       spin_lock_bh(&sk->sk_receive_queue.lock);
+       if (nlk->rx_ring.pg_vec) {
+               netlink_forward_ring(&nlk->rx_ring);
+               if (!netlink_previous_frame(&nlk->rx_ring, NL_MMAP_STATUS_UNUSED))
+                       mask |= POLLIN | POLLRDNORM;
+       }
+       spin_unlock_bh(&sk->sk_receive_queue.lock);
+
+       spin_lock_bh(&sk->sk_write_queue.lock);
+       if (nlk->tx_ring.pg_vec) {
+               if (netlink_current_frame(&nlk->tx_ring, NL_MMAP_STATUS_UNUSED))
+                       mask |= POLLOUT | POLLWRNORM;
+       }
+       spin_unlock_bh(&sk->sk_write_queue.lock);
+
+       return mask;
+}
+
+static struct nl_mmap_hdr *netlink_mmap_hdr(struct sk_buff *skb)
+{
+       return (struct nl_mmap_hdr *)(skb->head - NL_MMAP_HDRLEN);
+}
+
+static void netlink_ring_setup_skb(struct sk_buff *skb, struct sock *sk,
+                                  struct netlink_ring *ring,
+                                  struct nl_mmap_hdr *hdr)
+{
+       unsigned int size;
+       void *data;
+
+       size = ring->frame_size - NL_MMAP_HDRLEN;
+       data = (void *)hdr + NL_MMAP_HDRLEN;
+
+       skb->head       = data;
+       skb->data       = data;
+       skb_reset_tail_pointer(skb);
+       skb->end        = skb->tail + size;
+       skb->len        = 0;
+
+       skb->destructor = netlink_skb_destructor;
+       NETLINK_CB(skb).flags |= NETLINK_SKB_MMAPED;
+       NETLINK_CB(skb).sk = sk;
+}
+
+static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg,
+                               u32 dst_portid, u32 dst_group,
+                               struct sock_iocb *siocb)
+{
+       struct netlink_sock *nlk = nlk_sk(sk);
+       struct netlink_ring *ring;
+       struct nl_mmap_hdr *hdr;
+       struct sk_buff *skb;
+       unsigned int maxlen;
+       bool excl = true;
+       int err = 0, len = 0;
+
+       /* Netlink messages are validated by the receiver before processing.
+        * In order to avoid userspace changing the contents of the message
+        * after validation, the socket and the ring may only be used by a
+        * single process, otherwise we fall back to copying.
+        */
+       if (atomic_long_read(&sk->sk_socket->file->f_count) > 2 ||
+           atomic_read(&nlk->mapped) > 1)
+               excl = false;
+
+       mutex_lock(&nlk->pg_vec_lock);
+
+       ring   = &nlk->tx_ring;
+       maxlen = ring->frame_size - NL_MMAP_HDRLEN;
+
+       do {
+               hdr = netlink_current_frame(ring, NL_MMAP_STATUS_VALID);
+               if (hdr == NULL) {
+                       if (!(msg->msg_flags & MSG_DONTWAIT) &&
+                           atomic_read(&nlk->tx_ring.pending))
+                               schedule();
+                       continue;
+               }
+               if (hdr->nm_len > maxlen) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               netlink_frame_flush_dcache(hdr);
+
+               if (likely(dst_portid == 0 && dst_group == 0 && excl)) {
+                       skb = alloc_skb_head(GFP_KERNEL);
+                       if (skb == NULL) {
+                               err = -ENOBUFS;
+                               goto out;
+                       }
+                       sock_hold(sk);
+                       netlink_ring_setup_skb(skb, sk, ring, hdr);
+                       NETLINK_CB(skb).flags |= NETLINK_SKB_TX;
+                       __skb_put(skb, hdr->nm_len);
+                       netlink_set_status(hdr, NL_MMAP_STATUS_RESERVED);
+                       atomic_inc(&ring->pending);
+               } else {
+                       skb = alloc_skb(hdr->nm_len, GFP_KERNEL);
+                       if (skb == NULL) {
+                               err = -ENOBUFS;
+                               goto out;
+                       }
+                       __skb_put(skb, hdr->nm_len);
+                       memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, hdr->nm_len);
+                       netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED);
+               }
+
+               netlink_increment_head(ring);
+
+               NETLINK_CB(skb).portid    = nlk->portid;
+               NETLINK_CB(skb).dst_group = dst_group;
+               NETLINK_CB(skb).creds     = siocb->scm->creds;
+
+               err = security_netlink_send(sk, skb);
+               if (err) {
+                       kfree_skb(skb);
+                       goto out;
+               }
+
+               if (unlikely(dst_group)) {
+                       atomic_inc(&skb->users);
+                       netlink_broadcast(sk, skb, dst_portid, dst_group,
+                                         GFP_KERNEL);
+               }
+               err = netlink_unicast(sk, skb, dst_portid,
+                                     msg->msg_flags & MSG_DONTWAIT);
+               if (err < 0)
+                       goto out;
+               len += err;
+
+       } while (hdr != NULL ||
+                (!(msg->msg_flags & MSG_DONTWAIT) &&
+                 atomic_read(&nlk->tx_ring.pending)));
+
+       if (len > 0)
+               err = len;
+out:
+       mutex_unlock(&nlk->pg_vec_lock);
+       return err;
+}
+
+static void netlink_queue_mmaped_skb(struct sock *sk, struct sk_buff *skb)
+{
+       struct nl_mmap_hdr *hdr;
+
+       hdr = netlink_mmap_hdr(skb);
+       hdr->nm_len     = skb->len;
+       hdr->nm_group   = NETLINK_CB(skb).dst_group;
+       hdr->nm_pid     = NETLINK_CB(skb).creds.pid;
+       hdr->nm_uid     = NETLINK_CB(skb).creds.uid;
+       hdr->nm_gid     = NETLINK_CB(skb).creds.gid;
+       netlink_frame_flush_dcache(hdr);
+       netlink_set_status(hdr, NL_MMAP_STATUS_VALID);
+
+       NETLINK_CB(skb).flags |= NETLINK_SKB_DELIVERED;
+       kfree_skb(skb);
+}
+
+static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb)
+{
+       struct netlink_sock *nlk = nlk_sk(sk);
+       struct netlink_ring *ring = &nlk->rx_ring;
+       struct nl_mmap_hdr *hdr;
+
+       spin_lock_bh(&sk->sk_receive_queue.lock);
+       hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
+       if (hdr == NULL) {
+               spin_unlock_bh(&sk->sk_receive_queue.lock);
+               kfree_skb(skb);
+               netlink_overrun(sk);
+               return;
+       }
+       netlink_increment_head(ring);
+       __skb_queue_tail(&sk->sk_receive_queue, skb);
+       spin_unlock_bh(&sk->sk_receive_queue.lock);
+
+       hdr->nm_len     = skb->len;
+       hdr->nm_group   = NETLINK_CB(skb).dst_group;
+       hdr->nm_pid     = NETLINK_CB(skb).creds.pid;
+       hdr->nm_uid     = NETLINK_CB(skb).creds.uid;
+       hdr->nm_gid     = NETLINK_CB(skb).creds.gid;
+       netlink_set_status(hdr, NL_MMAP_STATUS_COPY);
+}
+
+#else /* CONFIG_NETLINK_MMAP */
+#define netlink_skb_is_mmaped(skb)     false
+#define netlink_rx_is_mmaped(sk)       false
+#define netlink_tx_is_mmaped(sk)       false
+#define netlink_mmap                   sock_no_mmap
+#define netlink_poll                   datagram_poll
+#define netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, siocb)    0
+#endif /* CONFIG_NETLINK_MMAP */
+
 static void netlink_destroy_callback(struct netlink_callback *cb)
 {
        kfree_skb(cb->skb);
@@ -164,6 +716,53 @@ static void netlink_consume_callback(struct netlink_callback *cb)
        kfree(cb);
 }
 
+static void netlink_skb_destructor(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETLINK_MMAP
+       struct nl_mmap_hdr *hdr;
+       struct netlink_ring *ring;
+       struct sock *sk;
+
+       /* If a packet from the kernel to userspace was freed because of an
+        * error without being delivered to userspace, the kernel must reset
+        * the status. In the direction userspace to kernel, the status is
+        * always reset here after the packet was processed and freed.
+        */
+       if (netlink_skb_is_mmaped(skb)) {
+               hdr = netlink_mmap_hdr(skb);
+               sk = NETLINK_CB(skb).sk;
+
+               if (NETLINK_CB(skb).flags & NETLINK_SKB_TX) {
+                       netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED);
+                       ring = &nlk_sk(sk)->tx_ring;
+               } else {
+                       if (!(NETLINK_CB(skb).flags & NETLINK_SKB_DELIVERED)) {
+                               hdr->nm_len = 0;
+                               netlink_set_status(hdr, NL_MMAP_STATUS_VALID);
+                       }
+                       ring = &nlk_sk(sk)->rx_ring;
+               }
+
+               WARN_ON(atomic_read(&ring->pending) == 0);
+               atomic_dec(&ring->pending);
+               sock_put(sk);
+
+               skb->data = NULL;
+       }
+#endif
+       if (skb->sk != NULL)
+               sock_rfree(skb);
+}
+
+static void netlink_skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
+{
+       WARN_ON(skb->sk != NULL);
+       skb->sk = sk;
+       skb->destructor = netlink_skb_destructor;
+       atomic_add(skb->truesize, &sk->sk_rmem_alloc);
+       sk_mem_charge(sk, skb->truesize);
+}
+
 static void netlink_sock_destruct(struct sock *sk)
 {
        struct netlink_sock *nlk = nlk_sk(sk);
@@ -177,6 +776,18 @@ static void netlink_sock_destruct(struct sock *sk)
        }
 
        skb_queue_purge(&sk->sk_receive_queue);
+#ifdef CONFIG_NETLINK_MMAP
+       if (1) {
+               struct nl_mmap_req req;
+
+               memset(&req, 0, sizeof(req));
+               if (nlk->rx_ring.pg_vec)
+                       netlink_set_ring(sk, &req, true, false);
+               memset(&req, 0, sizeof(req));
+               if (nlk->tx_ring.pg_vec)
+                       netlink_set_ring(sk, &req, true, true);
+       }
+#endif /* CONFIG_NETLINK_MMAP */
 
        if (!sock_flag(sk, SOCK_DEAD)) {
                printk(KERN_ERR "Freeing alive netlink socket %p\n", sk);
@@ -440,6 +1051,9 @@ static int __netlink_create(struct net *net, struct socket *sock,
                mutex_init(nlk->cb_mutex);
        }
        init_waitqueue_head(&nlk->wait);
+#ifdef CONFIG_NETLINK_MMAP
+       mutex_init(&nlk->pg_vec_lock);
+#endif
 
        sk->sk_destruct = netlink_sock_destruct;
        sk->sk_protocol = protocol;
@@ -771,19 +1385,6 @@ static int netlink_getname(struct socket *sock, struct sockaddr *addr,
        return 0;
 }
 
-static void netlink_overrun(struct sock *sk)
-{
-       struct netlink_sock *nlk = nlk_sk(sk);
-
-       if (!(nlk->flags & NETLINK_RECV_NO_ENOBUFS)) {
-               if (!test_and_set_bit(0, &nlk_sk(sk)->state)) {
-                       sk->sk_err = ENOBUFS;
-                       sk->sk_error_report(sk);
-               }
-       }
-       atomic_inc(&sk->sk_drops);
-}
-
 static struct sock *netlink_getsockbyportid(struct sock *ssk, u32 portid)
 {
        struct sock *sock;
@@ -836,8 +1437,9 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
 
        nlk = nlk_sk(sk);
 
-       if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
-           test_bit(0, &nlk->state)) {
+       if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
+            test_bit(NETLINK_CONGESTED, &nlk->state)) &&
+           !netlink_skb_is_mmaped(skb)) {
                DECLARE_WAITQUEUE(wait, current);
                if (!*timeo) {
                        if (!ssk || netlink_is_kernel(ssk))
@@ -851,7 +1453,7 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
                add_wait_queue(&nlk->wait, &wait);
 
                if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
-                    test_bit(0, &nlk->state)) &&
+                    test_bit(NETLINK_CONGESTED, &nlk->state)) &&
                    !sock_flag(sk, SOCK_DEAD))
                        *timeo = schedule_timeout(*timeo);
 
@@ -865,7 +1467,7 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
                }
                return 1;
        }
-       skb_set_owner_r(skb, sk);
+       netlink_skb_set_owner_r(skb, sk);
        return 0;
 }
 
@@ -873,7 +1475,14 @@ static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb)
 {
        int len = skb->len;
 
-       skb_queue_tail(&sk->sk_receive_queue, skb);
+#ifdef CONFIG_NETLINK_MMAP
+       if (netlink_skb_is_mmaped(skb))
+               netlink_queue_mmaped_skb(sk, skb);
+       else if (netlink_rx_is_mmaped(sk))
+               netlink_ring_set_copied(sk, skb);
+       else
+#endif /* CONFIG_NETLINK_MMAP */
+               skb_queue_tail(&sk->sk_receive_queue, skb);
        sk->sk_data_ready(sk, len);
        return len;
 }
@@ -896,7 +1505,9 @@ static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation)
 {
        int delta;
 
-       skb_orphan(skb);
+       WARN_ON(skb->sk != NULL);
+       if (netlink_skb_is_mmaped(skb))
+               return skb;
 
        delta = skb->end - skb->tail;
        if (delta * 2 < skb->truesize)
@@ -916,16 +1527,6 @@ static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation)
        return skb;
 }
 
-static void netlink_rcv_wake(struct sock *sk)
-{
-       struct netlink_sock *nlk = nlk_sk(sk);
-
-       if (skb_queue_empty(&sk->sk_receive_queue))
-               clear_bit(0, &nlk->state);
-       if (!test_bit(0, &nlk->state))
-               wake_up_interruptible(&nlk->wait);
-}
-
 static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb,
                                  struct sock *ssk)
 {
@@ -935,8 +1536,8 @@ static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb,
        ret = -ECONNREFUSED;
        if (nlk->netlink_rcv != NULL) {
                ret = skb->len;
-               skb_set_owner_r(skb, sk);
-               NETLINK_CB(skb).ssk = ssk;
+               netlink_skb_set_owner_r(skb, sk);
+               NETLINK_CB(skb).sk = ssk;
                nlk->netlink_rcv(skb);
                consume_skb(skb);
        } else {
@@ -982,6 +1583,69 @@ retry:
 }
 EXPORT_SYMBOL(netlink_unicast);
 
+struct sk_buff *netlink_alloc_skb(struct sock *ssk, unsigned int size,
+                                 u32 dst_portid, gfp_t gfp_mask)
+{
+#ifdef CONFIG_NETLINK_MMAP
+       struct sock *sk = NULL;
+       struct sk_buff *skb;
+       struct netlink_ring *ring;
+       struct nl_mmap_hdr *hdr;
+       unsigned int maxlen;
+
+       sk = netlink_getsockbyportid(ssk, dst_portid);
+       if (IS_ERR(sk))
+               goto out;
+
+       ring = &nlk_sk(sk)->rx_ring;
+       /* fast-path without atomic ops for common case: non-mmaped receiver */
+       if (ring->pg_vec == NULL)
+               goto out_put;
+
+       skb = alloc_skb_head(gfp_mask);
+       if (skb == NULL)
+               goto err1;
+
+       spin_lock_bh(&sk->sk_receive_queue.lock);
+       /* check again under lock */
+       if (ring->pg_vec == NULL)
+               goto out_free;
+
+       maxlen = ring->frame_size - NL_MMAP_HDRLEN;
+       if (maxlen < size)
+               goto out_free;
+
+       netlink_forward_ring(ring);
+       hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
+       if (hdr == NULL)
+               goto err2;
+       netlink_ring_setup_skb(skb, sk, ring, hdr);
+       netlink_set_status(hdr, NL_MMAP_STATUS_RESERVED);
+       atomic_inc(&ring->pending);
+       netlink_increment_head(ring);
+
+       spin_unlock_bh(&sk->sk_receive_queue.lock);
+       return skb;
+
+err2:
+       kfree_skb(skb);
+       spin_unlock_bh(&sk->sk_receive_queue.lock);
+       netlink_overrun(sk);
+err1:
+       sock_put(sk);
+       return NULL;
+
+out_free:
+       kfree_skb(skb);
+       spin_unlock_bh(&sk->sk_receive_queue.lock);
+out_put:
+       sock_put(sk);
+out:
+#endif
+       return alloc_skb(size, gfp_mask);
+}
+EXPORT_SYMBOL_GPL(netlink_alloc_skb);
+
 int netlink_has_listeners(struct sock *sk, unsigned int group)
 {
        int res = 0;
@@ -1006,8 +1670,8 @@ static int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb)
        struct netlink_sock *nlk = nlk_sk(sk);
 
        if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&
-           !test_bit(0, &nlk->state)) {
-               skb_set_owner_r(skb, sk);
+           !test_bit(NETLINK_CONGESTED, &nlk->state)) {
+               netlink_skb_set_owner_r(skb, sk);
                __netlink_sendskb(sk, skb);
                return atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1);
        }
@@ -1242,7 +1906,8 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
        if (level != SOL_NETLINK)
                return -ENOPROTOOPT;
 
-       if (optlen >= sizeof(int) &&
+       if (optname != NETLINK_RX_RING && optname != NETLINK_TX_RING &&
+           optlen >= sizeof(int) &&
            get_user(val, (unsigned int __user *)optval))
                return -EFAULT;
 
@@ -1284,13 +1949,32 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
        case NETLINK_NO_ENOBUFS:
                if (val) {
                        nlk->flags |= NETLINK_RECV_NO_ENOBUFS;
-                       clear_bit(0, &nlk->state);
+                       clear_bit(NETLINK_CONGESTED, &nlk->state);
                        wake_up_interruptible(&nlk->wait);
                } else {
                        nlk->flags &= ~NETLINK_RECV_NO_ENOBUFS;
                }
                err = 0;
                break;
+#ifdef CONFIG_NETLINK_MMAP
+       case NETLINK_RX_RING:
+       case NETLINK_TX_RING: {
+               struct nl_mmap_req req;
+
+               /* Rings might consume more memory than queue limits, require
+                * CAP_NET_ADMIN.
+                */
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               if (optlen < sizeof(req))
+                       return -EINVAL;
+               if (copy_from_user(&req, optval, sizeof(req)))
+                       return -EFAULT;
+               err = netlink_set_ring(sk, &req, false,
+                                      optname == NETLINK_TX_RING);
+               break;
+       }
+#endif /* CONFIG_NETLINK_MMAP */
        default:
                err = -ENOPROTOOPT;
        }
@@ -1401,6 +2085,13 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
                        goto out;
        }
 
+       if (netlink_tx_is_mmaped(sk) &&
+           msg->msg_iov->iov_base == NULL) {
+               err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group,
+                                          siocb);
+               goto out;
+       }
+
        err = -EMSGSIZE;
        if (len > sk->sk_sndbuf - 32)
                goto out;
@@ -1695,7 +2386,7 @@ struct nlmsghdr *
 __nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int flags)
 {
        struct nlmsghdr *nlh;
-       int size = NLMSG_LENGTH(len);
+       int size = nlmsg_msg_size(len);
 
        nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
        nlh->nlmsg_type = type;
@@ -1704,7 +2395,7 @@ __nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int fla
        nlh->nlmsg_pid = portid;
        nlh->nlmsg_seq = seq;
        if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0)
-               memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size);
+               memset(nlmsg_data(nlh) + len, 0, NLMSG_ALIGN(size) - size);
        return nlh;
 }
 EXPORT_SYMBOL(__nlmsg_put);
@@ -1733,9 +2424,13 @@ static int netlink_dump(struct sock *sk)
 
        alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE);
 
-       skb = sock_rmalloc(sk, alloc_size, 0, GFP_KERNEL);
+       if (!netlink_rx_is_mmaped(sk) &&
+           atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
+               goto errout_skb;
+       skb = netlink_alloc_skb(sk, alloc_size, nlk->portid, GFP_KERNEL);
        if (!skb)
                goto errout_skb;
+       netlink_skb_set_owner_r(skb, sk);
 
        len = cb->dump(skb, cb);
 
@@ -1790,6 +2485,19 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        if (cb == NULL)
                return -ENOBUFS;
 
+       /* Memory mapped dump requests need to be copied to avoid looping
+        * on the pending state in netlink_mmap_sendmsg() while the CB hold
+        * a reference to the skb.
+        */
+       if (netlink_skb_is_mmaped(skb)) {
+               skb = skb_copy(skb, GFP_KERNEL);
+               if (skb == NULL) {
+                       kfree(cb);
+                       return -ENOBUFS;
+               }
+       } else
+               atomic_inc(&skb->users);
+
        cb->dump = control->dump;
        cb->done = control->done;
        cb->nlh = nlh;
@@ -1850,7 +2558,8 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
        if (err)
                payload += nlmsg_len(nlh);
 
-       skb = nlmsg_new(payload, GFP_KERNEL);
+       skb = netlink_alloc_skb(in_skb->sk, nlmsg_total_size(payload),
+                               NETLINK_CB(in_skb).portid, GFP_KERNEL);
        if (!skb) {
                struct sock *sk;
 
@@ -2116,7 +2825,7 @@ static const struct proto_ops netlink_ops = {
        .socketpair =   sock_no_socketpair,
        .accept =       sock_no_accept,
        .getname =      netlink_getname,
-       .poll =         datagram_poll,
+       .poll =         netlink_poll,
        .ioctl =        sock_no_ioctl,
        .listen =       sock_no_listen,
        .shutdown =     sock_no_shutdown,
@@ -2124,7 +2833,7 @@ static const struct proto_ops netlink_ops = {
        .getsockopt =   netlink_getsockopt,
        .sendmsg =      netlink_sendmsg,
        .recvmsg =      netlink_recvmsg,
-       .mmap =         sock_no_mmap,
+       .mmap =         netlink_mmap,
        .sendpage =     sock_no_sendpage,
 };
 
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
new file mode 100644 (file)
index 0000000..ed85222
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef _AF_NETLINK_H
+#define _AF_NETLINK_H
+
+#include <net/sock.h>
+
+#define NLGRPSZ(x)     (ALIGN(x, sizeof(unsigned long) * 8) / 8)
+#define NLGRPLONGS(x)  (NLGRPSZ(x)/sizeof(unsigned long))
+
+struct netlink_ring {
+       void                    **pg_vec;
+       unsigned int            head;
+       unsigned int            frames_per_block;
+       unsigned int            frame_size;
+       unsigned int            frame_max;
+
+       unsigned int            pg_vec_order;
+       unsigned int            pg_vec_pages;
+       unsigned int            pg_vec_len;
+
+       atomic_t                pending;
+};
+
+struct netlink_sock {
+       /* struct sock has to be the first member of netlink_sock */
+       struct sock             sk;
+       u32                     portid;
+       u32                     dst_portid;
+       u32                     dst_group;
+       u32                     flags;
+       u32                     subscriptions;
+       u32                     ngroups;
+       unsigned long           *groups;
+       unsigned long           state;
+       wait_queue_head_t       wait;
+       struct netlink_callback *cb;
+       struct mutex            *cb_mutex;
+       struct mutex            cb_def_mutex;
+       void                    (*netlink_rcv)(struct sk_buff *skb);
+       void                    (*netlink_bind)(int group);
+       struct module           *module;
+#ifdef CONFIG_NETLINK_MMAP
+       struct mutex            pg_vec_lock;
+       struct netlink_ring     rx_ring;
+       struct netlink_ring     tx_ring;
+       atomic_t                mapped;
+#endif /* CONFIG_NETLINK_MMAP */
+};
+
+static inline struct netlink_sock *nlk_sk(struct sock *sk)
+{
+       return container_of(sk, struct netlink_sock, sk);
+}
+
+struct nl_portid_hash {
+       struct hlist_head       *table;
+       unsigned long           rehash_time;
+
+       unsigned int            mask;
+       unsigned int            shift;
+
+       unsigned int            entries;
+       unsigned int            max_shift;
+
+       u32                     rnd;
+};
+
+struct netlink_table {
+       struct nl_portid_hash   hash;
+       struct hlist_head       mc_list;
+       struct listeners __rcu  *listeners;
+       unsigned int            flags;
+       unsigned int            groups;
+       struct mutex            *cb_mutex;
+       struct module           *module;
+       void                    (*bind)(int group);
+       int                     registered;
+};
+
+extern struct netlink_table *nl_table;
+extern rwlock_t nl_table_lock;
+
+#endif
diff --git a/net/netlink/diag.c b/net/netlink/diag.c
new file mode 100644 (file)
index 0000000..4e4aa47
--- /dev/null
@@ -0,0 +1,220 @@
+#include <linux/module.h>
+
+#include <net/sock.h>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
+#include <linux/netlink_diag.h>
+
+#include "af_netlink.h"
+
+static int sk_diag_put_ring(struct netlink_ring *ring, int nl_type,
+                           struct sk_buff *nlskb)
+{
+       struct netlink_diag_ring ndr;
+
+       ndr.ndr_block_size = ring->pg_vec_pages << PAGE_SHIFT;
+       ndr.ndr_block_nr   = ring->pg_vec_len;
+       ndr.ndr_frame_size = ring->frame_size;
+       ndr.ndr_frame_nr   = ring->frame_max + 1;
+
+       return nla_put(nlskb, nl_type, sizeof(ndr), &ndr);
+}
+
+static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb)
+{
+       struct netlink_sock *nlk = nlk_sk(sk);
+       int ret;
+
+       mutex_lock(&nlk->pg_vec_lock);
+       ret = sk_diag_put_ring(&nlk->rx_ring, NETLINK_DIAG_RX_RING, nlskb);
+       if (!ret)
+               ret = sk_diag_put_ring(&nlk->tx_ring, NETLINK_DIAG_TX_RING,
+                                      nlskb);
+       mutex_unlock(&nlk->pg_vec_lock);
+
+       return ret;
+}
+
+static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb)
+{
+       struct netlink_sock *nlk = nlk_sk(sk);
+
+       if (nlk->groups == NULL)
+               return 0;
+
+       return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(nlk->ngroups),
+                      nlk->groups);
+}
+
+static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
+                       struct netlink_diag_req *req,
+                       u32 portid, u32 seq, u32 flags, int sk_ino)
+{
+       struct nlmsghdr *nlh;
+       struct netlink_diag_msg *rep;
+       struct netlink_sock *nlk = nlk_sk(sk);
+
+       nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep),
+                       flags);
+       if (!nlh)
+               return -EMSGSIZE;
+
+       rep = nlmsg_data(nlh);
+       rep->ndiag_family       = AF_NETLINK;
+       rep->ndiag_type         = sk->sk_type;
+       rep->ndiag_protocol     = sk->sk_protocol;
+       rep->ndiag_state        = sk->sk_state;
+
+       rep->ndiag_ino          = sk_ino;
+       rep->ndiag_portid       = nlk->portid;
+       rep->ndiag_dst_portid   = nlk->dst_portid;
+       rep->ndiag_dst_group    = nlk->dst_group;
+       sock_diag_save_cookie(sk, rep->ndiag_cookie);
+
+       if ((req->ndiag_show & NDIAG_SHOW_GROUPS) &&
+           sk_diag_dump_groups(sk, skb))
+               goto out_nlmsg_trim;
+
+       if ((req->ndiag_show & NDIAG_SHOW_MEMINFO) &&
+           sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO))
+               goto out_nlmsg_trim;
+
+       if ((req->ndiag_show & NDIAG_SHOW_RING_CFG) &&
+           sk_diag_put_rings_cfg(sk, skb))
+               goto out_nlmsg_trim;
+
+       return nlmsg_end(skb, nlh);
+
+out_nlmsg_trim:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
+}
+
+static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
+                               int protocol, int s_num)
+{
+       struct netlink_table *tbl = &nl_table[protocol];
+       struct nl_portid_hash *hash = &tbl->hash;
+       struct net *net = sock_net(skb->sk);
+       struct netlink_diag_req *req;
+       struct sock *sk;
+       int ret = 0, num = 0, i;
+
+       req = nlmsg_data(cb->nlh);
+
+       for (i = 0; i <= hash->mask; i++) {
+               sk_for_each(sk, &hash->table[i]) {
+                       if (!net_eq(sock_net(sk), net))
+                               continue;
+                       if (num < s_num) {
+                               num++;
+                               continue;
+                       }
+
+                       if (sk_diag_fill(sk, skb, req,
+                                        NETLINK_CB(cb->skb).portid,
+                                        cb->nlh->nlmsg_seq,
+                                        NLM_F_MULTI,
+                                        sock_i_ino(sk)) < 0) {
+                               ret = 1;
+                               goto done;
+                       }
+
+                       num++;
+               }
+       }
+
+       sk_for_each_bound(sk, &tbl->mc_list) {
+               if (sk_hashed(sk))
+                       continue;
+               if (!net_eq(sock_net(sk), net))
+                       continue;
+               if (num < s_num) {
+                       num++;
+                       continue;
+               }
+
+               if (sk_diag_fill(sk, skb, req,
+                                NETLINK_CB(cb->skb).portid,
+                                cb->nlh->nlmsg_seq,
+                                NLM_F_MULTI,
+                                sock_i_ino(sk)) < 0) {
+                       ret = 1;
+                       goto done;
+               }
+               num++;
+       }
+done:
+       cb->args[0] = num;
+       cb->args[1] = protocol;
+
+       return ret;
+}
+
+static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct netlink_diag_req *req;
+       int s_num = cb->args[0];
+
+       req = nlmsg_data(cb->nlh);
+
+       read_lock(&nl_table_lock);
+
+       if (req->sdiag_protocol == NDIAG_PROTO_ALL) {
+               int i;
+
+               for (i = cb->args[1]; i < MAX_LINKS; i++) {
+                       if (__netlink_diag_dump(skb, cb, i, s_num))
+                               break;
+                       s_num = 0;
+               }
+       } else {
+               if (req->sdiag_protocol >= MAX_LINKS) {
+                       read_unlock(&nl_table_lock);
+                       return -ENOENT;
+               }
+
+               __netlink_diag_dump(skb, cb, req->sdiag_protocol, s_num);
+       }
+
+       read_unlock(&nl_table_lock);
+
+       return skb->len;
+}
+
+static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
+{
+       int hdrlen = sizeof(struct netlink_diag_req);
+       struct net *net = sock_net(skb->sk);
+
+       if (nlmsg_len(h) < hdrlen)
+               return -EINVAL;
+
+       if (h->nlmsg_flags & NLM_F_DUMP) {
+               struct netlink_dump_control c = {
+                       .dump = netlink_diag_dump,
+               };
+               return netlink_dump_start(net->diag_nlsk, skb, h, &c);
+       } else
+               return -EOPNOTSUPP;
+}
+
+static const struct sock_diag_handler netlink_diag_handler = {
+       .family = AF_NETLINK,
+       .dump = netlink_diag_handler_dump,
+};
+
+static int __init netlink_diag_init(void)
+{
+       return sock_diag_register(&netlink_diag_handler);
+}
+
+static void __exit netlink_diag_exit(void)
+{
+       sock_diag_unregister(&netlink_diag_handler);
+}
+
+module_init(netlink_diag_init);
+module_exit(netlink_diag_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 16 /* AF_NETLINK */);
index c6bc3bd950526ee2de42426337e74962e6181deb..b75a9b3f9e8933ab0a2def3c2fde7248c14ed967 100644 (file)
@@ -117,6 +117,88 @@ u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length)
        return tlv;
 }
 
+struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap)
+{
+       struct nfc_llcp_sdp_tlv *sdres;
+       u8 value[2];
+
+       sdres = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
+       if (sdres == NULL)
+               return NULL;
+
+       value[0] = tid;
+       value[1] = sap;
+
+       sdres->tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, value, 2,
+                                       &sdres->tlv_len);
+       if (sdres->tlv == NULL) {
+               kfree(sdres);
+               return NULL;
+       }
+
+       sdres->tid = tid;
+       sdres->sap = sap;
+
+       INIT_HLIST_NODE(&sdres->node);
+
+       return sdres;
+}
+
+struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
+                                                 size_t uri_len)
+{
+       struct nfc_llcp_sdp_tlv *sdreq;
+
+       pr_debug("uri: %s, len: %zu\n", uri, uri_len);
+
+       sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
+       if (sdreq == NULL)
+               return NULL;
+
+       sdreq->tlv_len = uri_len + 3;
+
+       if (uri[uri_len - 1] == 0)
+               sdreq->tlv_len--;
+
+       sdreq->tlv = kzalloc(sdreq->tlv_len + 1, GFP_KERNEL);
+       if (sdreq->tlv == NULL) {
+               kfree(sdreq);
+               return NULL;
+       }
+
+       sdreq->tlv[0] = LLCP_TLV_SDREQ;
+       sdreq->tlv[1] = sdreq->tlv_len - 2;
+       sdreq->tlv[2] = tid;
+
+       sdreq->tid = tid;
+       sdreq->uri = sdreq->tlv + 3;
+       memcpy(sdreq->uri, uri, uri_len);
+
+       sdreq->time = jiffies;
+
+       INIT_HLIST_NODE(&sdreq->node);
+
+       return sdreq;
+}
+
+void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp)
+{
+       kfree(sdp->tlv);
+       kfree(sdp);
+}
+
+void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head)
+{
+       struct nfc_llcp_sdp_tlv *sdp;
+       struct hlist_node *n;
+
+       hlist_for_each_entry_safe(sdp, n, head, node) {
+               hlist_del(&sdp->node);
+
+               nfc_llcp_free_sdp_tlv(sdp);
+       }
+}
+
 int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
                          u8 *tlv_array, u16 tlv_array_len)
 {
@@ -184,10 +266,10 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
 
                switch (type) {
                case LLCP_TLV_MIUX:
-                       sock->miu = llcp_tlv_miux(tlv) + 128;
+                       sock->remote_miu = llcp_tlv_miux(tlv) + 128;
                        break;
                case LLCP_TLV_RW:
-                       sock->rw = llcp_tlv_rw(tlv);
+                       sock->remote_rw = llcp_tlv_rw(tlv);
                        break;
                case LLCP_TLV_SN:
                        break;
@@ -200,7 +282,8 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
                tlv += length + 2;
        }
 
-       pr_debug("sock %p rw %d miu %d\n", sock, sock->rw, sock->miu);
+       pr_debug("sock %p rw %d miu %d\n", sock,
+                sock->remote_rw, sock->remote_miu);
 
        return 0;
 }
@@ -318,9 +401,9 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
        struct sk_buff *skb;
        u8 *service_name_tlv = NULL, service_name_tlv_length;
        u8 *miux_tlv = NULL, miux_tlv_length;
-       u8 *rw_tlv = NULL, rw_tlv_length;
+       u8 *rw_tlv = NULL, rw_tlv_length, rw;
        int err;
-       u16 size = 0;
+       u16 size = 0, miux;
 
        pr_debug("Sending CONNECT\n");
 
@@ -336,11 +419,15 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
                size += service_name_tlv_length;
        }
 
-       miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
+       /* If the socket parameters are not set, use the local ones */
+       miux = sock->miux > LLCP_MAX_MIUX ? local->miux : sock->miux;
+       rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw;
+
+       miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
                                      &miux_tlv_length);
        size += miux_tlv_length;
 
-       rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length);
+       rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
        size += rw_tlv_length;
 
        pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len);
@@ -377,9 +464,9 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
        struct nfc_llcp_local *local;
        struct sk_buff *skb;
        u8 *miux_tlv = NULL, miux_tlv_length;
-       u8 *rw_tlv = NULL, rw_tlv_length;
+       u8 *rw_tlv = NULL, rw_tlv_length, rw;
        int err;
-       u16 size = 0;
+       u16 size = 0, miux;
 
        pr_debug("Sending CC\n");
 
@@ -387,11 +474,15 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
        if (local == NULL)
                return -ENODEV;
 
-       miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
+       /* If the socket parameters are not set, use the local ones */
+       miux = sock->miux > LLCP_MAX_MIUX ? local->miux : sock->miux;
+       rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw;
+
+       miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
                                      &miux_tlv_length);
        size += miux_tlv_length;
 
-       rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length);
+       rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
        size += rw_tlv_length;
 
        skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size);
@@ -416,48 +507,90 @@ error_tlv:
        return err;
 }
 
-int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap)
+static struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local,
+                                            size_t tlv_length)
 {
        struct sk_buff *skb;
        struct nfc_dev *dev;
-       u8 *sdres_tlv = NULL, sdres_tlv_length, sdres[2];
        u16 size = 0;
 
-       pr_debug("Sending SNL tid 0x%x sap 0x%x\n", tid, sap);
-
        if (local == NULL)
-               return -ENODEV;
+               return ERR_PTR(-ENODEV);
 
        dev = local->dev;
        if (dev == NULL)
-               return -ENODEV;
-
-       sdres[0] = tid;
-       sdres[1] = sap;
-       sdres_tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, sdres, 0,
-                                      &sdres_tlv_length);
-       if (sdres_tlv == NULL)
-               return -ENOMEM;
+               return ERR_PTR(-ENODEV);
 
        size += LLCP_HEADER_SIZE;
        size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
-       size += sdres_tlv_length;
+       size += tlv_length;
 
        skb = alloc_skb(size, GFP_KERNEL);
-       if (skb == NULL) {
-               kfree(sdres_tlv);
-               return -ENOMEM;
-       }
+       if (skb == NULL)
+               return ERR_PTR(-ENOMEM);
 
        skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
 
        skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL);
 
-       memcpy(skb_put(skb, sdres_tlv_length), sdres_tlv, sdres_tlv_length);
+       return skb;
+}
+
+int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local,
+                           struct hlist_head *tlv_list, size_t tlvs_len)
+{
+       struct nfc_llcp_sdp_tlv *sdp;
+       struct hlist_node *n;
+       struct sk_buff *skb;
+
+       skb = nfc_llcp_allocate_snl(local, tlvs_len);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       hlist_for_each_entry_safe(sdp, n, tlv_list, node) {
+               memcpy(skb_put(skb, sdp->tlv_len), sdp->tlv, sdp->tlv_len);
+
+               hlist_del(&sdp->node);
+
+               nfc_llcp_free_sdp_tlv(sdp);
+       }
 
        skb_queue_tail(&local->tx_queue, skb);
 
-       kfree(sdres_tlv);
+       return 0;
+}
+
+int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local,
+                           struct hlist_head *tlv_list, size_t tlvs_len)
+{
+       struct nfc_llcp_sdp_tlv *sdreq;
+       struct hlist_node *n;
+       struct sk_buff *skb;
+
+       skb = nfc_llcp_allocate_snl(local, tlvs_len);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       mutex_lock(&local->sdreq_lock);
+
+       if (hlist_empty(&local->pending_sdreqs))
+               mod_timer(&local->sdreq_timer,
+                         jiffies + msecs_to_jiffies(3 * local->remote_lto));
+
+       hlist_for_each_entry_safe(sdreq, n, tlv_list, node) {
+               pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri);
+
+               memcpy(skb_put(skb, sdreq->tlv_len), sdreq->tlv,
+                      sdreq->tlv_len);
+
+               hlist_del(&sdreq->node);
+
+               hlist_add_head(&sdreq->node, &local->pending_sdreqs);
+       }
+
+       mutex_unlock(&local->sdreq_lock);
+
+       skb_queue_tail(&local->tx_queue, skb);
 
        return 0;
 }
@@ -532,8 +665,8 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
        /* Remote is ready but has not acknowledged our frames */
        if((sock->remote_ready &&
-           skb_queue_len(&sock->tx_pending_queue) >= sock->rw &&
-           skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) {
+           skb_queue_len(&sock->tx_pending_queue) >= sock->remote_rw &&
+           skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) {
                pr_err("Pending queue is full %d frames\n",
                       skb_queue_len(&sock->tx_pending_queue));
                return -ENOBUFS;
@@ -541,7 +674,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
        /* Remote is not ready and we've been queueing enough frames */
        if ((!sock->remote_ready &&
-            skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) {
+            skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) {
                pr_err("Tx queue is full %d frames\n",
                       skb_queue_len(&sock->tx_queue));
                return -ENOBUFS;
@@ -561,7 +694,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
        while (remaining_len > 0) {
 
-               frag_len = min_t(size_t, sock->miu, remaining_len);
+               frag_len = min_t(size_t, sock->remote_miu, remaining_len);
 
                pr_debug("Fragment %zd bytes remaining %zd",
                         frag_len, remaining_len);
@@ -621,7 +754,7 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
 
        while (remaining_len > 0) {
 
-               frag_len = min_t(size_t, sock->miu, remaining_len);
+               frag_len = min_t(size_t, sock->remote_miu, remaining_len);
 
                pr_debug("Fragment %zd bytes remaining %zd",
                         frag_len, remaining_len);
index ee25f25f0cd638367cc4a6668c44e907945c9469..7de0368aff0c83b35d05061f6d205e26415c57c3 100644 (file)
@@ -182,6 +182,9 @@ static void local_cleanup(struct nfc_llcp_local *local, bool listen)
        cancel_work_sync(&local->rx_work);
        cancel_work_sync(&local->timeout_work);
        kfree_skb(local->rx_pending);
+       del_timer_sync(&local->sdreq_timer);
+       cancel_work_sync(&local->sdreq_timeout_work);
+       nfc_llcp_free_sdp_tlv_list(&local->pending_sdreqs);
 }
 
 static void local_release(struct kref *ref)
@@ -259,6 +262,47 @@ static void nfc_llcp_symm_timer(unsigned long data)
        schedule_work(&local->timeout_work);
 }
 
+static void nfc_llcp_sdreq_timeout_work(struct work_struct *work)
+{
+       unsigned long time;
+       HLIST_HEAD(nl_sdres_list);
+       struct hlist_node *n;
+       struct nfc_llcp_sdp_tlv *sdp;
+       struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
+                                                   sdreq_timeout_work);
+
+       mutex_lock(&local->sdreq_lock);
+
+       time = jiffies - msecs_to_jiffies(3 * local->remote_lto);
+
+       hlist_for_each_entry_safe(sdp, n, &local->pending_sdreqs, node) {
+               if (time_after(sdp->time, time))
+                       continue;
+
+               sdp->sap = LLCP_SDP_UNBOUND;
+
+               hlist_del(&sdp->node);
+
+               hlist_add_head(&sdp->node, &nl_sdres_list);
+       }
+
+       if (!hlist_empty(&local->pending_sdreqs))
+               mod_timer(&local->sdreq_timer,
+                         jiffies + msecs_to_jiffies(3 * local->remote_lto));
+
+       mutex_unlock(&local->sdreq_lock);
+
+       if (!hlist_empty(&nl_sdres_list))
+               nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
+}
+
+static void nfc_llcp_sdreq_timer(unsigned long data)
+{
+       struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;
+
+       schedule_work(&local->sdreq_timeout_work);
+}
+
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
 {
        struct nfc_llcp_local *local, *n;
@@ -802,8 +846,6 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
        ui_cb->dsap = dsap;
        ui_cb->ssap = ssap;
 
-       printk("%s %d %d\n", __func__, dsap, ssap);
-
        pr_debug("%d %d\n", dsap, ssap);
 
        /* We're looking for a bound socket, not a client one */
@@ -900,7 +942,9 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
        new_sock = nfc_llcp_sock(new_sk);
        new_sock->dev = local->dev;
        new_sock->local = nfc_llcp_local_get(local);
-       new_sock->miu = local->remote_miu;
+       new_sock->rw = sock->rw;
+       new_sock->miux = sock->miux;
+       new_sock->remote_miu = local->remote_miu;
        new_sock->nfc_protocol = sock->nfc_protocol;
        new_sock->dsap = ssap;
        new_sock->target_idx = local->target_idx;
@@ -954,11 +998,11 @@ int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
 
        pr_debug("Remote ready %d tx queue len %d remote rw %d",
                 sock->remote_ready, skb_queue_len(&sock->tx_pending_queue),
-                sock->rw);
+                sock->remote_rw);
 
        /* Try to queue some I frames for transmission */
        while (sock->remote_ready &&
-              skb_queue_len(&sock->tx_pending_queue) < sock->rw) {
+              skb_queue_len(&sock->tx_pending_queue) < sock->remote_rw) {
                struct sk_buff *pdu;
 
                pdu = skb_dequeue(&sock->tx_queue);
@@ -1178,6 +1222,10 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
        u16 tlv_len, offset;
        char *service_name;
        size_t service_name_len;
+       struct nfc_llcp_sdp_tlv *sdp;
+       HLIST_HEAD(llc_sdres_list);
+       size_t sdres_tlvs_len;
+       HLIST_HEAD(nl_sdres_list);
 
        dsap = nfc_llcp_dsap(skb);
        ssap = nfc_llcp_ssap(skb);
@@ -1192,6 +1240,7 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
        tlv = &skb->data[LLCP_HEADER_SIZE];
        tlv_len = skb->len - LLCP_HEADER_SIZE;
        offset = 0;
+       sdres_tlvs_len = 0;
 
        while (offset < tlv_len) {
                type = tlv[0];
@@ -1209,14 +1258,14 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
                            !strncmp(service_name, "urn:nfc:sn:sdp",
                                     service_name_len)) {
                                sap = 1;
-                               goto send_snl;
+                               goto add_snl;
                        }
 
                        llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
                                                          service_name_len);
                        if (!llcp_sock) {
                                sap = 0;
-                               goto send_snl;
+                               goto add_snl;
                        }
 
                        /*
@@ -1233,7 +1282,7 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
 
                                if (sap == LLCP_SAP_MAX) {
                                        sap = 0;
-                                       goto send_snl;
+                                       goto add_snl;
                                }
 
                                client_count =
@@ -1250,8 +1299,37 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
 
                        pr_debug("%p %d\n", llcp_sock, sap);
 
-send_snl:
-                       nfc_llcp_send_snl(local, tid, sap);
+add_snl:
+                       sdp = nfc_llcp_build_sdres_tlv(tid, sap);
+                       if (sdp == NULL)
+                               goto exit;
+
+                       sdres_tlvs_len += sdp->tlv_len;
+                       hlist_add_head(&sdp->node, &llc_sdres_list);
+                       break;
+
+               case LLCP_TLV_SDRES:
+                       mutex_lock(&local->sdreq_lock);
+
+                       pr_debug("LLCP_TLV_SDRES: searching tid %d\n", tlv[2]);
+
+                       hlist_for_each_entry(sdp, &local->pending_sdreqs, node) {
+                               if (sdp->tid != tlv[2])
+                                       continue;
+
+                               sdp->sap = tlv[3];
+
+                               pr_debug("Found: uri=%s, sap=%d\n",
+                                        sdp->uri, sdp->sap);
+
+                               hlist_del(&sdp->node);
+
+                               hlist_add_head(&sdp->node, &nl_sdres_list);
+
+                               break;
+                       }
+
+                       mutex_unlock(&local->sdreq_lock);
                        break;
 
                default:
@@ -1262,6 +1340,13 @@ send_snl:
                offset += length + 2;
                tlv += length + 2;
        }
+
+exit:
+       if (!hlist_empty(&nl_sdres_list))
+               nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
+
+       if (!hlist_empty(&llc_sdres_list))
+               nfc_llcp_send_snl_sdres(local, &llc_sdres_list, sdres_tlvs_len);
 }
 
 static void nfc_llcp_rx_work(struct work_struct *work)
@@ -1447,6 +1532,13 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
        local->remote_miu = LLCP_DEFAULT_MIU;
        local->remote_lto = LLCP_DEFAULT_LTO;
 
+       mutex_init(&local->sdreq_lock);
+       INIT_HLIST_HEAD(&local->pending_sdreqs);
+       init_timer(&local->sdreq_timer);
+       local->sdreq_timer.data = (unsigned long) local;
+       local->sdreq_timer.function = nfc_llcp_sdreq_timer;
+       INIT_WORK(&local->sdreq_timeout_work, nfc_llcp_sdreq_timeout_work);
+
        list_add(&local->list, &llcp_devices);
 
        return 0;
index 0eae5c5095047dcd449af0ddba8fd402f79f50e2..7e87a66b02ec1cda721fee9462d317698c51f7f7 100644 (file)
@@ -46,6 +46,19 @@ struct llcp_sock_list {
        rwlock_t          lock;
 };
 
+struct nfc_llcp_sdp_tlv {
+       u8 *tlv;
+       u8 tlv_len;
+
+       char *uri;
+       u8 tid;
+       u8 sap;
+
+       unsigned long time;
+
+       struct hlist_node node;
+};
+
 struct nfc_llcp_local {
        struct list_head list;
        struct nfc_dev *dev;
@@ -86,6 +99,12 @@ struct nfc_llcp_local {
        u8  remote_opt;
        u16 remote_wks;
 
+       struct mutex sdreq_lock;
+       struct hlist_head pending_sdreqs;
+       struct timer_list sdreq_timer;
+       struct work_struct sdreq_timeout_work;
+       u8 sdreq_next_tid;
+
        /* sockets array */
        struct llcp_sock_list sockets;
        struct llcp_sock_list connecting_sockets;
@@ -105,7 +124,12 @@ struct nfc_llcp_sock {
        char *service_name;
        size_t service_name_len;
        u8 rw;
-       u16 miu;
+       u16 miux;
+
+
+       /* Remote link parameters */
+       u8 remote_rw;
+       u16 remote_miu;
 
        /* Link variables */
        u8 send_n;
@@ -213,12 +237,20 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
 /* Commands API */
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
 u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length);
+struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap);
+struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
+                                                 size_t uri_len);
+void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
+void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head);
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
 int nfc_llcp_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_symm(struct nfc_dev *dev);
 int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
-int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap);
+int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local,
+                           struct hlist_head *tlv_list, size_t tlvs_len);
+int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local,
+                           struct hlist_head *tlv_list, size_t tlvs_len);
 int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason);
 int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
index 6c94447ec414c87479581b45fbcced1071f782cb..c1101e6de170fc531a37c1f796242abfbc8c0f64 100644 (file)
@@ -223,6 +223,124 @@ error:
        return ret;
 }
 
+static int nfc_llcp_setsockopt(struct socket *sock, int level, int optname,
+                              char __user *optval, unsigned int optlen)
+{
+       struct sock *sk = sock->sk;
+       struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+       u32 opt;
+       int err = 0;
+
+       pr_debug("%p optname %d\n", sk, optname);
+
+       if (level != SOL_NFC)
+               return -ENOPROTOOPT;
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case NFC_LLCP_RW:
+               if (sk->sk_state == LLCP_CONNECTED ||
+                   sk->sk_state == LLCP_BOUND ||
+                   sk->sk_state == LLCP_LISTEN) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > LLCP_MAX_RW) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               llcp_sock->rw = (u8) opt;
+
+               break;
+
+       case NFC_LLCP_MIUX:
+               if (sk->sk_state == LLCP_CONNECTED ||
+                   sk->sk_state == LLCP_BOUND ||
+                   sk->sk_state == LLCP_LISTEN) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > LLCP_MAX_MIUX) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               llcp_sock->miux = (u16) opt;
+
+               break;
+
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+
+       release_sock(sk);
+
+       pr_debug("%p rw %d miux %d\n", llcp_sock,
+                llcp_sock->rw, llcp_sock->miux);
+
+       return err;
+}
+
+static int nfc_llcp_getsockopt(struct socket *sock, int level, int optname,
+                              char __user *optval, int __user *optlen)
+{
+       struct sock *sk = sock->sk;
+       struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+       int len, err = 0;
+
+       pr_debug("%p optname %d\n", sk, optname);
+
+       if (level != SOL_NFC)
+               return -ENOPROTOOPT;
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       len = min_t(u32, len, sizeof(u32));
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case NFC_LLCP_RW:
+               if (put_user(llcp_sock->rw, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       case NFC_LLCP_MIUX:
+               if (put_user(llcp_sock->miux, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+
+       release_sock(sk);
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+
+       return err;
+}
+
 void nfc_llcp_accept_unlink(struct sock *sk)
 {
        struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
@@ -405,7 +523,8 @@ static unsigned int llcp_sock_poll(struct file *file, struct socket *sock,
                return llcp_accept_poll(sk);
 
        if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
-               mask |= POLLERR;
+               mask |= POLLERR |
+                       (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 
        if (!skb_queue_empty(&sk->sk_receive_queue))
                mask |= POLLIN | POLLRDNORM;
@@ -543,7 +662,7 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
 
        llcp_sock->dev = dev;
        llcp_sock->local = nfc_llcp_local_get(local);
-       llcp_sock->miu = llcp_sock->local->remote_miu;
+       llcp_sock->remote_miu = llcp_sock->local->remote_miu;
        llcp_sock->ssap = nfc_llcp_get_local_ssap(local);
        if (llcp_sock->ssap == LLCP_SAP_MAX) {
                ret = -ENOMEM;
@@ -740,8 +859,8 @@ static const struct proto_ops llcp_sock_ops = {
        .ioctl          = sock_no_ioctl,
        .listen         = llcp_sock_listen,
        .shutdown       = sock_no_shutdown,
-       .setsockopt     = sock_no_setsockopt,
-       .getsockopt     = sock_no_getsockopt,
+       .setsockopt     = nfc_llcp_setsockopt,
+       .getsockopt     = nfc_llcp_getsockopt,
        .sendmsg        = llcp_sock_sendmsg,
        .recvmsg        = llcp_sock_recvmsg,
        .mmap           = sock_no_mmap,
@@ -805,8 +924,10 @@ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp)
 
        llcp_sock->ssap = 0;
        llcp_sock->dsap = LLCP_SAP_SDP;
-       llcp_sock->rw = LLCP_DEFAULT_RW;
-       llcp_sock->miu = LLCP_DEFAULT_MIU;
+       llcp_sock->rw = LLCP_MAX_RW + 1;
+       llcp_sock->miux = LLCP_MAX_MIUX + 1;
+       llcp_sock->remote_rw = LLCP_DEFAULT_RW;
+       llcp_sock->remote_miu = LLCP_DEFAULT_MIU;
        llcp_sock->send_n = llcp_sock->send_ack_n = 0;
        llcp_sock->recv_n = llcp_sock->recv_ack_n = 0;
        llcp_sock->remote_ready = 1;
index 504b883439f1b535b565c8efdc79e965afe465d4..73fd51098f4dc1250ecc89fe160f56055b635987 100644 (file)
@@ -53,6 +53,15 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
        [NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 },
        [NFC_ATTR_IM_PROTOCOLS] = { .type = NLA_U32 },
        [NFC_ATTR_TM_PROTOCOLS] = { .type = NLA_U32 },
+       [NFC_ATTR_LLC_PARAM_LTO] = { .type = NLA_U8 },
+       [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 },
+       [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 },
+       [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
+       [NFC_SDP_ATTR_URI] = { .type = NLA_STRING },
+       [NFC_SDP_ATTR_SAP] = { .type = NLA_U8 },
 };
 
 static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
@@ -348,6 +357,74 @@ free_msg:
        return -EMSGSIZE;
 }
 
+int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list)
+{
+       struct sk_buff *msg;
+       struct nlattr *sdp_attr, *uri_attr;
+       struct nfc_llcp_sdp_tlv *sdres;
+       struct hlist_node *n;
+       void *hdr;
+       int rc = -EMSGSIZE;
+       int i;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_LLC_SDRES);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
+               goto nla_put_failure;
+
+       sdp_attr = nla_nest_start(msg, NFC_ATTR_LLC_SDP);
+       if (sdp_attr == NULL) {
+               rc = -ENOMEM;
+               goto nla_put_failure;
+       }
+
+       i = 1;
+       hlist_for_each_entry_safe(sdres, n, sdres_list, node) {
+               pr_debug("uri: %s, sap: %d\n", sdres->uri, sdres->sap);
+
+               uri_attr = nla_nest_start(msg, i++);
+               if (uri_attr == NULL) {
+                       rc = -ENOMEM;
+                       goto nla_put_failure;
+               }
+
+               if (nla_put_u8(msg, NFC_SDP_ATTR_SAP, sdres->sap))
+                       goto nla_put_failure;
+
+               if (nla_put_string(msg, NFC_SDP_ATTR_URI, sdres->uri))
+                       goto nla_put_failure;
+
+               nla_nest_end(msg, uri_attr);
+
+               hlist_del(&sdres->node);
+
+               nfc_llcp_free_sdp_tlv(sdres);
+       }
+
+       nla_nest_end(msg, sdp_attr);
+
+       genlmsg_end(msg, hdr);
+
+       return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+
+free_msg:
+       nlmsg_free(msg);
+
+       nfc_llcp_free_sdp_tlv_list(sdres_list);
+
+       return rc;
+}
+
 static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
                                u32 portid, u32 seq,
                                struct netlink_callback *cb,
@@ -859,6 +936,96 @@ exit:
        return rc;
 }
 
+static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       struct nfc_llcp_local *local;
+       struct nlattr *attr, *sdp_attrs[NFC_SDP_ATTR_MAX+1];
+       u32 idx;
+       u8 tid;
+       char *uri;
+       int rc = 0, rem;
+       size_t uri_len, tlvs_len;
+       struct hlist_head sdreq_list;
+       struct nfc_llcp_sdp_tlv *sdreq;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_LLC_SDP])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev) {
+               rc = -ENODEV;
+               goto exit;
+       }
+
+       device_lock(&dev->dev);
+
+       if (dev->dep_link_up == false) {
+               rc = -ENOLINK;
+               goto exit;
+       }
+
+       local = nfc_llcp_find_local(dev);
+       if (!local) {
+               nfc_put_device(dev);
+               rc = -ENODEV;
+               goto exit;
+       }
+
+       INIT_HLIST_HEAD(&sdreq_list);
+
+       tlvs_len = 0;
+
+       nla_for_each_nested(attr, info->attrs[NFC_ATTR_LLC_SDP], rem) {
+               rc = nla_parse_nested(sdp_attrs, NFC_SDP_ATTR_MAX, attr,
+                                     nfc_sdp_genl_policy);
+
+               if (rc != 0) {
+                       rc = -EINVAL;
+                       goto exit;
+               }
+
+               if (!sdp_attrs[NFC_SDP_ATTR_URI])
+                       continue;
+
+               uri_len = nla_len(sdp_attrs[NFC_SDP_ATTR_URI]);
+               if (uri_len == 0)
+                       continue;
+
+               uri = nla_data(sdp_attrs[NFC_SDP_ATTR_URI]);
+               if (uri == NULL || *uri == 0)
+                       continue;
+
+               tid = local->sdreq_next_tid++;
+
+               sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len);
+               if (sdreq == NULL) {
+                       rc = -ENOMEM;
+                       goto exit;
+               }
+
+               tlvs_len += sdreq->tlv_len;
+
+               hlist_add_head(&sdreq->node, &sdreq_list);
+       }
+
+       if (hlist_empty(&sdreq_list)) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = nfc_llcp_send_snl_sdreq(local, &sdreq_list, tlvs_len);
+exit:
+       device_unlock(&dev->dev);
+
+       nfc_put_device(dev);
+
+       return rc;
+}
+
 static struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
@@ -913,6 +1080,11 @@ static struct genl_ops nfc_genl_ops[] = {
                .doit = nfc_genl_llc_set_params,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_LLC_SDREQ,
+               .doit = nfc_genl_llc_sdreq,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index 87d914d2876aeb029483ab88242d9e4fda6ca006..94bfe19ba678186cb7467ae03213f07e638046dd 100644 (file)
@@ -46,6 +46,8 @@ struct nfc_rawsock {
 #define to_rawsock_sk(_tx_work) \
        ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work))
 
+struct nfc_llcp_sdp_tlv;
+
 #ifdef CONFIG_NFC_LLCP
 
 void nfc_llcp_mac_is_down(struct nfc_dev *dev);
@@ -59,6 +61,8 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb);
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
 int __init nfc_llcp_init(void);
 void nfc_llcp_exit(void);
+void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
+void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head);
 
 #else
 
@@ -112,6 +116,14 @@ static inline void nfc_llcp_exit(void)
 {
 }
 
+static inline void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp)
+{
+}
+
+static inline void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head)
+{
+}
+
 #endif
 
 int __init rawsock_init(void);
@@ -144,6 +156,8 @@ int nfc_genl_dep_link_down_event(struct nfc_dev *dev);
 int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol);
 int nfc_genl_tm_deactivated(struct nfc_dev *dev);
 
+int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list);
+
 struct nfc_dev *nfc_get_device(unsigned int idx);
 
 static inline void nfc_put_device(struct nfc_dev *dev)
index d4d5363c7ba7868731cb9188ad9bc5ccd28b8304..894b6cbdd9295841e6782268b8743fcb63358391 100644 (file)
@@ -98,7 +98,7 @@ static int pop_vlan(struct sk_buff *skb)
        if (unlikely(err))
                return err;
 
-       __vlan_hwaccel_put_tag(skb, ntohs(tci));
+       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(tci));
        return 0;
 }
 
@@ -110,7 +110,7 @@ static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vla
                /* push down current VLAN tag */
                current_tag = vlan_tx_tag_get(skb);
 
-               if (!__vlan_put_tag(skb, current_tag))
+               if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
                        return -ENOMEM;
 
                if (skb->ip_summed == CHECKSUM_COMPLETE)
@@ -118,7 +118,7 @@ static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vla
                                        + (2 * ETH_ALEN), VLAN_HLEN, 0));
 
        }
-       __vlan_hwaccel_put_tag(skb, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
+       __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
        return 0;
 }
 
index 6980c3e6f0667b0ed22098098b66da7cf929a47e..d2f9f2e572989c1fe19034b9aac37e094cf2917c 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/netfilter_ipv4.h>
 #include <linux/inetdevice.h>
 #include <linux/list.h>
+#include <linux/lockdep.h>
 #include <linux/openvswitch.h>
 #include <linux/rculist.h>
 #include <linux/dmi.h>
 #include "flow.h"
 #include "vport-internal_dev.h"
 
-/**
- * struct ovs_net - Per net-namespace data for ovs.
- * @dps: List of datapaths to enable dumping them all out.
- * Protected by genl_mutex.
- */
-struct ovs_net {
-       struct list_head dps;
-};
-
-static int ovs_net_id __read_mostly;
 
 #define REHASH_FLOW_INTERVAL (10 * 60 * HZ)
 static void rehash_flow_table(struct work_struct *work);
 static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table);
 
+int ovs_net_id __read_mostly;
+
+static void ovs_notify(struct sk_buff *skb, struct genl_info *info,
+                      struct genl_multicast_group *grp)
+{
+       genl_notify(skb, genl_info_net(info), info->snd_portid,
+                   grp->id, info->nlhdr, GFP_KERNEL);
+}
+
 /**
  * DOC: Locking:
  *
- * Writes to device state (add/remove datapath, port, set operations on vports,
- * etc.) are protected by RTNL.
- *
- * Writes to other state (flow table modifications, set miscellaneous datapath
- * parameters, etc.) are protected by genl_mutex.  The RTNL lock nests inside
- * genl_mutex.
+ * All writes e.g. Writes to device state (add/remove datapath, port, set
+ * operations on vports, etc.), Writes to other state (flow table
+ * modifications, set miscellaneous datapath parameters, etc.) are protected
+ * by ovs_lock.
  *
  * Reads are protected by RCU.
  *
  * There are a few special cases (mostly stats) that have their own
  * synchronization but they nest under all of above and don't interact with
  * each other.
+ *
+ * The RTNL lock nests inside ovs_mutex.
  */
 
+static DEFINE_MUTEX(ovs_mutex);
+
+void ovs_lock(void)
+{
+       mutex_lock(&ovs_mutex);
+}
+
+void ovs_unlock(void)
+{
+       mutex_unlock(&ovs_mutex);
+}
+
+#ifdef CONFIG_LOCKDEP
+int lockdep_ovsl_is_held(void)
+{
+       if (debug_locks)
+               return lockdep_is_held(&ovs_mutex);
+       else
+               return 1;
+}
+#endif
+
 static struct vport *new_vport(const struct vport_parms *);
 static int queue_gso_packets(struct net *, int dp_ifindex, struct sk_buff *,
                             const struct dp_upcall_info *);
@@ -95,7 +117,7 @@ static int queue_userspace_packet(struct net *, int dp_ifindex,
                                  struct sk_buff *,
                                  const struct dp_upcall_info *);
 
-/* Must be called with rcu_read_lock, genl_mutex, or RTNL lock. */
+/* Must be called with rcu_read_lock or ovs_mutex. */
 static struct datapath *get_dp(struct net *net, int dp_ifindex)
 {
        struct datapath *dp = NULL;
@@ -113,10 +135,10 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex)
        return dp;
 }
 
-/* Must be called with rcu_read_lock or RTNL lock. */
+/* Must be called with rcu_read_lock or ovs_mutex. */
 const char *ovs_dp_name(const struct datapath *dp)
 {
-       struct vport *vport = ovs_vport_rtnl_rcu(dp, OVSP_LOCAL);
+       struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL);
        return vport->ops->get_name(vport);
 }
 
@@ -168,7 +190,7 @@ struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no)
        return NULL;
 }
 
-/* Called with RTNL lock and genl_lock. */
+/* Called with ovs_mutex. */
 static struct vport *new_vport(const struct vport_parms *parms)
 {
        struct vport *vport;
@@ -180,14 +202,12 @@ static struct vport *new_vport(const struct vport_parms *parms)
 
                hlist_add_head_rcu(&vport->dp_hash_node, head);
        }
-
        return vport;
 }
 
-/* Called with RTNL lock. */
 void ovs_dp_detach_port(struct vport *p)
 {
-       ASSERT_RTNL();
+       ASSERT_OVSL();
 
        /* First drop references to device. */
        hlist_del_rcu(&p->dp_hash_node);
@@ -337,6 +357,35 @@ static int queue_gso_packets(struct net *net, int dp_ifindex,
        return err;
 }
 
+static size_t key_attr_size(void)
+{
+       return    nla_total_size(4)   /* OVS_KEY_ATTR_PRIORITY */
+               + nla_total_size(4)   /* OVS_KEY_ATTR_IN_PORT */
+               + nla_total_size(4)   /* OVS_KEY_ATTR_SKB_MARK */
+               + nla_total_size(12)  /* OVS_KEY_ATTR_ETHERNET */
+               + nla_total_size(2)   /* OVS_KEY_ATTR_ETHERTYPE */
+               + nla_total_size(4)   /* OVS_KEY_ATTR_8021Q */
+               + nla_total_size(0)   /* OVS_KEY_ATTR_ENCAP */
+               + nla_total_size(2)   /* OVS_KEY_ATTR_ETHERTYPE */
+               + nla_total_size(40)  /* OVS_KEY_ATTR_IPV6 */
+               + nla_total_size(2)   /* OVS_KEY_ATTR_ICMPV6 */
+               + nla_total_size(28); /* OVS_KEY_ATTR_ND */
+}
+
+static size_t upcall_msg_size(const struct sk_buff *skb,
+                             const struct nlattr *userdata)
+{
+       size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))
+               + nla_total_size(skb->len) /* OVS_PACKET_ATTR_PACKET */
+               + nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */
+
+       /* OVS_PACKET_ATTR_USERDATA */
+       if (userdata)
+               size += NLA_ALIGN(userdata->nla_len);
+
+       return size;
+}
+
 static int queue_userspace_packet(struct net *net, int dp_ifindex,
                                  struct sk_buff *skb,
                                  const struct dp_upcall_info *upcall_info)
@@ -345,7 +394,6 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
        struct sk_buff *nskb = NULL;
        struct sk_buff *user_skb; /* to be queued to userspace */
        struct nlattr *nla;
-       unsigned int len;
        int err;
 
        if (vlan_tx_tag_present(skb)) {
@@ -353,7 +401,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
                if (!nskb)
                        return -ENOMEM;
 
-               nskb = __vlan_put_tag(nskb, vlan_tx_tag_get(nskb));
+               nskb = __vlan_put_tag(nskb, nskb->vlan_proto, vlan_tx_tag_get(nskb));
                if (!nskb)
                        return -ENOMEM;
 
@@ -366,13 +414,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
                goto out;
        }
 
-       len = sizeof(struct ovs_header);
-       len += nla_total_size(skb->len);
-       len += nla_total_size(FLOW_BUFSIZE);
-       if (upcall_info->cmd == OVS_PACKET_CMD_ACTION)
-               len += nla_total_size(8);
-
-       user_skb = genlmsg_new(len, GFP_ATOMIC);
+       user_skb = genlmsg_new(upcall_msg_size(skb, upcall_info->userdata), GFP_ATOMIC);
        if (!user_skb) {
                err = -ENOMEM;
                goto out;
@@ -387,8 +429,9 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
        nla_nest_end(user_skb, nla);
 
        if (upcall_info->userdata)
-               nla_put_u64(user_skb, OVS_PACKET_ATTR_USERDATA,
-                           nla_get_u64(upcall_info->userdata));
+               __nla_put(user_skb, OVS_PACKET_ATTR_USERDATA,
+                         nla_len(upcall_info->userdata),
+                         nla_data(upcall_info->userdata));
 
        nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len);
 
@@ -402,13 +445,13 @@ out:
        return err;
 }
 
-/* Called with genl_mutex. */
+/* Called with ovs_mutex. */
 static int flush_flows(struct datapath *dp)
 {
        struct flow_table *old_table;
        struct flow_table *new_table;
 
-       old_table = genl_dereference(dp->table);
+       old_table = ovsl_dereference(dp->table);
        new_table = ovs_flow_tbl_alloc(TBL_MIN_BUCKETS);
        if (!new_table)
                return -ENOMEM;
@@ -544,7 +587,7 @@ static int validate_userspace(const struct nlattr *attr)
 {
        static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] =   {
                [OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 },
-               [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_U64 },
+               [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC },
        };
        struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1];
        int error;
@@ -661,8 +704,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
 
        err = -EINVAL;
        if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] ||
-           !a[OVS_PACKET_ATTR_ACTIONS] ||
-           nla_len(a[OVS_PACKET_ATTR_PACKET]) < ETH_HLEN)
+           !a[OVS_PACKET_ATTR_ACTIONS])
                goto err;
 
        len = nla_len(a[OVS_PACKET_ATTR_PACKET]);
@@ -672,7 +714,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
                goto err;
        skb_reserve(packet, NET_IP_ALIGN);
 
-       memcpy(__skb_put(packet, len), nla_data(a[OVS_PACKET_ATTR_PACKET]), len);
+       nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len);
 
        skb_reset_mac_header(packet);
        eth = eth_hdr(packet);
@@ -680,7 +722,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        /* Normally, setting the skb 'protocol' field would be handled by a
         * call to eth_type_trans(), but it assumes there's a sending
         * device, which we may not have. */
-       if (ntohs(eth->h_proto) >= 1536)
+       if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
                packet->protocol = eth->h_proto;
        else
                packet->protocol = htons(ETH_P_802_2);
@@ -743,7 +785,7 @@ err:
 }
 
 static const struct nla_policy packet_policy[OVS_PACKET_ATTR_MAX + 1] = {
-       [OVS_PACKET_ATTR_PACKET] = { .type = NLA_UNSPEC },
+       [OVS_PACKET_ATTR_PACKET] = { .len = ETH_HLEN },
        [OVS_PACKET_ATTR_KEY] = { .type = NLA_NESTED },
        [OVS_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED },
 };
@@ -759,7 +801,7 @@ static struct genl_ops dp_packet_genl_ops[] = {
 static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats)
 {
        int i;
-       struct flow_table *table = genl_dereference(dp->table);
+       struct flow_table *table = ovsl_dereference(dp->table);
 
        stats->n_flows = ovs_flow_tbl_count(table);
 
@@ -801,7 +843,17 @@ static struct genl_multicast_group ovs_dp_flow_multicast_group = {
        .name = OVS_FLOW_MCGROUP
 };
 
-/* Called with genl_lock. */
+static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
+{
+       return NLMSG_ALIGN(sizeof(struct ovs_header))
+               + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */
+               + nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */
+               + nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */
+               + nla_total_size(8) /* OVS_FLOW_ATTR_USED */
+               + nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */
+}
+
+/* Called with ovs_mutex. */
 static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
                                  struct sk_buff *skb, u32 portid,
                                  u32 seq, u32 flags, u8 cmd)
@@ -815,8 +867,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        u8 tcp_flags;
        int err;
 
-       sf_acts = rcu_dereference_protected(flow->sf_acts,
-                                           lockdep_genl_is_held());
+       sf_acts = ovsl_dereference(flow->sf_acts);
 
        ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd);
        if (!ovs_header)
@@ -879,25 +930,10 @@ error:
 static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow)
 {
        const struct sw_flow_actions *sf_acts;
-       int len;
 
-       sf_acts = rcu_dereference_protected(flow->sf_acts,
-                                           lockdep_genl_is_held());
+       sf_acts = ovsl_dereference(flow->sf_acts);
 
-       /* OVS_FLOW_ATTR_KEY */
-       len = nla_total_size(FLOW_BUFSIZE);
-       /* OVS_FLOW_ATTR_ACTIONS */
-       len += nla_total_size(sf_acts->actions_len);
-       /* OVS_FLOW_ATTR_STATS */
-       len += nla_total_size(sizeof(struct ovs_flow_stats));
-       /* OVS_FLOW_ATTR_TCP_FLAGS */
-       len += nla_total_size(1);
-       /* OVS_FLOW_ATTR_USED */
-       len += nla_total_size(8);
-
-       len += NLMSG_ALIGN(sizeof(struct ovs_header));
-
-       return genlmsg_new(len, GFP_KERNEL);
+       return genlmsg_new(ovs_flow_cmd_msg_size(sf_acts), GFP_KERNEL);
 }
 
 static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
@@ -946,12 +982,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                goto error;
        }
 
+       ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        error = -ENODEV;
        if (!dp)
-               goto error;
+               goto err_unlock_ovs;
 
-       table = genl_dereference(dp->table);
+       table = ovsl_dereference(dp->table);
        flow = ovs_flow_tbl_lookup(table, &key, key_len);
        if (!flow) {
                struct sw_flow_actions *acts;
@@ -959,7 +996,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                /* Bail out if we're not allowed to create a new flow. */
                error = -ENOENT;
                if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
-                       goto error;
+                       goto err_unlock_ovs;
 
                /* Expand table, if necessary, to make room. */
                if (ovs_flow_tbl_need_to_expand(table)) {
@@ -969,7 +1006,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                        if (!IS_ERR(new_table)) {
                                rcu_assign_pointer(dp->table, new_table);
                                ovs_flow_tbl_deferred_destroy(table);
-                               table = genl_dereference(dp->table);
+                               table = ovsl_dereference(dp->table);
                        }
                }
 
@@ -977,7 +1014,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                flow = ovs_flow_alloc();
                if (IS_ERR(flow)) {
                        error = PTR_ERR(flow);
-                       goto error;
+                       goto err_unlock_ovs;
                }
                flow->key = key;
                clear_stats(flow);
@@ -1010,11 +1047,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                error = -EEXIST;
                if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW &&
                    info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
-                       goto error;
+                       goto err_unlock_ovs;
 
                /* Update actions. */
-               old_acts = rcu_dereference_protected(flow->sf_acts,
-                                                    lockdep_genl_is_held());
+               old_acts = ovsl_dereference(flow->sf_acts);
                acts_attrs = a[OVS_FLOW_ATTR_ACTIONS];
                if (acts_attrs &&
                   (old_acts->actions_len != nla_len(acts_attrs) ||
@@ -1025,7 +1061,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                        new_acts = ovs_flow_actions_alloc(acts_attrs);
                        error = PTR_ERR(new_acts);
                        if (IS_ERR(new_acts))
-                               goto error;
+                               goto err_unlock_ovs;
 
                        rcu_assign_pointer(flow->sf_acts, new_acts);
                        ovs_flow_deferred_free_acts(old_acts);
@@ -1041,11 +1077,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                        spin_unlock_bh(&flow->lock);
                }
        }
+       ovs_unlock();
 
        if (!IS_ERR(reply))
-               genl_notify(reply, genl_info_net(info), info->snd_portid,
-                          ovs_dp_flow_multicast_group.id, info->nlhdr,
-                          GFP_KERNEL);
+               ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
        else
                netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
                                ovs_dp_flow_multicast_group.id, PTR_ERR(reply));
@@ -1053,6 +1088,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 
 error_free_flow:
        ovs_flow_free(flow);
+err_unlock_ovs:
+       ovs_unlock();
 error:
        return error;
 }
@@ -1075,21 +1112,32 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
+       ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
-       if (!dp)
-               return -ENODEV;
+       if (!dp) {
+               err = -ENODEV;
+               goto unlock;
+       }
 
-       table = genl_dereference(dp->table);
+       table = ovsl_dereference(dp->table);
        flow = ovs_flow_tbl_lookup(table, &key, key_len);
-       if (!flow)
-               return -ENOENT;
+       if (!flow) {
+               err = -ENOENT;
+               goto unlock;
+       }
 
        reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                        info->snd_seq, OVS_FLOW_CMD_NEW);
-       if (IS_ERR(reply))
-               return PTR_ERR(reply);
+       if (IS_ERR(reply)) {
+               err = PTR_ERR(reply);
+               goto unlock;
+       }
 
+       ovs_unlock();
        return genlmsg_reply(reply, info);
+unlock:
+       ovs_unlock();
+       return err;
 }
 
 static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
@@ -1104,25 +1152,33 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        int err;
        int key_len;
 
+       ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
-       if (!dp)
-               return -ENODEV;
-
-       if (!a[OVS_FLOW_ATTR_KEY])
-               return flush_flows(dp);
+       if (!dp) {
+               err = -ENODEV;
+               goto unlock;
+       }
 
+       if (!a[OVS_FLOW_ATTR_KEY]) {
+               err = flush_flows(dp);
+               goto unlock;
+       }
        err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
        if (err)
-               return err;
+               goto unlock;
 
-       table = genl_dereference(dp->table);
+       table = ovsl_dereference(dp->table);
        flow = ovs_flow_tbl_lookup(table, &key, key_len);
-       if (!flow)
-               return -ENOENT;
+       if (!flow) {
+               err = -ENOENT;
+               goto unlock;
+       }
 
        reply = ovs_flow_cmd_alloc_info(flow);
-       if (!reply)
-               return -ENOMEM;
+       if (!reply) {
+               err = -ENOMEM;
+               goto unlock;
+       }
 
        ovs_flow_tbl_remove(table, flow);
 
@@ -1131,10 +1187,13 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        BUG_ON(err < 0);
 
        ovs_flow_deferred_free(flow);
+       ovs_unlock();
 
-       genl_notify(reply, genl_info_net(info), info->snd_portid,
-                   ovs_dp_flow_multicast_group.id, info->nlhdr, GFP_KERNEL);
+       ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
        return 0;
+unlock:
+       ovs_unlock();
+       return err;
 }
 
 static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
@@ -1143,11 +1202,14 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        struct datapath *dp;
        struct flow_table *table;
 
+       ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
-       if (!dp)
+       if (!dp) {
+               ovs_unlock();
                return -ENODEV;
+       }
 
-       table = genl_dereference(dp->table);
+       table = ovsl_dereference(dp->table);
 
        for (;;) {
                struct sw_flow *flow;
@@ -1168,6 +1230,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                cb->args[0] = bucket;
                cb->args[1] = obj;
        }
+       ovs_unlock();
        return skb->len;
 }
 
@@ -1213,6 +1276,16 @@ static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
        .name = OVS_DATAPATH_MCGROUP
 };
 
+static size_t ovs_dp_cmd_msg_size(void)
+{
+       size_t msgsize = NLMSG_ALIGN(sizeof(struct ovs_header));
+
+       msgsize += nla_total_size(IFNAMSIZ);
+       msgsize += nla_total_size(sizeof(struct ovs_dp_stats));
+
+       return msgsize;
+}
+
 static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
                                u32 portid, u32 seq, u32 flags, u8 cmd)
 {
@@ -1251,7 +1324,7 @@ static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 portid,
        struct sk_buff *skb;
        int retval;
 
-       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       skb = genlmsg_new(ovs_dp_cmd_msg_size(), GFP_KERNEL);
        if (!skb)
                return ERR_PTR(-ENOMEM);
 
@@ -1263,7 +1336,7 @@ static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 portid,
        return skb;
 }
 
-/* Called with genl_mutex and optionally with RTNL lock also. */
+/* Called with ovs_mutex. */
 static struct datapath *lookup_datapath(struct net *net,
                                        struct ovs_header *ovs_header,
                                        struct nlattr *a[OVS_DP_ATTR_MAX + 1])
@@ -1297,12 +1370,12 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID])
                goto err;
 
-       rtnl_lock();
+       ovs_lock();
 
        err = -ENOMEM;
        dp = kzalloc(sizeof(*dp), GFP_KERNEL);
        if (dp == NULL)
-               goto err_unlock_rtnl;
+               goto err_unlock_ovs;
 
        ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
 
@@ -1353,37 +1426,34 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 
        ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id);
        list_add_tail(&dp->list_node, &ovs_net->dps);
-       rtnl_unlock();
 
-       genl_notify(reply, genl_info_net(info), info->snd_portid,
-                   ovs_dp_datapath_multicast_group.id, info->nlhdr,
-                   GFP_KERNEL);
+       ovs_unlock();
+
+       ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
        return 0;
 
 err_destroy_local_port:
-       ovs_dp_detach_port(ovs_vport_rtnl(dp, OVSP_LOCAL));
+       ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
 err_destroy_ports_array:
        kfree(dp->ports);
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
 err_destroy_table:
-       ovs_flow_tbl_destroy(genl_dereference(dp->table));
+       ovs_flow_tbl_destroy(ovsl_dereference(dp->table));
 err_free_dp:
        release_net(ovs_dp_get_net(dp));
        kfree(dp);
-err_unlock_rtnl:
-       rtnl_unlock();
+err_unlock_ovs:
+       ovs_unlock();
 err:
        return err;
 }
 
-/* Called with genl_mutex. */
+/* Called with ovs_mutex. */
 static void __dp_destroy(struct datapath *dp)
 {
        int i;
 
-       rtnl_lock();
-
        for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {
                struct vport *vport;
                struct hlist_node *n;
@@ -1394,14 +1464,11 @@ static void __dp_destroy(struct datapath *dp)
        }
 
        list_del(&dp->list_node);
-       ovs_dp_detach_port(ovs_vport_rtnl(dp, OVSP_LOCAL));
 
-       /* rtnl_unlock() will wait until all the references to devices that
-        * are pending unregistration have been dropped.  We do it here to
-        * ensure that any internal devices (which contain DP pointers) are
-        * fully destroyed before freeing the datapath.
+       /* OVSP_LOCAL is datapath internal port. We need to make sure that
+        * all port in datapath are destroyed first before freeing datapath.
         */
-       rtnl_unlock();
+       ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
 
        call_rcu(&dp->rcu, destroy_dp_rcu);
 }
@@ -1412,24 +1479,27 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
+       ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        err = PTR_ERR(dp);
        if (IS_ERR(dp))
-               return err;
+               goto unlock;
 
        reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
                                      info->snd_seq, OVS_DP_CMD_DEL);
        err = PTR_ERR(reply);
        if (IS_ERR(reply))
-               return err;
+               goto unlock;
 
        __dp_destroy(dp);
+       ovs_unlock();
 
-       genl_notify(reply, genl_info_net(info), info->snd_portid,
-                   ovs_dp_datapath_multicast_group.id, info->nlhdr,
-                   GFP_KERNEL);
+       ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
 
        return 0;
+unlock:
+       ovs_unlock();
+       return err;
 }
 
 static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
@@ -1438,9 +1508,11 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
+       ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
+       err = PTR_ERR(dp);
        if (IS_ERR(dp))
-               return PTR_ERR(dp);
+               goto unlock;
 
        reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
                                      info->snd_seq, OVS_DP_CMD_NEW);
@@ -1448,31 +1520,45 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
                err = PTR_ERR(reply);
                netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
                                ovs_dp_datapath_multicast_group.id, err);
-               return 0;
+               err = 0;
+               goto unlock;
        }
 
-       genl_notify(reply, genl_info_net(info), info->snd_portid,
-                   ovs_dp_datapath_multicast_group.id, info->nlhdr,
-                   GFP_KERNEL);
+       ovs_unlock();
+       ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
 
        return 0;
+unlock:
+       ovs_unlock();
+       return err;
 }
 
 static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *reply;
        struct datapath *dp;
+       int err;
 
+       ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
-       if (IS_ERR(dp))
-               return PTR_ERR(dp);
+       if (IS_ERR(dp)) {
+               err = PTR_ERR(dp);
+               goto unlock;
+       }
 
        reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
                                      info->snd_seq, OVS_DP_CMD_NEW);
-       if (IS_ERR(reply))
-               return PTR_ERR(reply);
+       if (IS_ERR(reply)) {
+               err = PTR_ERR(reply);
+               goto unlock;
+       }
 
+       ovs_unlock();
        return genlmsg_reply(reply, info);
+
+unlock:
+       ovs_unlock();
+       return err;
 }
 
 static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
@@ -1482,6 +1568,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        int skip = cb->args[0];
        int i = 0;
 
+       ovs_lock();
        list_for_each_entry(dp, &ovs_net->dps, list_node) {
                if (i >= skip &&
                    ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid,
@@ -1490,6 +1577,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        break;
                i++;
        }
+       ovs_unlock();
 
        cb->args[0] = i;
 
@@ -1542,7 +1630,7 @@ struct genl_multicast_group ovs_dp_vport_multicast_group = {
        .name = OVS_VPORT_MCGROUP
 };
 
-/* Called with RTNL lock or RCU read lock. */
+/* Called with ovs_mutex or RCU read lock. */
 static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
                                   u32 portid, u32 seq, u32 flags, u8 cmd)
 {
@@ -1581,7 +1669,7 @@ error:
        return err;
 }
 
-/* Called with RTNL lock or RCU read lock. */
+/* Called with ovs_mutex or RCU read lock. */
 struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
                                         u32 seq, u8 cmd)
 {
@@ -1598,7 +1686,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
        return skb;
 }
 
-/* Called with RTNL lock or RCU read lock. */
+/* Called with ovs_mutex or RCU read lock. */
 static struct vport *lookup_vport(struct net *net,
                                  struct ovs_header *ovs_header,
                                  struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
@@ -1624,9 +1712,9 @@ static struct vport *lookup_vport(struct net *net,
                if (!dp)
                        return ERR_PTR(-ENODEV);
 
-               vport = ovs_vport_rtnl_rcu(dp, port_no);
+               vport = ovs_vport_ovsl_rcu(dp, port_no);
                if (!vport)
-                       return ERR_PTR(-ENOENT);
+                       return ERR_PTR(-ENODEV);
                return vport;
        } else
                return ERR_PTR(-EINVAL);
@@ -1648,7 +1736,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
            !a[OVS_VPORT_ATTR_UPCALL_PID])
                goto exit;
 
-       rtnl_lock();
+       ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        err = -ENODEV;
        if (!dp)
@@ -1661,7 +1749,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
                if (port_no >= DP_MAX_PORTS)
                        goto exit_unlock;
 
-               vport = ovs_vport_rtnl_rcu(dp, port_no);
+               vport = ovs_vport_ovsl(dp, port_no);
                err = -EBUSY;
                if (vport)
                        goto exit_unlock;
@@ -1671,7 +1759,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
                                err = -EFBIG;
                                goto exit_unlock;
                        }
-                       vport = ovs_vport_rtnl(dp, port_no);
+                       vport = ovs_vport_ovsl(dp, port_no);
                        if (!vport)
                                break;
                }
@@ -1697,11 +1785,11 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
                ovs_dp_detach_port(vport);
                goto exit_unlock;
        }
-       genl_notify(reply, genl_info_net(info), info->snd_portid,
-                   ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
+
+       ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
 
 exit_unlock:
-       rtnl_unlock();
+       ovs_unlock();
 exit:
        return err;
 }
@@ -1713,7 +1801,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
-       rtnl_lock();
+       ovs_lock();
        vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
@@ -1742,8 +1830,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
                                      info->snd_seq, 0, OVS_VPORT_CMD_NEW);
        BUG_ON(err < 0);
 
-       genl_notify(reply, genl_info_net(info), info->snd_portid,
-                   ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
+       ovs_unlock();
+       ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
+       return 0;
 
        rtnl_unlock();
        return 0;
@@ -1751,7 +1840,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
 exit_free:
        kfree_skb(reply);
 exit_unlock:
-       rtnl_unlock();
+       ovs_unlock();
        return err;
 }
 
@@ -1762,7 +1851,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
-       rtnl_lock();
+       ovs_lock();
        vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
@@ -1782,11 +1871,10 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
        err = 0;
        ovs_dp_detach_port(vport);
 
-       genl_notify(reply, genl_info_net(info), info->snd_portid,
-                   ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
+       ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
 
 exit_unlock:
-       rtnl_unlock();
+       ovs_unlock();
        return err;
 }
 
@@ -1946,13 +2034,13 @@ static void rehash_flow_table(struct work_struct *work)
        struct datapath *dp;
        struct net *net;
 
-       genl_lock();
+       ovs_lock();
        rtnl_lock();
        for_each_net(net) {
                struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
 
                list_for_each_entry(dp, &ovs_net->dps, list_node) {
-                       struct flow_table *old_table = genl_dereference(dp->table);
+                       struct flow_table *old_table = ovsl_dereference(dp->table);
                        struct flow_table *new_table;
 
                        new_table = ovs_flow_tbl_rehash(old_table);
@@ -1963,8 +2051,7 @@ static void rehash_flow_table(struct work_struct *work)
                }
        }
        rtnl_unlock();
-       genl_unlock();
-
+       ovs_unlock();
        schedule_delayed_work(&rehash_flow_wq, REHASH_FLOW_INTERVAL);
 }
 
@@ -1973,18 +2060,21 @@ static int __net_init ovs_init_net(struct net *net)
        struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
 
        INIT_LIST_HEAD(&ovs_net->dps);
+       INIT_WORK(&ovs_net->dp_notify_work, ovs_dp_notify_wq);
        return 0;
 }
 
 static void __net_exit ovs_exit_net(struct net *net)
 {
-       struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
        struct datapath *dp, *dp_next;
+       struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
 
-       genl_lock();
+       ovs_lock();
        list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node)
                __dp_destroy(dp);
-       genl_unlock();
+       ovs_unlock();
+
+       cancel_work_sync(&ovs_net->dp_notify_work);
 }
 
 static struct pernet_operations ovs_net_ops = {
index 031dfbf37c937dff57657d33d45143d454cc4e46..16b8406952164e4c264a859f23f53e3bbe300740 100644 (file)
@@ -57,10 +57,9 @@ struct dp_stats_percpu {
  * struct datapath - datapath for flow-based packet switching
  * @rcu: RCU callback head for deferred destruction.
  * @list_node: Element in global 'dps' list.
- * @n_flows: Number of flows currently in flow table.
- * @table: Current flow table.  Protected by genl_lock and RCU.
+ * @table: Current flow table.  Protected by ovs_mutex and RCU.
  * @ports: Hash table for ports.  %OVSP_LOCAL port always exists.  Protected by
- * RTNL and RCU.
+ * ovs_mutex and RCU.
  * @stats_percpu: Per-CPU datapath statistics.
  * @net: Reference to net namespace.
  *
@@ -86,26 +85,6 @@ struct datapath {
 #endif
 };
 
-struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no);
-
-static inline struct vport *ovs_vport_rcu(const struct datapath *dp, int port_no)
-{
-       WARN_ON_ONCE(!rcu_read_lock_held());
-       return ovs_lookup_vport(dp, port_no);
-}
-
-static inline struct vport *ovs_vport_rtnl_rcu(const struct datapath *dp, int port_no)
-{
-       WARN_ON_ONCE(!rcu_read_lock_held() && !rtnl_is_locked());
-       return ovs_lookup_vport(dp, port_no);
-}
-
-static inline struct vport *ovs_vport_rtnl(const struct datapath *dp, int port_no)
-{
-       ASSERT_RTNL();
-       return ovs_lookup_vport(dp, port_no);
-}
-
 /**
  * struct ovs_skb_cb - OVS data in skb CB
  * @flow: The flow associated with this packet.  May be %NULL if no flow.
@@ -119,7 +98,7 @@ struct ovs_skb_cb {
  * struct dp_upcall - metadata to include with a packet to send to userspace
  * @cmd: One of %OVS_PACKET_CMD_*.
  * @key: Becomes %OVS_PACKET_ATTR_KEY.  Must be nonnull.
- * @userdata: If nonnull, its u64 value is extracted and passed to userspace as
+ * @userdata: If nonnull, its variable-length value is passed to userspace as
  * %OVS_PACKET_ATTR_USERDATA.
  * @pid: Netlink PID to which packet should be sent.  If @pid is 0 then no
  * packet is sent and the packet is accounted in the datapath's @n_lost
@@ -132,6 +111,30 @@ struct dp_upcall_info {
        u32 portid;
 };
 
+/**
+ * struct ovs_net - Per net-namespace data for ovs.
+ * @dps: List of datapaths to enable dumping them all out.
+ * Protected by genl_mutex.
+ */
+struct ovs_net {
+       struct list_head dps;
+       struct work_struct dp_notify_work;
+};
+
+extern int ovs_net_id;
+void ovs_lock(void);
+void ovs_unlock(void);
+
+#ifdef CONFIG_LOCKDEP
+int lockdep_ovsl_is_held(void);
+#else
+#define lockdep_ovsl_is_held() 1
+#endif
+
+#define ASSERT_OVSL()          WARN_ON(unlikely(!lockdep_ovsl_is_held()))
+#define ovsl_dereference(p)                                    \
+       rcu_dereference_protected(p, lockdep_ovsl_is_held())
+
 static inline struct net *ovs_dp_get_net(struct datapath *dp)
 {
        return read_pnet(&dp->net);
@@ -142,6 +145,26 @@ static inline void ovs_dp_set_net(struct datapath *dp, struct net *net)
        write_pnet(&dp->net, net);
 }
 
+struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no);
+
+static inline struct vport *ovs_vport_rcu(const struct datapath *dp, int port_no)
+{
+       WARN_ON_ONCE(!rcu_read_lock_held());
+       return ovs_lookup_vport(dp, port_no);
+}
+
+static inline struct vport *ovs_vport_ovsl_rcu(const struct datapath *dp, int port_no)
+{
+       WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_ovsl_is_held());
+       return ovs_lookup_vport(dp, port_no);
+}
+
+static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_no)
+{
+       ASSERT_OVSL();
+       return ovs_lookup_vport(dp, port_no);
+}
+
 extern struct notifier_block ovs_dp_device_notifier;
 extern struct genl_multicast_group ovs_dp_vport_multicast_group;
 
@@ -155,4 +178,5 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
                                         u8 cmd);
 
 int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
+void ovs_dp_notify_wq(struct work_struct *work);
 #endif /* datapath.h */
index 5558350e0d33ee4f9b26f8be13801a6139fb7aa8..ef4feec6cd845e67903706f448ccc5d1a8f96556 100644 (file)
 
 #include <linux/netdevice.h>
 #include <net/genetlink.h>
+#include <net/netns/generic.h>
 
 #include "datapath.h"
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
+static void dp_detach_port_notify(struct vport *vport)
+{
+       struct sk_buff *notify;
+       struct datapath *dp;
+
+       dp = vport->dp;
+       notify = ovs_vport_cmd_build_info(vport, 0, 0,
+                                         OVS_VPORT_CMD_DEL);
+       ovs_dp_detach_port(vport);
+       if (IS_ERR(notify)) {
+               netlink_set_err(ovs_dp_get_net(dp)->genl_sock, 0,
+                               ovs_dp_vport_multicast_group.id,
+                               PTR_ERR(notify));
+               return;
+       }
+
+       genlmsg_multicast_netns(ovs_dp_get_net(dp), notify, 0,
+                               ovs_dp_vport_multicast_group.id,
+                               GFP_KERNEL);
+}
+
+void ovs_dp_notify_wq(struct work_struct *work)
+{
+       struct ovs_net *ovs_net = container_of(work, struct ovs_net, dp_notify_work);
+       struct datapath *dp;
+
+       ovs_lock();
+       list_for_each_entry(dp, &ovs_net->dps, list_node) {
+               int i;
+
+               for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {
+                       struct vport *vport;
+                       struct hlist_node *n;
+
+                       hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node) {
+                               struct netdev_vport *netdev_vport;
+
+                               if (vport->ops->type != OVS_VPORT_TYPE_NETDEV)
+                                       continue;
+
+                               netdev_vport = netdev_vport_priv(vport);
+                               if (netdev_vport->dev->reg_state == NETREG_UNREGISTERED ||
+                                   netdev_vport->dev->reg_state == NETREG_UNREGISTERING)
+                                       dp_detach_port_notify(vport);
+                       }
+               }
+       }
+       ovs_unlock();
+}
+
 static int dp_device_event(struct notifier_block *unused, unsigned long event,
                           void *ptr)
 {
+       struct ovs_net *ovs_net;
        struct net_device *dev = ptr;
-       struct vport *vport;
+       struct vport *vport = NULL;
 
-       if (ovs_is_internal_dev(dev))
-               vport = ovs_internal_dev_get_vport(dev);
-       else
+       if (!ovs_is_internal_dev(dev))
                vport = ovs_netdev_get_vport(dev);
 
        if (!vport)
                return NOTIFY_DONE;
 
-       switch (event) {
-       case NETDEV_UNREGISTER:
-               if (!ovs_is_internal_dev(dev)) {
-                       struct sk_buff *notify;
-                       struct datapath *dp = vport->dp;
-
-                       notify = ovs_vport_cmd_build_info(vport, 0, 0,
-                                                         OVS_VPORT_CMD_DEL);
-                       ovs_dp_detach_port(vport);
-                       if (IS_ERR(notify)) {
-                               netlink_set_err(ovs_dp_get_net(dp)->genl_sock, 0,
-                                               ovs_dp_vport_multicast_group.id,
-                                               PTR_ERR(notify));
-                               break;
-                       }
-
-                       genlmsg_multicast_netns(ovs_dp_get_net(dp), notify, 0,
-                                               ovs_dp_vport_multicast_group.id,
-                                               GFP_KERNEL);
-               }
-               break;
+       if (event == NETDEV_UNREGISTER) {
+               ovs_net = net_generic(dev_net(dev), ovs_net_id);
+               queue_work(system_wq, &ovs_net->dp_notify_work);
        }
 
        return NOTIFY_DONE;
index 67a2b783fe70257cb6c33542806d3f3ca17f3d76..b15321a2228c879a6a2bc56e950e7335be2a9507 100644 (file)
@@ -211,7 +211,7 @@ struct sw_flow_actions *ovs_flow_actions_alloc(const struct nlattr *actions)
                return ERR_PTR(-ENOMEM);
 
        sfa->actions_len = actions_len;
-       memcpy(sfa->actions, nla_data(actions), actions_len);
+       nla_memcpy(sfa->actions, actions, actions_len);
        return sfa;
 }
 
@@ -466,7 +466,7 @@ static __be16 parse_ethertype(struct sk_buff *skb)
        proto = *(__be16 *) skb->data;
        __skb_pull(skb, sizeof(__be16));
 
-       if (ntohs(proto) >= 1536)
+       if (ntohs(proto) >= ETH_P_802_3_MIN)
                return proto;
 
        if (skb->len < sizeof(struct llc_snap_hdr))
@@ -483,7 +483,7 @@ static __be16 parse_ethertype(struct sk_buff *skb)
 
        __skb_pull(skb, sizeof(struct llc_snap_hdr));
 
-       if (ntohs(llc->ethertype) >= 1536)
+       if (ntohs(llc->ethertype) >= ETH_P_802_3_MIN)
                return llc->ethertype;
 
        return htons(ETH_P_802_2);
@@ -1038,7 +1038,7 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 
        if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
                swkey->eth.type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
-               if (ntohs(swkey->eth.type) < 1536)
+               if (ntohs(swkey->eth.type) < ETH_P_802_3_MIN)
                        return -EINVAL;
                attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
        } else {
index a7bb60ff3b5b6f057c8bf5c7300357a9a926e148..0875fde65b9cc21815b725ac84d579efbd64a2da 100644 (file)
@@ -138,27 +138,6 @@ int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
 void ovs_flow_used(struct sw_flow *, struct sk_buff *);
 u64 ovs_flow_used_time(unsigned long flow_jiffies);
 
-/* Upper bound on the length of a nlattr-formatted flow key.  The longest
- * nlattr-formatted flow key would be:
- *
- *                         struct  pad  nl hdr  total
- *                         ------  ---  ------  -----
- *  OVS_KEY_ATTR_PRIORITY      4    --     4      8
- *  OVS_KEY_ATTR_IN_PORT       4    --     4      8
- *  OVS_KEY_ATTR_SKB_MARK      4    --     4      8
- *  OVS_KEY_ATTR_ETHERNET     12    --     4     16
- *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (outer VLAN ethertype)
- *  OVS_KEY_ATTR_8021Q         4    --     4      8
- *  OVS_KEY_ATTR_ENCAP         0    --     4      4  (VLAN encapsulation)
- *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (inner VLAN ethertype)
- *  OVS_KEY_ATTR_IPV6         40    --     4     44
- *  OVS_KEY_ATTR_ICMPV6        2     2     4      8
- *  OVS_KEY_ATTR_ND           28    --     4     32
- *  -------------------------------------------------
- *  total                                       152
- */
-#define FLOW_BUFSIZE 152
-
 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
 int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
                      const struct nlattr *);
index 0531de6c7a4a858e6c5ef88c53e9dd639aff6fef..73682de8dc69c49905f24548f32c624f6c1d333f 100644 (file)
@@ -63,16 +63,6 @@ static struct rtnl_link_stats64 *internal_dev_get_stats(struct net_device *netde
        return stats;
 }
 
-static int internal_dev_mac_addr(struct net_device *dev, void *p)
-{
-       struct sockaddr *addr = p;
-
-       if (!is_valid_ether_addr(addr->sa_data))
-               return -EADDRNOTAVAIL;
-       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-       return 0;
-}
-
 /* Called with rcu_read_lock_bh. */
 static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
@@ -126,7 +116,7 @@ static const struct net_device_ops internal_dev_netdev_ops = {
        .ndo_open = internal_dev_open,
        .ndo_stop = internal_dev_stop,
        .ndo_start_xmit = internal_dev_xmit,
-       .ndo_set_mac_address = internal_dev_mac_addr,
+       .ndo_set_mac_address = eth_mac_addr,
        .ndo_change_mtu = internal_dev_change_mtu,
        .ndo_get_stats64 = internal_dev_get_stats,
 };
@@ -138,6 +128,7 @@ static void do_setup(struct net_device *netdev)
        netdev->netdev_ops = &internal_dev_netdev_ops;
 
        netdev->priv_flags &= ~IFF_TX_SKB_SHARING;
+       netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
        netdev->destructor = internal_dev_destructor;
        SET_ETHTOOL_OPS(netdev, &internal_dev_ethtool_ops);
        netdev->tx_queue_len = 0;
@@ -146,7 +137,7 @@ static void do_setup(struct net_device *netdev)
                           NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_TSO;
 
        netdev->vlan_features = netdev->features;
-       netdev->features |= NETIF_F_HW_VLAN_TX;
+       netdev->features |= NETIF_F_HW_VLAN_CTAG_TX;
        netdev->hw_features = netdev->features & ~NETIF_F_LLTX;
        eth_hw_addr_random(netdev);
 }
@@ -182,16 +173,19 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
        if (vport->port_no == OVSP_LOCAL)
                netdev_vport->dev->features |= NETIF_F_NETNS_LOCAL;
 
+       rtnl_lock();
        err = register_netdevice(netdev_vport->dev);
        if (err)
                goto error_free_netdev;
 
        dev_set_promiscuity(netdev_vport->dev, 1);
+       rtnl_unlock();
        netif_start_queue(netdev_vport->dev);
 
        return vport;
 
 error_free_netdev:
+       rtnl_unlock();
        free_netdev(netdev_vport->dev);
 error_free_vport:
        ovs_vport_free(vport);
@@ -204,10 +198,13 @@ static void internal_dev_destroy(struct vport *vport)
        struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
 
        netif_stop_queue(netdev_vport->dev);
+       rtnl_lock();
        dev_set_promiscuity(netdev_vport->dev, -1);
 
        /* unregister_netdevice() waits for an RCU grace period. */
        unregister_netdevice(netdev_vport->dev);
+
+       rtnl_unlock();
 }
 
 static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
index 2130d61c384a227e0d7f5093e285054497e8fb40..40a89ae8e19fd8556d4c51ac635bf029eddb634b 100644 (file)
@@ -100,16 +100,20 @@ static struct vport *netdev_create(const struct vport_parms *parms)
                goto error_put;
        }
 
+       rtnl_lock();
        err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook,
                                         vport);
        if (err)
-               goto error_put;
+               goto error_unlock;
 
        dev_set_promiscuity(netdev_vport->dev, 1);
        netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;
+       rtnl_unlock();
 
        return vport;
 
+error_unlock:
+       rtnl_unlock();
 error_put:
        dev_put(netdev_vport->dev);
 error_free_vport:
@@ -131,9 +135,11 @@ static void netdev_destroy(struct vport *vport)
 {
        struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
 
+       rtnl_lock();
        netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
        netdev_rx_handler_unregister(netdev_vport->dev);
        dev_set_promiscuity(netdev_vport->dev, -1);
+       rtnl_unlock();
 
        call_rcu(&netdev_vport->rcu, free_port_rcu);
 }
index f6b8132ce4cb7380a6bca860b4e50a2aa6756772..720623190eaa82d9ad4a2fcf460a0270cf57a03b 100644 (file)
@@ -40,7 +40,7 @@ static const struct vport_ops *vport_ops_list[] = {
        &ovs_internal_vport_ops,
 };
 
-/* Protected by RCU read lock for reading, RTNL lock for writing. */
+/* Protected by RCU read lock for reading, ovs_mutex for writing. */
 static struct hlist_head *dev_table;
 #define VPORT_HASH_BUCKETS 1024
 
@@ -80,7 +80,7 @@ static struct hlist_head *hash_bucket(struct net *net, const char *name)
  *
  * @name: name of port to find
  *
- * Must be called with RTNL or RCU read lock.
+ * Must be called with ovs or RCU read lock.
  */
 struct vport *ovs_vport_locate(struct net *net, const char *name)
 {
@@ -128,7 +128,7 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
        vport->ops = ops;
        INIT_HLIST_NODE(&vport->dp_hash_node);
 
-       vport->percpu_stats = alloc_percpu(struct vport_percpu_stats);
+       vport->percpu_stats = alloc_percpu(struct pcpu_tstats);
        if (!vport->percpu_stats) {
                kfree(vport);
                return ERR_PTR(-ENOMEM);
@@ -161,7 +161,7 @@ void ovs_vport_free(struct vport *vport)
  * @parms: Information about new vport.
  *
  * Creates a new vport with the specified configuration (which is dependent on
- * device type).  RTNL lock must be held.
+ * device type).  ovs_mutex must be held.
  */
 struct vport *ovs_vport_add(const struct vport_parms *parms)
 {
@@ -169,8 +169,6 @@ struct vport *ovs_vport_add(const struct vport_parms *parms)
        int err = 0;
        int i;
 
-       ASSERT_RTNL();
-
        for (i = 0; i < ARRAY_SIZE(vport_ops_list); i++) {
                if (vport_ops_list[i]->type == parms->type) {
                        struct hlist_head *bucket;
@@ -201,12 +199,10 @@ out:
  * @port: New configuration.
  *
  * Modifies an existing device with the specified configuration (which is
- * dependent on device type).  RTNL lock must be held.
+ * dependent on device type).  ovs_mutex must be held.
  */
 int ovs_vport_set_options(struct vport *vport, struct nlattr *options)
 {
-       ASSERT_RTNL();
-
        if (!vport->ops->set_options)
                return -EOPNOTSUPP;
        return vport->ops->set_options(vport, options);
@@ -218,11 +214,11 @@ int ovs_vport_set_options(struct vport *vport, struct nlattr *options)
  * @vport: vport to delete.
  *
  * Detaches @vport from its datapath and destroys it.  It is possible to fail
- * for reasons such as lack of memory.  RTNL lock must be held.
+ * for reasons such as lack of memory.  ovs_mutex must be held.
  */
 void ovs_vport_del(struct vport *vport)
 {
-       ASSERT_RTNL();
+       ASSERT_OVSL();
 
        hlist_del_rcu(&vport->hash_node);
 
@@ -237,7 +233,7 @@ void ovs_vport_del(struct vport *vport)
  *
  * Retrieves transmit, receive, and error stats for the given device.
  *
- * Must be called with RTNL lock or rcu_read_lock.
+ * Must be called with ovs_mutex or rcu_read_lock.
  */
 void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
 {
@@ -264,16 +260,16 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
        spin_unlock_bh(&vport->stats_lock);
 
        for_each_possible_cpu(i) {
-               const struct vport_percpu_stats *percpu_stats;
-               struct vport_percpu_stats local_stats;
+               const struct pcpu_tstats *percpu_stats;
+               struct pcpu_tstats local_stats;
                unsigned int start;
 
                percpu_stats = per_cpu_ptr(vport->percpu_stats, i);
 
                do {
-                       start = u64_stats_fetch_begin_bh(&percpu_stats->sync);
+                       start = u64_stats_fetch_begin_bh(&percpu_stats->syncp);
                        local_stats = *percpu_stats;
-               } while (u64_stats_fetch_retry_bh(&percpu_stats->sync, start));
+               } while (u64_stats_fetch_retry_bh(&percpu_stats->syncp, start));
 
                stats->rx_bytes         += local_stats.rx_bytes;
                stats->rx_packets       += local_stats.rx_packets;
@@ -296,22 +292,24 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
  * negative error code if a real error occurred.  If an error occurs, @skb is
  * left unmodified.
  *
- * Must be called with RTNL lock or rcu_read_lock.
+ * Must be called with ovs_mutex or rcu_read_lock.
  */
 int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
 {
        struct nlattr *nla;
+       int err;
+
+       if (!vport->ops->get_options)
+               return 0;
 
        nla = nla_nest_start(skb, OVS_VPORT_ATTR_OPTIONS);
        if (!nla)
                return -EMSGSIZE;
 
-       if (vport->ops->get_options) {
-               int err = vport->ops->get_options(vport, skb);
-               if (err) {
-                       nla_nest_cancel(skb, nla);
-                       return err;
-               }
+       err = vport->ops->get_options(vport, skb);
+       if (err) {
+               nla_nest_cancel(skb, nla);
+               return err;
        }
 
        nla_nest_end(skb, nla);
@@ -329,13 +327,13 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
  */
 void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
 {
-       struct vport_percpu_stats *stats;
+       struct pcpu_tstats *stats;
 
        stats = this_cpu_ptr(vport->percpu_stats);
-       u64_stats_update_begin(&stats->sync);
+       u64_stats_update_begin(&stats->syncp);
        stats->rx_packets++;
        stats->rx_bytes += skb->len;
-       u64_stats_update_end(&stats->sync);
+       u64_stats_update_end(&stats->syncp);
 
        ovs_dp_process_received_packet(vport, skb);
 }
@@ -346,7 +344,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
  * @vport: vport on which to send the packet
  * @skb: skb to send
  *
- * Sends the given packet and returns the length of data sent.  Either RTNL
+ * Sends the given packet and returns the length of data sent.  Either ovs
  * lock or rcu_read_lock must be held.
  */
 int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
@@ -354,14 +352,14 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
        int sent = vport->ops->send(vport, skb);
 
        if (likely(sent)) {
-               struct vport_percpu_stats *stats;
+               struct pcpu_tstats *stats;
 
                stats = this_cpu_ptr(vport->percpu_stats);
 
-               u64_stats_update_begin(&stats->sync);
+               u64_stats_update_begin(&stats->syncp);
                stats->tx_packets++;
                stats->tx_bytes += sent;
-               u64_stats_update_end(&stats->sync);
+               u64_stats_update_end(&stats->syncp);
        }
        return sent;
 }
index 3f7961ea3c568d54011d6a570975fceb3c20d15f..7ba08c30b853e597b6bce2157695a6f08f58a808 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef VPORT_H
 #define VPORT_H 1
 
+#include <linux/if_tunnel.h>
 #include <linux/list.h>
 #include <linux/netlink.h>
 #include <linux/openvswitch.h>
@@ -50,14 +51,6 @@ int ovs_vport_send(struct vport *, struct sk_buff *);
 
 /* The following definitions are for implementers of vport devices: */
 
-struct vport_percpu_stats {
-       u64 rx_bytes;
-       u64 rx_packets;
-       u64 tx_bytes;
-       u64 tx_packets;
-       struct u64_stats_sync sync;
-};
-
 struct vport_err_stats {
        u64 rx_dropped;
        u64 rx_errors;
@@ -68,10 +61,10 @@ struct vport_err_stats {
 /**
  * struct vport - one port within a datapath
  * @rcu: RCU callback head for deferred destruction.
- * @port_no: Index into @dp's @ports array.
  * @dp: Datapath to which this port belongs.
  * @upcall_portid: The Netlink port to use for packets received on this port that
  * miss the flow table.
+ * @port_no: Index into @dp's @ports array.
  * @hash_node: Element in @dev_table hash table in vport.c.
  * @dp_hash_node: Element in @datapath->ports hash table in datapath.c.
  * @ops: Class structure.
@@ -81,15 +74,15 @@ struct vport_err_stats {
  */
 struct vport {
        struct rcu_head rcu;
-       u16 port_no;
        struct datapath *dp;
        u32 upcall_portid;
+       u16 port_no;
 
        struct hlist_node hash_node;
        struct hlist_node dp_hash_node;
        const struct vport_ops *ops;
 
-       struct vport_percpu_stats __percpu *percpu_stats;
+       struct pcpu_tstats __percpu *percpu_stats;
 
        spinlock_t stats_lock;
        struct vport_err_stats err_stats;
@@ -138,14 +131,14 @@ struct vport_parms {
 struct vport_ops {
        enum ovs_vport_type type;
 
-       /* Called with RTNL lock. */
+       /* Called with ovs_mutex. */
        struct vport *(*create)(const struct vport_parms *);
        void (*destroy)(struct vport *);
 
        int (*set_options)(struct vport *, struct nlattr *);
        int (*get_options)(const struct vport *, struct sk_buff *);
 
-       /* Called with rcu_read_lock or RTNL lock. */
+       /* Called with rcu_read_lock or ovs_mutex. */
        const char *(*get_name)(const struct vport *);
        void (*get_config)(const struct vport *, void *);
        int (*get_ifindex)(const struct vport *);
index 1d6793dbfbae23f8c3f28163c0a67718d489d3ed..7e387ff64465c934075993f77c4def101d2160de 100644 (file)
@@ -158,10 +158,16 @@ struct packet_mreq_max {
        unsigned char   mr_address[MAX_ADDR_LEN];
 };
 
+union tpacket_uhdr {
+       struct tpacket_hdr  *h1;
+       struct tpacket2_hdr *h2;
+       struct tpacket3_hdr *h3;
+       void *raw;
+};
+
 static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
                int closing, int tx_ring);
 
-
 #define V3_ALIGNMENT   (8)
 
 #define BLK_HDR_LEN    (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))
@@ -181,6 +187,8 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
 
 struct packet_sock;
 static int tpacket_snd(struct packet_sock *po, struct msghdr *msg);
+static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
+                      struct packet_type *pt, struct net_device *orig_dev);
 
 static void *packet_previous_frame(struct packet_sock *po,
                struct packet_ring_buffer *rb,
@@ -288,11 +296,7 @@ static inline __pure struct page *pgv_to_page(void *addr)
 
 static void __packet_set_status(struct packet_sock *po, void *frame, int status)
 {
-       union {
-               struct tpacket_hdr *h1;
-               struct tpacket2_hdr *h2;
-               void *raw;
-       } h;
+       union tpacket_uhdr h;
 
        h.raw = frame;
        switch (po->tp_version) {
@@ -315,11 +319,7 @@ static void __packet_set_status(struct packet_sock *po, void *frame, int status)
 
 static int __packet_get_status(struct packet_sock *po, void *frame)
 {
-       union {
-               struct tpacket_hdr *h1;
-               struct tpacket2_hdr *h2;
-               void *raw;
-       } h;
+       union tpacket_uhdr h;
 
        smp_rmb();
 
@@ -345,11 +345,7 @@ static void *packet_lookup_frame(struct packet_sock *po,
                int status)
 {
        unsigned int pg_vec_pos, frame_offset;
-       union {
-               struct tpacket_hdr *h1;
-               struct tpacket2_hdr *h2;
-               void *raw;
-       } h;
+       union tpacket_uhdr h;
 
        pg_vec_pos = position / rb->frames_per_block;
        frame_offset = position % rb->frames_per_block;
@@ -973,11 +969,11 @@ static void *packet_current_rx_frame(struct packet_sock *po,
 
 static void *prb_lookup_block(struct packet_sock *po,
                                     struct packet_ring_buffer *rb,
-                                    unsigned int previous,
+                                    unsigned int idx,
                                     int status)
 {
        struct tpacket_kbdq_core *pkc  = GET_PBDQC_FROM_RB(rb);
-       struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, previous);
+       struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, idx);
 
        if (status != BLOCK_STATUS(pbd))
                return NULL;
@@ -1041,6 +1037,29 @@ static void packet_increment_head(struct packet_ring_buffer *buff)
        buff->head = buff->head != buff->frame_max ? buff->head+1 : 0;
 }
 
+static bool packet_rcv_has_room(struct packet_sock *po, struct sk_buff *skb)
+{
+       struct sock *sk = &po->sk;
+       bool has_room;
+
+       if (po->prot_hook.func != tpacket_rcv)
+               return (atomic_read(&sk->sk_rmem_alloc) + skb->truesize)
+                       <= sk->sk_rcvbuf;
+
+       spin_lock(&sk->sk_receive_queue.lock);
+       if (po->tp_version == TPACKET_V3)
+               has_room = prb_lookup_block(po, &po->rx_ring,
+                                           po->rx_ring.prb_bdqc.kactive_blk_num,
+                                           TP_STATUS_KERNEL);
+       else
+               has_room = packet_lookup_frame(po, &po->rx_ring,
+                                              po->rx_ring.head,
+                                              TP_STATUS_KERNEL);
+       spin_unlock(&sk->sk_receive_queue.lock);
+
+       return has_room;
+}
+
 static void packet_sock_destruct(struct sock *sk)
 {
        skb_queue_purge(&sk->sk_error_queue);
@@ -1066,16 +1085,16 @@ static int fanout_rr_next(struct packet_fanout *f, unsigned int num)
        return x;
 }
 
-static struct sock *fanout_demux_hash(struct packet_fanout *f, struct sk_buff *skb, unsigned int num)
+static unsigned int fanout_demux_hash(struct packet_fanout *f,
+                                     struct sk_buff *skb,
+                                     unsigned int num)
 {
-       u32 idx, hash = skb->rxhash;
-
-       idx = ((u64)hash * num) >> 32;
-
-       return f->arr[idx];
+       return (((u64)skb->rxhash) * num) >> 32;
 }
 
-static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb, unsigned int num)
+static unsigned int fanout_demux_lb(struct packet_fanout *f,
+                                   struct sk_buff *skb,
+                                   unsigned int num)
 {
        int cur, old;
 
@@ -1083,14 +1102,40 @@ static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb
        while ((old = atomic_cmpxchg(&f->rr_cur, cur,
                                     fanout_rr_next(f, num))) != cur)
                cur = old;
-       return f->arr[cur];
+       return cur;
+}
+
+static unsigned int fanout_demux_cpu(struct packet_fanout *f,
+                                    struct sk_buff *skb,
+                                    unsigned int num)
+{
+       return smp_processor_id() % num;
 }
 
-static struct sock *fanout_demux_cpu(struct packet_fanout *f, struct sk_buff *skb, unsigned int num)
+static unsigned int fanout_demux_rollover(struct packet_fanout *f,
+                                         struct sk_buff *skb,
+                                         unsigned int idx, unsigned int skip,
+                                         unsigned int num)
 {
-       unsigned int cpu = smp_processor_id();
+       unsigned int i, j;
 
-       return f->arr[cpu % num];
+       i = j = min_t(int, f->next[idx], num - 1);
+       do {
+               if (i != skip && packet_rcv_has_room(pkt_sk(f->arr[i]), skb)) {
+                       if (i != j)
+                               f->next[idx] = i;
+                       return i;
+               }
+               if (++i == num)
+                       i = 0;
+       } while (i != j);
+
+       return idx;
+}
+
+static bool fanout_has_flag(struct packet_fanout *f, u16 flag)
+{
+       return f->flags & (flag >> 8);
 }
 
 static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,
@@ -1099,7 +1144,7 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,
        struct packet_fanout *f = pt->af_packet_priv;
        unsigned int num = f->num_members;
        struct packet_sock *po;
-       struct sock *sk;
+       unsigned int idx;
 
        if (!net_eq(dev_net(dev), read_pnet(&f->net)) ||
            !num) {
@@ -1110,23 +1155,31 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,
        switch (f->type) {
        case PACKET_FANOUT_HASH:
        default:
-               if (f->defrag) {
+               if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) {
                        skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET);
                        if (!skb)
                                return 0;
                }
                skb_get_rxhash(skb);
-               sk = fanout_demux_hash(f, skb, num);
+               idx = fanout_demux_hash(f, skb, num);
                break;
        case PACKET_FANOUT_LB:
-               sk = fanout_demux_lb(f, skb, num);
+               idx = fanout_demux_lb(f, skb, num);
                break;
        case PACKET_FANOUT_CPU:
-               sk = fanout_demux_cpu(f, skb, num);
+               idx = fanout_demux_cpu(f, skb, num);
+               break;
+       case PACKET_FANOUT_ROLLOVER:
+               idx = fanout_demux_rollover(f, skb, 0, (unsigned int) -1, num);
                break;
        }
 
-       po = pkt_sk(sk);
+       po = pkt_sk(f->arr[idx]);
+       if (fanout_has_flag(f, PACKET_FANOUT_FLAG_ROLLOVER) &&
+           unlikely(!packet_rcv_has_room(po, skb))) {
+               idx = fanout_demux_rollover(f, skb, idx, idx, num);
+               po = pkt_sk(f->arr[idx]);
+       }
 
        return po->prot_hook.func(skb, dev, &po->prot_hook, orig_dev);
 }
@@ -1175,10 +1228,13 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
        struct packet_sock *po = pkt_sk(sk);
        struct packet_fanout *f, *match;
        u8 type = type_flags & 0xff;
-       u8 defrag = (type_flags & PACKET_FANOUT_FLAG_DEFRAG) ? 1 : 0;
+       u8 flags = type_flags >> 8;
        int err;
 
        switch (type) {
+       case PACKET_FANOUT_ROLLOVER:
+               if (type_flags & PACKET_FANOUT_FLAG_ROLLOVER)
+                       return -EINVAL;
        case PACKET_FANOUT_HASH:
        case PACKET_FANOUT_LB:
        case PACKET_FANOUT_CPU:
@@ -1203,7 +1259,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
                }
        }
        err = -EINVAL;
-       if (match && match->defrag != defrag)
+       if (match && match->flags != flags)
                goto out;
        if (!match) {
                err = -ENOMEM;
@@ -1213,7 +1269,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
                write_pnet(&match->net, sock_net(sk));
                match->id = id;
                match->type = type;
-               match->defrag = defrag;
+               match->flags = flags;
                atomic_set(&match->rr_cur, 0);
                INIT_LIST_HEAD(&match->list);
                spin_lock_init(&match->lock);
@@ -1443,13 +1499,14 @@ retry:
        skb->dev = dev;
        skb->priority = sk->sk_priority;
        skb->mark = sk->sk_mark;
-       err = sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
-       if (err < 0)
-               goto out_unlock;
+
+       sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
 
        if (unlikely(extra_len == 4))
                skb->no_fcs = 1;
 
+       skb_probe_transport_header(skb, 0);
+
        dev_queue_xmit(skb);
        rcu_read_unlock();
        return len;
@@ -1600,27 +1657,40 @@ drop:
        return 0;
 }
 
+static void tpacket_get_timestamp(struct sk_buff *skb, struct timespec *ts,
+                                 unsigned int flags)
+{
+       struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
+
+       if (shhwtstamps) {
+               if ((flags & SOF_TIMESTAMPING_SYS_HARDWARE) &&
+                   ktime_to_timespec_cond(shhwtstamps->syststamp, ts))
+                       return;
+               if ((flags & SOF_TIMESTAMPING_RAW_HARDWARE) &&
+                   ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts))
+                       return;
+       }
+
+       if (ktime_to_timespec_cond(skb->tstamp, ts))
+               return;
+
+       getnstimeofday(ts);
+}
+
 static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
                       struct packet_type *pt, struct net_device *orig_dev)
 {
        struct sock *sk;
        struct packet_sock *po;
        struct sockaddr_ll *sll;
-       union {
-               struct tpacket_hdr *h1;
-               struct tpacket2_hdr *h2;
-               struct tpacket3_hdr *h3;
-               void *raw;
-       } h;
+       union tpacket_uhdr h;
        u8 *skb_head = skb->data;
        int skb_len = skb->len;
        unsigned int snaplen, res;
        unsigned long status = TP_STATUS_USER;
        unsigned short macoff, netoff, hdrlen;
        struct sk_buff *copy_skb = NULL;
-       struct timeval tv;
        struct timespec ts;
-       struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
 
        if (skb->pkt_type == PACKET_LOOPBACK)
                goto drop;
@@ -1703,6 +1773,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
        spin_unlock(&sk->sk_receive_queue.lock);
 
        skb_copy_bits(skb, 0, h.raw + macoff, snaplen);
+       tpacket_get_timestamp(skb, &ts, po->tp_tstamp);
 
        switch (po->tp_version) {
        case TPACKET_V1:
@@ -1710,18 +1781,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
                h.h1->tp_snaplen = snaplen;
                h.h1->tp_mac = macoff;
                h.h1->tp_net = netoff;
-               if ((po->tp_tstamp & SOF_TIMESTAMPING_SYS_HARDWARE)
-                               && shhwtstamps->syststamp.tv64)
-                       tv = ktime_to_timeval(shhwtstamps->syststamp);
-               else if ((po->tp_tstamp & SOF_TIMESTAMPING_RAW_HARDWARE)
-                               && shhwtstamps->hwtstamp.tv64)
-                       tv = ktime_to_timeval(shhwtstamps->hwtstamp);
-               else if (skb->tstamp.tv64)
-                       tv = ktime_to_timeval(skb->tstamp);
-               else
-                       do_gettimeofday(&tv);
-               h.h1->tp_sec = tv.tv_sec;
-               h.h1->tp_usec = tv.tv_usec;
+               h.h1->tp_sec = ts.tv_sec;
+               h.h1->tp_usec = ts.tv_nsec / NSEC_PER_USEC;
                hdrlen = sizeof(*h.h1);
                break;
        case TPACKET_V2:
@@ -1729,16 +1790,6 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
                h.h2->tp_snaplen = snaplen;
                h.h2->tp_mac = macoff;
                h.h2->tp_net = netoff;
-               if ((po->tp_tstamp & SOF_TIMESTAMPING_SYS_HARDWARE)
-                               && shhwtstamps->syststamp.tv64)
-                       ts = ktime_to_timespec(shhwtstamps->syststamp);
-               else if ((po->tp_tstamp & SOF_TIMESTAMPING_RAW_HARDWARE)
-                               && shhwtstamps->hwtstamp.tv64)
-                       ts = ktime_to_timespec(shhwtstamps->hwtstamp);
-               else if (skb->tstamp.tv64)
-                       ts = ktime_to_timespec(skb->tstamp);
-               else
-                       getnstimeofday(&ts);
                h.h2->tp_sec = ts.tv_sec;
                h.h2->tp_nsec = ts.tv_nsec;
                if (vlan_tx_tag_present(skb)) {
@@ -1759,16 +1810,6 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
                h.h3->tp_snaplen = snaplen;
                h.h3->tp_mac = macoff;
                h.h3->tp_net = netoff;
-               if ((po->tp_tstamp & SOF_TIMESTAMPING_SYS_HARDWARE)
-                               && shhwtstamps->syststamp.tv64)
-                       ts = ktime_to_timespec(shhwtstamps->syststamp);
-               else if ((po->tp_tstamp & SOF_TIMESTAMPING_RAW_HARDWARE)
-                               && shhwtstamps->hwtstamp.tv64)
-                       ts = ktime_to_timespec(shhwtstamps->hwtstamp);
-               else if (skb->tstamp.tv64)
-                       ts = ktime_to_timespec(skb->tstamp);
-               else
-                       getnstimeofday(&ts);
                h.h3->tp_sec  = ts.tv_sec;
                h.h3->tp_nsec = ts.tv_nsec;
                hdrlen = sizeof(*h.h3);
@@ -1846,11 +1887,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
                void *frame, struct net_device *dev, int size_max,
                __be16 proto, unsigned char *addr, int hlen)
 {
-       union {
-               struct tpacket_hdr *h1;
-               struct tpacket2_hdr *h2;
-               void *raw;
-       } ph;
+       union tpacket_uhdr ph;
        int to_write, offset, len, tp_len, nr_frags, len_max;
        struct socket *sock = po->sk.sk_socket;
        struct page *page;
@@ -1880,6 +1917,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
 
        skb_reserve(skb, hlen);
        skb_reset_network_header(skb);
+       skb_probe_transport_header(skb, 0);
 
        if (po->tp_tx_has_off) {
                int off_min, off_max, off;
@@ -2247,9 +2285,8 @@ static int packet_snd(struct socket *sock,
        err = skb_copy_datagram_from_iovec(skb, offset, msg->msg_iov, 0, len);
        if (err)
                goto out_free;
-       err = sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
-       if (err < 0)
-               goto out_free;
+
+       sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
 
        if (!gso_type && (len > dev->mtu + reserve + extra_len)) {
                /* Earlier code assumed this would be a VLAN pkt,
@@ -2289,6 +2326,8 @@ static int packet_snd(struct socket *sock,
                len += vnet_hdr_len;
        }
 
+       skb_probe_transport_header(skb, reserve);
+
        if (unlikely(extra_len == 4))
                skb->no_fcs = 1;
 
@@ -3240,7 +3279,8 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
        case PACKET_FANOUT:
                val = (po->fanout ?
                       ((u32)po->fanout->id |
-                       ((u32)po->fanout->type << 16)) :
+                       ((u32)po->fanout->type << 16) |
+                       ((u32)po->fanout->flags << 24)) :
                       0);
                break;
        case PACKET_TX_HAS_OFF:
index e84cab8cb7a99a6a07fe26daa9f33c03f8bb3bda..e891f025a1b9870f35fb72f341d7f8f3f993e476 100644 (file)
@@ -77,10 +77,11 @@ struct packet_fanout {
        unsigned int            num_members;
        u16                     id;
        u8                      type;
-       u8                      defrag;
+       u8                      flags;
        atomic_t                rr_cur;
        struct list_head        list;
        struct sock             *arr[PACKET_FANOUT_MAX];
+       int                     next[PACKET_FANOUT_MAX];
        spinlock_t              lock;
        atomic_t                sk_ref;
        struct packet_type      prot_hook ____cacheline_aligned_in_smp;
index 0193630d306125a62f273db3165e98a306249fc0..dc15f430080831e74fade00799a661ab10cc6f84 100644 (file)
@@ -61,7 +61,7 @@ static const struct nla_policy ifa_phonet_policy[IFA_MAX+1] = {
        [IFA_LOCAL] = { .type = NLA_U8 },
 };
 
-static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr)
+static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[IFA_MAX+1];
@@ -224,7 +224,7 @@ static const struct nla_policy rtm_phonet_policy[RTA_MAX+1] = {
        [RTA_OIF] = { .type = NLA_U32 },
 };
 
-static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr)
+static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[RTA_MAX+1];
index 4b5ab21ecb24a0732d971f3ce08256a96d496125..d11ac79246e46d8388d6bee79de663928aa0f86e 100644 (file)
@@ -51,7 +51,7 @@ static int rfkill_regulator_set_block(void *data, bool blocked)
        return 0;
 }
 
-struct rfkill_ops rfkill_regulator_ops = {
+static struct rfkill_ops rfkill_regulator_ops = {
        .set_block = rfkill_regulator_set_block,
 };
 
index 8579c4bb20c9daf093adf0b33b8bd13a94f5c6e0..fd7072827a40139c4ffba595aaa261282641e37f 100644 (file)
@@ -982,7 +982,7 @@ done:
        return ret;
 }
 
-static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tca[TCA_ACT_MAX + 1];
index 08fa1e8a4ca4a5a0d45e399103cd680622b870bd..3a4c0caa1f7de0fdfdb618bac9308aa32330c7f7 100644 (file)
@@ -166,15 +166,17 @@ static int tcf_csum_ipv4_igmp(struct sk_buff *skb,
        return 1;
 }
 
-static int tcf_csum_ipv6_icmp(struct sk_buff *skb, struct ipv6hdr *ip6h,
+static int tcf_csum_ipv6_icmp(struct sk_buff *skb,
                              unsigned int ihl, unsigned int ipl)
 {
        struct icmp6hdr *icmp6h;
+       const struct ipv6hdr *ip6h;
 
        icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h));
        if (icmp6h == NULL)
                return 0;
 
+       ip6h = ipv6_hdr(skb);
        icmp6h->icmp6_cksum = 0;
        skb->csum = csum_partial(icmp6h, ipl - ihl, 0);
        icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
@@ -186,15 +188,17 @@ static int tcf_csum_ipv6_icmp(struct sk_buff *skb, struct ipv6hdr *ip6h,
        return 1;
 }
 
-static int tcf_csum_ipv4_tcp(struct sk_buff *skb, struct iphdr *iph,
+static int tcf_csum_ipv4_tcp(struct sk_buff *skb,
                             unsigned int ihl, unsigned int ipl)
 {
        struct tcphdr *tcph;
+       const struct iphdr *iph;
 
        tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
        if (tcph == NULL)
                return 0;
 
+       iph = ip_hdr(skb);
        tcph->check = 0;
        skb->csum = csum_partial(tcph, ipl - ihl, 0);
        tcph->check = tcp_v4_check(ipl - ihl,
@@ -205,15 +209,17 @@ static int tcf_csum_ipv4_tcp(struct sk_buff *skb, struct iphdr *iph,
        return 1;
 }
 
-static int tcf_csum_ipv6_tcp(struct sk_buff *skb, struct ipv6hdr *ip6h,
+static int tcf_csum_ipv6_tcp(struct sk_buff *skb,
                             unsigned int ihl, unsigned int ipl)
 {
        struct tcphdr *tcph;
+       const struct ipv6hdr *ip6h;
 
        tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
        if (tcph == NULL)
                return 0;
 
+       ip6h = ipv6_hdr(skb);
        tcph->check = 0;
        skb->csum = csum_partial(tcph, ipl - ihl, 0);
        tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
@@ -225,10 +231,11 @@ static int tcf_csum_ipv6_tcp(struct sk_buff *skb, struct ipv6hdr *ip6h,
        return 1;
 }
 
-static int tcf_csum_ipv4_udp(struct sk_buff *skb, struct iphdr *iph,
+static int tcf_csum_ipv4_udp(struct sk_buff *skb,
                             unsigned int ihl, unsigned int ipl, int udplite)
 {
        struct udphdr *udph;
+       const struct iphdr *iph;
        u16 ul;
 
        /*
@@ -242,6 +249,7 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, struct iphdr *iph,
        if (udph == NULL)
                return 0;
 
+       iph = ip_hdr(skb);
        ul = ntohs(udph->len);
 
        if (udplite || udph->check) {
@@ -276,10 +284,11 @@ ignore_obscure_skb:
        return 1;
 }
 
-static int tcf_csum_ipv6_udp(struct sk_buff *skb, struct ipv6hdr *ip6h,
+static int tcf_csum_ipv6_udp(struct sk_buff *skb,
                             unsigned int ihl, unsigned int ipl, int udplite)
 {
        struct udphdr *udph;
+       const struct ipv6hdr *ip6h;
        u16 ul;
 
        /*
@@ -293,6 +302,7 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, struct ipv6hdr *ip6h,
        if (udph == NULL)
                return 0;
 
+       ip6h = ipv6_hdr(skb);
        ul = ntohs(udph->len);
 
        udph->check = 0;
@@ -328,7 +338,7 @@ ignore_obscure_skb:
 
 static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
 {
-       struct iphdr *iph;
+       const struct iphdr *iph;
        int ntkoff;
 
        ntkoff = skb_network_offset(skb);
@@ -353,19 +363,19 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
                break;
        case IPPROTO_TCP:
                if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP)
-                       if (!tcf_csum_ipv4_tcp(skb, iph, iph->ihl * 4,
+                       if (!tcf_csum_ipv4_tcp(skb, iph->ihl * 4,
                                               ntohs(iph->tot_len)))
                                goto fail;
                break;
        case IPPROTO_UDP:
                if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP)
-                       if (!tcf_csum_ipv4_udp(skb, iph, iph->ihl * 4,
+                       if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4,
                                               ntohs(iph->tot_len), 0))
                                goto fail;
                break;
        case IPPROTO_UDPLITE:
                if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE)
-                       if (!tcf_csum_ipv4_udp(skb, iph, iph->ihl * 4,
+                       if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4,
                                               ntohs(iph->tot_len), 1))
                                goto fail;
                break;
@@ -377,7 +387,7 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
                    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
                        goto fail;
 
-               ip_send_check(iph);
+               ip_send_check(ip_hdr(skb));
        }
 
        return 1;
@@ -456,6 +466,7 @@ static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags)
                        ixhl = ipv6_optlen(ip6xh);
                        if (!pskb_may_pull(skb, hl + ixhl + ntkoff))
                                goto fail;
+                       ip6xh = (void *)(skb_network_header(skb) + hl);
                        if ((nexthdr == NEXTHDR_HOP) &&
                            !(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl)))
                                goto fail;
@@ -464,25 +475,25 @@ static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags)
                        break;
                case IPPROTO_ICMPV6:
                        if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP)
-                               if (!tcf_csum_ipv6_icmp(skb, ip6h,
+                               if (!tcf_csum_ipv6_icmp(skb,
                                                        hl, pl + sizeof(*ip6h)))
                                        goto fail;
                        goto done;
                case IPPROTO_TCP:
                        if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP)
-                               if (!tcf_csum_ipv6_tcp(skb, ip6h,
+                               if (!tcf_csum_ipv6_tcp(skb,
                                                       hl, pl + sizeof(*ip6h)))
                                        goto fail;
                        goto done;
                case IPPROTO_UDP:
                        if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP)
-                               if (!tcf_csum_ipv6_udp(skb, ip6h, hl,
+                               if (!tcf_csum_ipv6_udp(skb, hl,
                                                       pl + sizeof(*ip6h), 0))
                                        goto fail;
                        goto done;
                case IPPROTO_UDPLITE:
                        if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE)
-                               if (!tcf_csum_ipv6_udp(skb, ip6h, hl,
+                               if (!tcf_csum_ipv6_udp(skb, hl,
                                                       pl + sizeof(*ip6h), 1))
                                        goto fail;
                        goto done;
index 964f5e4f4b8ac641f3f96b1b5a69d3c64aa70d48..8e118af9097345a500e1006bff6fa5c108435603 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 #include <linux/kmod.h>
-#include <linux/netlink.h>
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <net/net_namespace.h>
@@ -118,7 +117,7 @@ static inline u32 tcf_auto_prio(struct tcf_proto *tp)
 
 /* Add/change/delete/get a filter node */
 
-static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tca[TCA_MAX + 1];
@@ -141,7 +140,12 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 
        if ((n->nlmsg_type != RTM_GETTFILTER) && !capable(CAP_NET_ADMIN))
                return -EPERM;
+
 replay:
+       err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
+       if (err < 0)
+               return err;
+
        t = nlmsg_data(n);
        protocol = TC_H_MIN(t->tcm_info);
        prio = TC_H_MAJ(t->tcm_info);
@@ -164,10 +168,6 @@ replay:
        if (dev == NULL)
                return -ENODEV;
 
-       err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
-       if (err < 0)
-               return err;
-
        /* Find qdisc */
        if (!parent) {
                q = dev->qdisc;
@@ -427,7 +427,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
        const struct Qdisc_class_ops *cops;
        struct tcf_dump_args arg;
 
-       if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))
+       if (nlmsg_len(cb->nlh) < sizeof(*tcm))
                return skb->len;
        dev = __dev_get_by_index(net, tcm->tcm_ifindex);
        if (!dev)
index aa36a8c8b33bf27e286443f23ba27393e7156cda..7881e2fccbc2ea1b7cd0eaf98ac3f0675836e150 100644 (file)
@@ -393,7 +393,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
                        return -EOPNOTSUPP;
 
                if ((keymask & (FLOW_KEY_SKUID|FLOW_KEY_SKGID)) &&
-                   sk_user_ns(NETLINK_CB(in_skb).ssk) != &init_user_ns)
+                   sk_user_ns(NETLINK_CB(in_skb).sk) != &init_user_ns)
                        return -EOPNOTSUPP;
        }
 
index c297e2a8e2a10d79fb560d2bcea112be204c873c..2b935e7cfe7b7bbb78c3cc914a39929f1db766a8 100644 (file)
@@ -971,13 +971,13 @@ check_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w)
  * Delete/get qdisc.
  */
 
-static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
 {
        struct net *net = sock_net(skb->sk);
        struct tcmsg *tcm = nlmsg_data(n);
        struct nlattr *tca[TCA_MAX + 1];
        struct net_device *dev;
-       u32 clid = tcm->tcm_parent;
+       u32 clid;
        struct Qdisc *q = NULL;
        struct Qdisc *p = NULL;
        int err;
@@ -985,14 +985,15 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
        if ((n->nlmsg_type != RTM_GETQDISC) && !capable(CAP_NET_ADMIN))
                return -EPERM;
 
-       dev = __dev_get_by_index(net, tcm->tcm_ifindex);
-       if (!dev)
-               return -ENODEV;
-
        err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
        if (err < 0)
                return err;
 
+       dev = __dev_get_by_index(net, tcm->tcm_ifindex);
+       if (!dev)
+               return -ENODEV;
+
+       clid = tcm->tcm_parent;
        if (clid) {
                if (clid != TC_H_ROOT) {
                        if (TC_H_MAJ(clid) != TC_H_MAJ(TC_H_INGRESS)) {
@@ -1038,7 +1039,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
  * Create/change qdisc.
  */
 
-static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
 {
        struct net *net = sock_net(skb->sk);
        struct tcmsg *tcm;
@@ -1053,6 +1054,10 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 
 replay:
        /* Reinit, just in case something touches this. */
+       err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
+       if (err < 0)
+               return err;
+
        tcm = nlmsg_data(n);
        clid = tcm->tcm_parent;
        q = p = NULL;
@@ -1061,9 +1066,6 @@ replay:
        if (!dev)
                return -ENODEV;
 
-       err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
-       if (err < 0)
-               return err;
 
        if (clid) {
                if (clid != TC_H_ROOT) {
@@ -1372,7 +1374,7 @@ done:
 
 
 
-static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n)
 {
        struct net *net = sock_net(skb->sk);
        struct tcmsg *tcm = nlmsg_data(n);
@@ -1382,22 +1384,22 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
        const struct Qdisc_class_ops *cops;
        unsigned long cl = 0;
        unsigned long new_cl;
-       u32 portid = tcm->tcm_parent;
-       u32 clid = tcm->tcm_handle;
-       u32 qid = TC_H_MAJ(clid);
+       u32 portid;
+       u32 clid;
+       u32 qid;
        int err;
 
        if ((n->nlmsg_type != RTM_GETTCLASS) && !capable(CAP_NET_ADMIN))
                return -EPERM;
 
-       dev = __dev_get_by_index(net, tcm->tcm_ifindex);
-       if (!dev)
-               return -ENODEV;
-
        err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
        if (err < 0)
                return err;
 
+       dev = __dev_get_by_index(net, tcm->tcm_ifindex);
+       if (!dev)
+               return -ENODEV;
+
        /*
           parent == TC_H_UNSPEC - unspecified parent.
           parent == TC_H_ROOT   - class is root, which has no parent.
@@ -1413,6 +1415,10 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 
        /* Step 1. Determine qdisc handle X:0 */
 
+       portid = tcm->tcm_parent;
+       clid = tcm->tcm_handle;
+       qid = TC_H_MAJ(clid);
+
        if (portid != TC_H_ROOT) {
                u32 qid1 = TC_H_MAJ(portid);
 
@@ -1636,7 +1642,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
        struct net_device *dev;
        int t, s_t;
 
-       if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))
+       if (nlmsg_len(cb->nlh) < sizeof(*tcm))
                return 0;
        dev = dev_get_by_index(net, tcm->tcm_ifindex);
        if (!dev)
index 571f1d211f4d52482c137f6a80aa02e3496e7984..79b1876b6cd260fac7d150ceeaa7a5a647266a56 100644 (file)
@@ -981,6 +981,7 @@ static const struct nla_policy htb_policy[TCA_HTB_MAX + 1] = {
        [TCA_HTB_INIT]  = { .len = sizeof(struct tc_htb_glob) },
        [TCA_HTB_CTAB]  = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
        [TCA_HTB_RTAB]  = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
+       [TCA_HTB_DIRECT_QLEN] = { .type = NLA_U32 },
 };
 
 static void htb_work_func(struct work_struct *work)
@@ -994,7 +995,7 @@ static void htb_work_func(struct work_struct *work)
 static int htb_init(struct Qdisc *sch, struct nlattr *opt)
 {
        struct htb_sched *q = qdisc_priv(sch);
-       struct nlattr *tb[TCA_HTB_INIT + 1];
+       struct nlattr *tb[TCA_HTB_MAX + 1];
        struct tc_htb_glob *gopt;
        int err;
        int i;
@@ -1002,20 +1003,16 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
        if (!opt)
                return -EINVAL;
 
-       err = nla_parse_nested(tb, TCA_HTB_INIT, opt, htb_policy);
+       err = nla_parse_nested(tb, TCA_HTB_MAX, opt, htb_policy);
        if (err < 0)
                return err;
 
-       if (tb[TCA_HTB_INIT] == NULL) {
-               pr_err("HTB: hey probably you have bad tc tool ?\n");
+       if (!tb[TCA_HTB_INIT])
                return -EINVAL;
-       }
+
        gopt = nla_data(tb[TCA_HTB_INIT]);
-       if (gopt->version != HTB_VER >> 16) {
-               pr_err("HTB: need tc/htb version %d (minor is %d), you have %d\n",
-                      HTB_VER >> 16, HTB_VER & 0xffff, gopt->version);
+       if (gopt->version != HTB_VER >> 16)
                return -EINVAL;
-       }
 
        err = qdisc_class_hash_init(&q->clhash);
        if (err < 0)
@@ -1027,10 +1024,13 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
        INIT_WORK(&q->work, htb_work_func);
        skb_queue_head_init(&q->direct_queue);
 
-       q->direct_qlen = qdisc_dev(sch)->tx_queue_len;
-       if (q->direct_qlen < 2) /* some devices have zero tx_queue_len */
-               q->direct_qlen = 2;
-
+       if (tb[TCA_HTB_DIRECT_QLEN])
+               q->direct_qlen = nla_get_u32(tb[TCA_HTB_DIRECT_QLEN]);
+       else {
+               q->direct_qlen = qdisc_dev(sch)->tx_queue_len;
+               if (q->direct_qlen < 2) /* some devices have zero tx_queue_len */
+                       q->direct_qlen = 2;
+       }
        if ((q->rate2quantum = gopt->rate2quantum) < 1)
                q->rate2quantum = 1;
        q->defcls = gopt->defcls;
@@ -1056,7 +1056,8 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
        nest = nla_nest_start(skb, TCA_OPTIONS);
        if (nest == NULL)
                goto nla_put_failure;
-       if (nla_put(skb, TCA_HTB_INIT, sizeof(gopt), &gopt))
+       if (nla_put(skb, TCA_HTB_INIT, sizeof(gopt), &gopt) ||
+           nla_put_u32(skb, TCA_HTB_DIRECT_QLEN, q->direct_qlen))
                goto nla_put_failure;
        nla_nest_end(skb, nest);
 
@@ -1311,7 +1312,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
        struct htb_sched *q = qdisc_priv(sch);
        struct htb_class *cl = (struct htb_class *)*arg, *parent;
        struct nlattr *opt = tca[TCA_OPTIONS];
-       struct nlattr *tb[__TCA_HTB_MAX];
+       struct nlattr *tb[TCA_HTB_MAX + 1];
        struct tc_htb_opt *hopt;
 
        /* extract all subattrs from opt attr */
index d2709e2b7be6642d47a691d27c983a937c7e8648..423549a714e51f6d653b5858f679eeb376ab974e 100644 (file)
@@ -104,8 +104,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
 
        /* Initialize the object handling fields.  */
        atomic_set(&asoc->base.refcnt, 1);
-       asoc->base.dead = 0;
-       asoc->base.malloced = 0;
+       asoc->base.dead = false;
 
        /* Initialize the bind addr area.  */
        sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port);
@@ -371,7 +370,6 @@ struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep,
        if (!sctp_association_init(asoc, ep, sk, scope, gfp))
                goto fail_init;
 
-       asoc->base.malloced = 1;
        SCTP_DBG_OBJCNT_INC(assoc);
        SCTP_DEBUG_PRINTK("Created asoc %p\n", asoc);
 
@@ -409,7 +407,7 @@ void sctp_association_free(struct sctp_association *asoc)
        /* Mark as dead, so other users can know this structure is
         * going away.
         */
-       asoc->base.dead = 1;
+       asoc->base.dead = true;
 
        /* Dispose of any data lying around in the outqueue. */
        sctp_outq_free(&asoc->outqueue);
@@ -484,10 +482,8 @@ static void sctp_association_destroy(struct sctp_association *asoc)
 
        WARN_ON(atomic_read(&asoc->rmem_alloc));
 
-       if (asoc->base.malloced) {
-               kfree(asoc);
-               SCTP_DBG_OBJCNT_DEC(assoc);
-       }
+       kfree(asoc);
+       SCTP_DBG_OBJCNT_DEC(assoc);
 }
 
 /* Change the primary destination address for the peer. */
index d886b3bf84f5a1823e208d3c784db05a6c88938a..41145fe31813bc70907a9bdf9bba92886246b5b6 100644 (file)
@@ -131,8 +131,6 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest,
  */
 void sctp_bind_addr_init(struct sctp_bind_addr *bp, __u16 port)
 {
-       bp->malloced = 0;
-
        INIT_LIST_HEAD(&bp->address_list);
        bp->port = port;
 }
@@ -155,11 +153,6 @@ void sctp_bind_addr_free(struct sctp_bind_addr *bp)
 {
        /* Empty the bind address list. */
        sctp_bind_addr_clean(bp);
-
-       if (bp->malloced) {
-               kfree(bp);
-               SCTP_DBG_OBJCNT_DEC(bind_addr);
-       }
 }
 
 /* Add an address to the bind address list in the SCTP_bind_addr structure. */
index 12ed45dbe75d6b779fd0dcf4d38f6126e23c03fb..5fbd7bc6bb11077f8af91bf01ea8403c5087c31e 100644 (file)
@@ -121,8 +121,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
 
        /* Initialize the basic object fields. */
        atomic_set(&ep->base.refcnt, 1);
-       ep->base.dead = 0;
-       ep->base.malloced = 1;
+       ep->base.dead = false;
 
        /* Create an input queue.  */
        sctp_inq_init(&ep->base.inqueue);
@@ -198,7 +197,7 @@ struct sctp_endpoint *sctp_endpoint_new(struct sock *sk, gfp_t gfp)
                goto fail;
        if (!sctp_endpoint_init(ep, sk, gfp))
                goto fail_init;
-       ep->base.malloced = 1;
+
        SCTP_DBG_OBJCNT_INC(ep);
        return ep;
 
@@ -234,7 +233,7 @@ void sctp_endpoint_add_asoc(struct sctp_endpoint *ep,
  */
 void sctp_endpoint_free(struct sctp_endpoint *ep)
 {
-       ep->base.dead = 1;
+       ep->base.dead = true;
 
        ep->base.sk->sk_state = SCTP_SS_CLOSED;
 
@@ -279,11 +278,8 @@ static void sctp_endpoint_destroy(struct sctp_endpoint *ep)
        if (ep->base.sk)
                sock_put(ep->base.sk);
 
-       /* Finally, free up our memory. */
-       if (ep->base.malloced) {
-               kfree(ep);
-               SCTP_DBG_OBJCNT_DEC(ep);
-       }
+       kfree(ep);
+       SCTP_DBG_OBJCNT_DEC(ep);
 }
 
 /* Hold a reference to an endpoint. */
index 2d5ad280de388d07f3949a578ce6bbdf9cf6ae5f..3221d073448ce0356ac24078a3f69c6501e91371 100644 (file)
@@ -58,8 +58,6 @@ void sctp_inq_init(struct sctp_inq *queue)
 
        /* Create a task for delivering data.  */
        INIT_WORK(&queue->immediate, NULL);
-
-       queue->malloced = 0;
 }
 
 /* Release the memory associated with an SCTP inqueue.  */
@@ -80,11 +78,6 @@ void sctp_inq_free(struct sctp_inq *queue)
                sctp_chunk_free(queue->in_progress);
                queue->in_progress = NULL;
        }
-
-       if (queue->malloced) {
-               /* Dump the master memory segment.  */
-               kfree(queue);
-       }
 }
 
 /* Put a new packet in an SCTP inqueue.
index f5200a2ad852e0ac9e14593a070fac3ad6e66558..bbef4a7a9b569f1d639bf0fe4b2bbef8a5296951 100644 (file)
@@ -136,7 +136,7 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
        packet->overhead = overhead;
        sctp_packet_reset(packet);
        packet->vtag = 0;
-       packet->malloced = 0;
+
        return packet;
 }
 
@@ -151,9 +151,6 @@ void sctp_packet_free(struct sctp_packet *packet)
                list_del_init(&chunk->list);
                sctp_chunk_free(chunk);
        }
-
-       if (packet->malloced)
-               kfree(packet);
 }
 
 /* This routine tries to append the chunk to the offered packet. If adding
index 01dca753db16a9b53c57fc4e92fca6d52e55df36..32a4625fef7798f782859f3e9f6bda2979235dd9 100644 (file)
@@ -217,8 +217,6 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
        q->outstanding_bytes = 0;
        q->empty = 1;
        q->cork  = 0;
-
-       q->malloced = 0;
        q->out_qlen = 0;
 }
 
@@ -295,10 +293,6 @@ void sctp_outq_free(struct sctp_outq *q)
 {
        /* Throw away leftover chunks. */
        __sctp_outq_teardown(q);
-
-       /* If we were kmalloc()'d, free the memory.  */
-       if (q->malloced)
-               kfree(q);
 }
 
 /* Put a new chunk in an sctp_outq.  */
@@ -707,11 +701,10 @@ redo:
 /* Cork the outqueue so queued chunks are really queued. */
 int sctp_outq_uncork(struct sctp_outq *q)
 {
-       int error = 0;
        if (q->cork)
                q->cork = 0;
-       error = sctp_outq_flush(q, 0);
-       return error;
+
+       return sctp_outq_flush(q, 0);
 }
 
 
index ab3bba8cb0a80542df0f2839874886a179ac1bbd..4e45ee35d0db149582c4435462baf253647d7774 100644 (file)
@@ -295,7 +295,8 @@ static void * sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
                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\n");
+                               "HBINT INS OUTS MAXRT T1X T2X RTXC "
+                               "wmema wmemq sndbuf rcvbuf\n");
 
        return (void *)pos;
 }
@@ -349,11 +350,16 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
                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 ",
+               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);
+                       assoc->rtx_data_chunks,
+                       atomic_read(&sk->sk_wmem_alloc),
+                       sk->sk_wmem_queued,
+                       sk->sk_sndbuf,
+                       sk->sk_rcvbuf);
                seq_printf(seq, "\n");
        }
        read_unlock(&head->lock);
index b9070736b8d9a24383be81b1764d191e854d3e63..f631c5ff4dbfd84426d140920b15650972f34646 100644 (file)
@@ -1119,9 +1119,10 @@ static int __sctp_connect(struct sock* sk,
                /* Make sure the destination port is correctly set
                 * in all addresses.
                 */
-               if (asoc && asoc->peer.port && asoc->peer.port != port)
+               if (asoc && asoc->peer.port && asoc->peer.port != port) {
+                       err = -EINVAL;
                        goto out_free;
-
+               }
 
                /* Check if there already is a matching association on the
                 * endpoint (other than the one created here).
@@ -6185,7 +6186,8 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
 
        /* Is there any exceptional events?  */
        if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
-               mask |= POLLERR;
+               mask |= POLLERR |
+                       sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0;
        if (sk->sk_shutdown & RCV_SHUTDOWN)
                mask |= POLLRDHUP | POLLIN | POLLRDNORM;
        if (sk->sk_shutdown == SHUTDOWN_MASK)
index 825ea94415b39818f637b54c5abe112394edc322..da860352380872ad1964af228906178bf8b56531 100644 (file)
@@ -74,7 +74,6 @@ struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out,
        if (!sctp_ssnmap_init(retval, in, out))
                goto fail_map;
 
-       retval->malloced = 1;
        SCTP_DBG_OBJCNT_INC(ssnmap);
 
        return retval;
@@ -118,14 +117,16 @@ void sctp_ssnmap_clear(struct sctp_ssnmap *map)
 /* Dispose of a ssnmap.  */
 void sctp_ssnmap_free(struct sctp_ssnmap *map)
 {
-       if (map && map->malloced) {
-               int size;
-
-               size = sctp_ssnmap_size(map->in.len, map->out.len);
-               if (size <= KMALLOC_MAX_SIZE)
-                       kfree(map);
-               else
-                       free_pages((unsigned long)map, get_order(size));
-               SCTP_DBG_OBJCNT_DEC(ssnmap);
-       }
+       int size;
+
+       if (unlikely(!map))
+               return;
+
+       size = sctp_ssnmap_size(map->in.len, map->out.len);
+       if (size <= KMALLOC_MAX_SIZE)
+               kfree(map);
+       else
+               free_pages((unsigned long)map, get_order(size));
+
+       SCTP_DBG_OBJCNT_DEC(ssnmap);
 }
index fafd2a461ba0bee0f5d8d4750ae9b3b73bdac079..098f1d5f769e264b73aa8b3800ccf3a4eeb75c92 100644 (file)
@@ -123,7 +123,6 @@ struct sctp_transport *sctp_transport_new(struct net *net,
        if (!sctp_transport_init(net, transport, addr, gfp))
                goto fail_init;
 
-       transport->malloced = 1;
        SCTP_DBG_OBJCNT_INC(transport);
 
        return transport;
index 0fd5b3d2df03158d17f0d356825209e571e419d7..04e3d470f877e51cc3238a3939595c1d90b83b37 100644 (file)
@@ -68,7 +68,6 @@ struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq,
        skb_queue_head_init(&ulpq->reasm);
        skb_queue_head_init(&ulpq->lobby);
        ulpq->pd_mode  = 0;
-       ulpq->malloced = 0;
 
        return ulpq;
 }
@@ -96,8 +95,6 @@ void sctp_ulpq_flush(struct sctp_ulpq *ulpq)
 void sctp_ulpq_free(struct sctp_ulpq *ulpq)
 {
        sctp_ulpq_flush(ulpq);
-       if (ulpq->malloced)
-               kfree(ulpq);
 }
 
 /* Process an incoming DATA chunk.  */
index 88f759adf3afba28af3b1a694220c18f5e895616..280283f03cccee4c23055103a5586bb94a49809b 100644 (file)
@@ -600,7 +600,7 @@ void sock_release(struct socket *sock)
 }
 EXPORT_SYMBOL(sock_release);
 
-int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags)
+void sock_tx_timestamp(struct sock *sk, __u8 *tx_flags)
 {
        *tx_flags = 0;
        if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE))
@@ -609,7 +609,6 @@ int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags)
                *tx_flags |= SKBTX_SW_TSTAMP;
        if (sock_flag(sk, SOCK_WIFI_STATUS))
                *tx_flags |= SKBTX_WIFI_STATUS;
-       return 0;
 }
 EXPORT_SYMBOL(sock_tx_timestamp);
 
@@ -682,16 +681,6 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
 }
 EXPORT_SYMBOL(kernel_sendmsg);
 
-static int ktime2ts(ktime_t kt, struct timespec *ts)
-{
-       if (kt.tv64) {
-               *ts = ktime_to_timespec(kt);
-               return 1;
-       } else {
-               return 0;
-       }
-}
-
 /*
  * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
  */
@@ -724,17 +713,15 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
 
 
        memset(ts, 0, sizeof(ts));
-       if (skb->tstamp.tv64 &&
-           sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) {
-               skb_get_timestampns(skb, ts + 0);
+       if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE) &&
+           ktime_to_timespec_cond(skb->tstamp, ts + 0))
                empty = 0;
-       }
        if (shhwtstamps) {
                if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE) &&
-                   ktime2ts(shhwtstamps->syststamp, ts + 1))
+                   ktime_to_timespec_cond(shhwtstamps->syststamp, ts + 1))
                        empty = 0;
                if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE) &&
-                   ktime2ts(shhwtstamps->hwtstamp, ts + 2))
+                   ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts + 2))
                        empty = 0;
        }
        if (!empty)
index 4f99600a5fed7b6ad702d540e371e89248c76a1f..c890848f9d560fc4cda305653c08f53c712dfbe2 100644 (file)
@@ -31,3 +31,10 @@ config TIPC_PORTS
 
          Setting this to a smaller value saves some memory,
          setting it to higher allows for more ports.
+
+config TIPC_MEDIA_IB
+       bool "InfiniBand media type support"
+       depends on TIPC && INFINIBAND_IPOIB
+       help
+         Saying Y here will enable support for running TIPC on
+         IP-over-InfiniBand devices.
index 6cd55d671d3abf4283f749897474058d705de3b7..4df8e02d900823cbb417e1f032bee672f9081966 100644 (file)
@@ -9,3 +9,5 @@ tipc-y  += addr.o bcast.o bearer.o config.o \
           name_distr.o  subscr.o name_table.o net.o  \
           netlink.o node.o node_subscr.o port.o ref.o  \
           socket.o log.o eth_media.o
+
+tipc-$(CONFIG_TIPC_MEDIA_IB)   += ib_media.o
index 2655c9f4ecad0a5fd91ba76058d47be8a65ff148..25e159c2feb486a25f026ec8029c807043d83fd8 100644 (file)
@@ -620,10 +620,10 @@ static int tipc_bcbearer_send(struct sk_buff *buf,
                        continue;       /* bearer pair doesn't add anything */
 
                if (!tipc_bearer_blocked(p))
-                       tipc_bearer_send(p, buf, &p->media->bcast_addr);
+                       tipc_bearer_send(p, buf, &p->bcast_addr);
                else if (s && !tipc_bearer_blocked(s))
                        /* unable to send on primary bearer */
-                       tipc_bearer_send(s, buf, &s->media->bcast_addr);
+                       tipc_bearer_send(s, buf, &s->bcast_addr);
                else
                        /* unable to send on either bearer */
                        continue;
index aa62f93a91275b355b994c66318fddb8b42e1af6..cb29ef7ba2f0b1d285c079d87ddf8c5a07c118af 100644 (file)
@@ -39,7 +39,7 @@
 #include "bearer.h"
 #include "discover.h"
 
-#define MAX_ADDR_STR 32
+#define MAX_ADDR_STR 60
 
 static struct tipc_media *media_list[MAX_MEDIA];
 static u32 media_count;
@@ -89,9 +89,6 @@ int tipc_register_media(struct tipc_media *m_ptr)
 
        if ((strlen(m_ptr->name) + 1) > TIPC_MAX_MEDIA_NAME)
                goto exit;
-       if ((m_ptr->bcast_addr.media_id != m_ptr->type_id) ||
-           !m_ptr->bcast_addr.broadcast)
-               goto exit;
        if (m_ptr->priority > TIPC_MAX_LINK_PRI)
                goto exit;
        if ((m_ptr->tolerance < TIPC_MIN_LINK_TOL) ||
@@ -407,7 +404,7 @@ restart:
        INIT_LIST_HEAD(&b_ptr->links);
        spin_lock_init(&b_ptr->lock);
 
-       res = tipc_disc_create(b_ptr, &m_ptr->bcast_addr, disc_domain);
+       res = tipc_disc_create(b_ptr, &b_ptr->bcast_addr, disc_domain);
        if (res) {
                bearer_disable(b_ptr);
                pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
index 39f1192d04bff34dfd2295cbe99b491d603e7cbb..09c869adcfcf4fd7f3e6cac8423c4e3ccd7762c1 100644 (file)
@@ -56,6 +56,7 @@
  * Identifiers of supported TIPC media types
  */
 #define TIPC_MEDIA_TYPE_ETH    1
+#define TIPC_MEDIA_TYPE_IB     2
 
 /**
  * struct tipc_media_addr - destination address used by TIPC bearers
@@ -77,7 +78,6 @@ struct tipc_bearer;
  * @enable_bearer: routine which enables a bearer
  * @disable_bearer: routine which disables a bearer
  * @addr2str: routine which converts media address to string
- * @str2addr: routine which converts media address from string
  * @addr2msg: routine which converts media address to protocol message area
  * @msg2addr: routine which converts media address from protocol message area
  * @bcast_addr: media address used in broadcasting
@@ -94,10 +94,9 @@ struct tipc_media {
        int (*enable_bearer)(struct tipc_bearer *b_ptr);
        void (*disable_bearer)(struct tipc_bearer *b_ptr);
        int (*addr2str)(struct tipc_media_addr *a, char *str_buf, int str_size);
-       int (*str2addr)(struct tipc_media_addr *a, char *str_buf);
        int (*addr2msg)(struct tipc_media_addr *a, char *msg_area);
-       int (*msg2addr)(struct tipc_media_addr *a, char *msg_area);
-       struct tipc_media_addr bcast_addr;
+       int (*msg2addr)(const struct tipc_bearer *b_ptr,
+                       struct tipc_media_addr *a, char *msg_area);
        u32 priority;
        u32 tolerance;
        u32 window;
@@ -136,6 +135,7 @@ struct tipc_bearer {
        char name[TIPC_MAX_BEARER_NAME];
        spinlock_t lock;
        struct tipc_media *media;
+       struct tipc_media_addr bcast_addr;
        u32 priority;
        u32 window;
        u32 tolerance;
@@ -175,6 +175,14 @@ int tipc_disable_bearer(const char *name);
 int  tipc_eth_media_start(void);
 void tipc_eth_media_stop(void);
 
+#ifdef CONFIG_TIPC_MEDIA_IB
+int  tipc_ib_media_start(void);
+void tipc_ib_media_stop(void);
+#else
+static inline int tipc_ib_media_start(void) { return 0; }
+static inline void tipc_ib_media_stop(void) { return; }
+#endif
+
 int tipc_media_set_priority(const char *name, u32 new_value);
 int tipc_media_set_window(const char *name, u32 new_value);
 void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a);
index fc05cecd7481652dd57431727dc2355d39627859..7ec2c1eb94f17a40db04bb004a6abf35f2fc25cf 100644 (file)
@@ -82,6 +82,7 @@ static void tipc_core_stop_net(void)
 {
        tipc_net_stop();
        tipc_eth_media_stop();
+       tipc_ib_media_stop();
 }
 
 /**
@@ -93,8 +94,15 @@ int tipc_core_start_net(unsigned long addr)
 
        tipc_net_start(addr);
        res = tipc_eth_media_start();
-       if (res)
-               tipc_core_stop_net();
+       if (res < 0)
+               goto err;
+       res = tipc_ib_media_start();
+       if (res < 0)
+               goto err;
+       return res;
+
+err:
+       tipc_core_stop_net();
        return res;
 }
 
index 1074b9587e81ed57a77bc20042c829be18bfc4a0..eedff58d03877c6eaae13feb6a5cab2508659a10 100644 (file)
@@ -129,7 +129,7 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
        int link_fully_up;
 
        media_addr.broadcast = 1;
-       b_ptr->media->msg2addr(&media_addr, msg_media_addr(msg));
+       b_ptr->media->msg2addr(b_ptr, &media_addr, msg_media_addr(msg));
        kfree_skb(buf);
 
        /* Ensure message from node is valid and communication is permitted */
index 2132c1ef2951aa3c907e0650805f41aa88448a9b..120a676a3360173311a7acb0a6499f0db921d5d9 100644 (file)
@@ -77,12 +77,13 @@ static struct notifier_block notifier = {
  * Media-dependent "value" field stores MAC address in first 6 bytes
  * and zeroes out the remaining bytes.
  */
-static void eth_media_addr_set(struct tipc_media_addr *a, char *mac)
+static void eth_media_addr_set(const struct tipc_bearer *tb_ptr,
+                              struct tipc_media_addr *a, char *mac)
 {
        memcpy(a->value, mac, ETH_ALEN);
        memset(a->value + ETH_ALEN, 0, sizeof(a->value) - ETH_ALEN);
        a->media_id = TIPC_MEDIA_TYPE_ETH;
-       a->broadcast = !memcmp(mac, eth_media_info.bcast_addr.value, ETH_ALEN);
+       a->broadcast = !memcmp(mac, tb_ptr->bcast_addr.value, ETH_ALEN);
 }
 
 /**
@@ -110,6 +111,7 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr,
 
        skb_reset_network_header(clone);
        clone->dev = dev;
+       clone->protocol = htons(ETH_P_TIPC);
        dev_hard_header(clone, dev, ETH_P_TIPC, dest->value,
                        dev->dev_addr, clone->len);
        dev_queue_xmit(clone);
@@ -201,9 +203,13 @@ static int enable_bearer(struct tipc_bearer *tb_ptr)
        /* Associate TIPC bearer with Ethernet bearer */
        eb_ptr->bearer = tb_ptr;
        tb_ptr->usr_handle = (void *)eb_ptr;
+       memset(tb_ptr->bcast_addr.value, 0, sizeof(tb_ptr->bcast_addr.value));
+       memcpy(tb_ptr->bcast_addr.value, dev->broadcast, ETH_ALEN);
+       tb_ptr->bcast_addr.media_id = TIPC_MEDIA_TYPE_ETH;
+       tb_ptr->bcast_addr.broadcast = 1;
        tb_ptr->mtu = dev->mtu;
        tb_ptr->blocked = 0;
-       eth_media_addr_set(&tb_ptr->addr, (char *)dev->dev_addr);
+       eth_media_addr_set(tb_ptr, &tb_ptr->addr, (char *)dev->dev_addr);
        return 0;
 }
 
@@ -301,25 +307,6 @@ static int eth_addr2str(struct tipc_media_addr *a, char *str_buf, int str_size)
        return 0;
 }
 
-/**
- * eth_str2addr - convert string to Ethernet address
- */
-static int eth_str2addr(struct tipc_media_addr *a, char *str_buf)
-{
-       char mac[ETH_ALEN];
-       int r;
-
-       r = sscanf(str_buf, "%02x:%02x:%02x:%02x:%02x:%02x",
-                      (u32 *)&mac[0], (u32 *)&mac[1], (u32 *)&mac[2],
-                      (u32 *)&mac[3], (u32 *)&mac[4], (u32 *)&mac[5]);
-
-       if (r != ETH_ALEN)
-               return 1;
-
-       eth_media_addr_set(a, mac);
-       return 0;
-}
-
 /**
  * eth_str2addr - convert Ethernet address format to message header format
  */
@@ -334,12 +321,13 @@ static int eth_addr2msg(struct tipc_media_addr *a, char *msg_area)
 /**
  * eth_str2addr - convert message header address format to Ethernet format
  */
-static int eth_msg2addr(struct tipc_media_addr *a, char *msg_area)
+static int eth_msg2addr(const struct tipc_bearer *tb_ptr,
+                       struct tipc_media_addr *a, char *msg_area)
 {
        if (msg_area[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_ETH)
                return 1;
 
-       eth_media_addr_set(a, msg_area + ETH_ADDR_OFFSET);
+       eth_media_addr_set(tb_ptr, a, msg_area + ETH_ADDR_OFFSET);
        return 0;
 }
 
@@ -351,11 +339,8 @@ static struct tipc_media eth_media_info = {
        .enable_bearer  = enable_bearer,
        .disable_bearer = disable_bearer,
        .addr2str       = eth_addr2str,
-       .str2addr       = eth_str2addr,
        .addr2msg       = eth_addr2msg,
        .msg2addr       = eth_msg2addr,
-       .bcast_addr     = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
-                           TIPC_MEDIA_TYPE_ETH, 1 },
        .priority       = TIPC_DEF_LINK_PRI,
        .tolerance      = TIPC_DEF_LINK_TOL,
        .window         = TIPC_DEF_LINK_WIN,
diff --git a/net/tipc/ib_media.c b/net/tipc/ib_media.c
new file mode 100644 (file)
index 0000000..2a2864c
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * net/tipc/ib_media.c: Infiniband bearer support for TIPC
+ *
+ * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
+ *
+ * Based on eth_media.c, which carries the following copyright notice:
+ *
+ * Copyright (c) 2001-2007, Ericsson AB
+ * Copyright (c) 2005-2008, 2011, Wind River Systems
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the 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/if_infiniband.h>
+#include "core.h"
+#include "bearer.h"
+
+#define MAX_IB_BEARERS         MAX_BEARERS
+
+/**
+ * struct ib_bearer - Infiniband bearer data structure
+ * @bearer: ptr to associated "generic" bearer structure
+ * @dev: ptr to associated Infiniband network device
+ * @tipc_packet_type: used in binding TIPC to Infiniband driver
+ * @cleanup: work item used when disabling bearer
+ */
+
+struct ib_bearer {
+       struct tipc_bearer *bearer;
+       struct net_device *dev;
+       struct packet_type tipc_packet_type;
+       struct work_struct setup;
+       struct work_struct cleanup;
+};
+
+static struct tipc_media ib_media_info;
+static struct ib_bearer ib_bearers[MAX_IB_BEARERS];
+static int ib_started;
+
+/**
+ * ib_media_addr_set - initialize Infiniband media address structure
+ *
+ * Media-dependent "value" field stores MAC address in first 6 bytes
+ * and zeroes out the remaining bytes.
+ */
+static void ib_media_addr_set(const struct tipc_bearer *tb_ptr,
+                             struct tipc_media_addr *a, char *mac)
+{
+       BUILD_BUG_ON(sizeof(a->value) < INFINIBAND_ALEN);
+       memcpy(a->value, mac, INFINIBAND_ALEN);
+       a->media_id = TIPC_MEDIA_TYPE_IB;
+       a->broadcast = !memcmp(mac, tb_ptr->bcast_addr.value, INFINIBAND_ALEN);
+}
+
+/**
+ * send_msg - send a TIPC message out over an InfiniBand interface
+ */
+static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr,
+                   struct tipc_media_addr *dest)
+{
+       struct sk_buff *clone;
+       struct net_device *dev;
+       int delta;
+
+       clone = skb_clone(buf, GFP_ATOMIC);
+       if (!clone)
+               return 0;
+
+       dev = ((struct ib_bearer *)(tb_ptr->usr_handle))->dev;
+       delta = dev->hard_header_len - skb_headroom(buf);
+
+       if ((delta > 0) &&
+           pskb_expand_head(clone, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
+               kfree_skb(clone);
+               return 0;
+       }
+
+       skb_reset_network_header(clone);
+       clone->dev = dev;
+       clone->protocol = htons(ETH_P_TIPC);
+       dev_hard_header(clone, dev, ETH_P_TIPC, dest->value,
+                       dev->dev_addr, clone->len);
+       dev_queue_xmit(clone);
+       return 0;
+}
+
+/**
+ * recv_msg - handle incoming TIPC message from an InfiniBand interface
+ *
+ * Accept only packets explicitly sent to this node, or broadcast packets;
+ * ignores packets sent using InfiniBand multicast, and traffic sent to other
+ * nodes (which can happen if interface is running in promiscuous mode).
+ */
+static int recv_msg(struct sk_buff *buf, struct net_device *dev,
+                   struct packet_type *pt, struct net_device *orig_dev)
+{
+       struct ib_bearer *ib_ptr = (struct ib_bearer *)pt->af_packet_priv;
+
+       if (!net_eq(dev_net(dev), &init_net)) {
+               kfree_skb(buf);
+               return 0;
+       }
+
+       if (likely(ib_ptr->bearer)) {
+               if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
+                       buf->next = NULL;
+                       tipc_recv_msg(buf, ib_ptr->bearer);
+                       return 0;
+               }
+       }
+       kfree_skb(buf);
+       return 0;
+}
+
+/**
+ * setup_bearer - setup association between InfiniBand bearer and interface
+ */
+static void setup_bearer(struct work_struct *work)
+{
+       struct ib_bearer *ib_ptr =
+               container_of(work, struct ib_bearer, setup);
+
+       dev_add_pack(&ib_ptr->tipc_packet_type);
+}
+
+/**
+ * enable_bearer - attach TIPC bearer to an InfiniBand interface
+ */
+static int enable_bearer(struct tipc_bearer *tb_ptr)
+{
+       struct net_device *dev = NULL;
+       struct net_device *pdev = NULL;
+       struct ib_bearer *ib_ptr = &ib_bearers[0];
+       struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS];
+       char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1;
+       int pending_dev = 0;
+
+       /* Find unused InfiniBand bearer structure */
+       while (ib_ptr->dev) {
+               if (!ib_ptr->bearer)
+                       pending_dev++;
+               if (++ib_ptr == stop)
+                       return pending_dev ? -EAGAIN : -EDQUOT;
+       }
+
+       /* Find device with specified name */
+       read_lock(&dev_base_lock);
+       for_each_netdev(&init_net, pdev) {
+               if (!strncmp(pdev->name, driver_name, IFNAMSIZ)) {
+                       dev = pdev;
+                       dev_hold(dev);
+                       break;
+               }
+       }
+       read_unlock(&dev_base_lock);
+       if (!dev)
+               return -ENODEV;
+
+       /* Create InfiniBand bearer for device */
+       ib_ptr->dev = dev;
+       ib_ptr->tipc_packet_type.type = htons(ETH_P_TIPC);
+       ib_ptr->tipc_packet_type.dev = dev;
+       ib_ptr->tipc_packet_type.func = recv_msg;
+       ib_ptr->tipc_packet_type.af_packet_priv = ib_ptr;
+       INIT_LIST_HEAD(&(ib_ptr->tipc_packet_type.list));
+       INIT_WORK(&ib_ptr->setup, setup_bearer);
+       schedule_work(&ib_ptr->setup);
+
+       /* Associate TIPC bearer with InfiniBand bearer */
+       ib_ptr->bearer = tb_ptr;
+       tb_ptr->usr_handle = (void *)ib_ptr;
+       memset(tb_ptr->bcast_addr.value, 0, sizeof(tb_ptr->bcast_addr.value));
+       memcpy(tb_ptr->bcast_addr.value, dev->broadcast, INFINIBAND_ALEN);
+       tb_ptr->bcast_addr.media_id = TIPC_MEDIA_TYPE_IB;
+       tb_ptr->bcast_addr.broadcast = 1;
+       tb_ptr->mtu = dev->mtu;
+       tb_ptr->blocked = 0;
+       ib_media_addr_set(tb_ptr, &tb_ptr->addr, (char *)dev->dev_addr);
+       return 0;
+}
+
+/**
+ * cleanup_bearer - break association between InfiniBand bearer and interface
+ *
+ * This routine must be invoked from a work queue because it can sleep.
+ */
+static void cleanup_bearer(struct work_struct *work)
+{
+       struct ib_bearer *ib_ptr =
+               container_of(work, struct ib_bearer, cleanup);
+
+       dev_remove_pack(&ib_ptr->tipc_packet_type);
+       dev_put(ib_ptr->dev);
+       ib_ptr->dev = NULL;
+}
+
+/**
+ * disable_bearer - detach TIPC bearer from an InfiniBand interface
+ *
+ * Mark InfiniBand bearer as inactive so that incoming buffers are thrown away,
+ * then get worker thread to complete bearer cleanup.  (Can't do cleanup
+ * here because cleanup code needs to sleep and caller holds spinlocks.)
+ */
+static void disable_bearer(struct tipc_bearer *tb_ptr)
+{
+       struct ib_bearer *ib_ptr = (struct ib_bearer *)tb_ptr->usr_handle;
+
+       ib_ptr->bearer = NULL;
+       INIT_WORK(&ib_ptr->cleanup, cleanup_bearer);
+       schedule_work(&ib_ptr->cleanup);
+}
+
+/**
+ * recv_notification - handle device updates from OS
+ *
+ * Change the state of the InfiniBand bearer (if any) associated with the
+ * specified device.
+ */
+static int recv_notification(struct notifier_block *nb, unsigned long evt,
+                            void *dv)
+{
+       struct net_device *dev = (struct net_device *)dv;
+       struct ib_bearer *ib_ptr = &ib_bearers[0];
+       struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS];
+
+       if (!net_eq(dev_net(dev), &init_net))
+               return NOTIFY_DONE;
+
+       while ((ib_ptr->dev != dev)) {
+               if (++ib_ptr == stop)
+                       return NOTIFY_DONE;     /* couldn't find device */
+       }
+       if (!ib_ptr->bearer)
+               return NOTIFY_DONE;             /* bearer had been disabled */
+
+       ib_ptr->bearer->mtu = dev->mtu;
+
+       switch (evt) {
+       case NETDEV_CHANGE:
+               if (netif_carrier_ok(dev))
+                       tipc_continue(ib_ptr->bearer);
+               else
+                       tipc_block_bearer(ib_ptr->bearer->name);
+               break;
+       case NETDEV_UP:
+               tipc_continue(ib_ptr->bearer);
+               break;
+       case NETDEV_DOWN:
+               tipc_block_bearer(ib_ptr->bearer->name);
+               break;
+       case NETDEV_CHANGEMTU:
+       case NETDEV_CHANGEADDR:
+               tipc_block_bearer(ib_ptr->bearer->name);
+               tipc_continue(ib_ptr->bearer);
+               break;
+       case NETDEV_UNREGISTER:
+       case NETDEV_CHANGENAME:
+               tipc_disable_bearer(ib_ptr->bearer->name);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block notifier = {
+       .notifier_call  = recv_notification,
+       .priority       = 0,
+};
+
+/**
+ * ib_addr2str - convert InfiniBand address to string
+ */
+static int ib_addr2str(struct tipc_media_addr *a, char *str_buf, int str_size)
+{
+       if (str_size < 60)      /* 60 = 19 * strlen("xx:") + strlen("xx\0") */
+               return 1;
+
+       sprintf(str_buf, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
+                        "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+               a->value[0], a->value[1], a->value[2], a->value[3],
+               a->value[4], a->value[5], a->value[6], a->value[7],
+               a->value[8], a->value[9], a->value[10], a->value[11],
+               a->value[12], a->value[13], a->value[14], a->value[15],
+               a->value[16], a->value[17], a->value[18], a->value[19]);
+
+       return 0;
+}
+
+/**
+ * ib_addr2msg - convert InfiniBand address format to message header format
+ */
+static int ib_addr2msg(struct tipc_media_addr *a, char *msg_area)
+{
+       memset(msg_area, 0, TIPC_MEDIA_ADDR_SIZE);
+       msg_area[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_IB;
+       memcpy(msg_area, a->value, INFINIBAND_ALEN);
+       return 0;
+}
+
+/**
+ * ib_msg2addr - convert message header address format to InfiniBand format
+ */
+static int ib_msg2addr(const struct tipc_bearer *tb_ptr,
+                      struct tipc_media_addr *a, char *msg_area)
+{
+       ib_media_addr_set(tb_ptr, a, msg_area);
+       return 0;
+}
+
+/*
+ * InfiniBand media registration info
+ */
+static struct tipc_media ib_media_info = {
+       .send_msg       = send_msg,
+       .enable_bearer  = enable_bearer,
+       .disable_bearer = disable_bearer,
+       .addr2str       = ib_addr2str,
+       .addr2msg       = ib_addr2msg,
+       .msg2addr       = ib_msg2addr,
+       .priority       = TIPC_DEF_LINK_PRI,
+       .tolerance      = TIPC_DEF_LINK_TOL,
+       .window         = TIPC_DEF_LINK_WIN,
+       .type_id        = TIPC_MEDIA_TYPE_IB,
+       .name           = "ib"
+};
+
+/**
+ * tipc_ib_media_start - activate InfiniBand bearer support
+ *
+ * Register InfiniBand media type with TIPC bearer code.  Also register
+ * with OS for notifications about device state changes.
+ */
+int tipc_ib_media_start(void)
+{
+       int res;
+
+       if (ib_started)
+               return -EINVAL;
+
+       res = tipc_register_media(&ib_media_info);
+       if (res)
+               return res;
+
+       res = register_netdevice_notifier(&notifier);
+       if (!res)
+               ib_started = 1;
+       return res;
+}
+
+/**
+ * tipc_ib_media_stop - deactivate InfiniBand bearer support
+ */
+void tipc_ib_media_stop(void)
+{
+       if (!ib_started)
+               return;
+
+       flush_scheduled_work();
+       unregister_netdevice_notifier(&notifier);
+       ib_started = 0;
+}
index 6675914dc592cd54b13e6320051f80296a7223c8..8bcd4985d0fb341f795346f06c55d1f059c4c643 100644 (file)
@@ -44,7 +44,7 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
        struct nlmsghdr *rep_nlh;
        struct nlmsghdr *req_nlh = info->nlhdr;
        struct tipc_genlmsghdr *req_userhdr = info->userhdr;
-       int hdr_space = NLMSG_SPACE(GENL_HDRLEN + TIPC_GENL_HDRLEN);
+       int hdr_space = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
        u16 cmd;
 
        if ((req_userhdr->cmd & 0xC000) && (!capable(CAP_NET_ADMIN)))
@@ -53,8 +53,8 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
                cmd = req_userhdr->cmd;
 
        rep_buf = tipc_cfg_do_cmd(req_userhdr->dest, cmd,
-                       NLMSG_DATA(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN,
-                       NLMSG_PAYLOAD(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN),
+                       nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN,
+                       nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN),
                        hdr_space);
 
        if (rep_buf) {
index 2db702d82e7d16fea7a27a82cc7c5dc3d3d95726..9efe01113c5c5bc898dd6b26a4e0ced9733e1c01 100644 (file)
@@ -1340,7 +1340,6 @@ static void unix_destruct_scm(struct sk_buff *skb)
        struct scm_cookie scm;
        memset(&scm, 0, sizeof(scm));
        scm.pid  = UNIXCB(skb).pid;
-       scm.cred = UNIXCB(skb).cred;
        if (UNIXCB(skb).fp)
                unix_detach_fds(&scm, skb);
 
@@ -1391,8 +1390,8 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
        int err = 0;
 
        UNIXCB(skb).pid  = get_pid(scm->pid);
-       if (scm->cred)
-               UNIXCB(skb).cred = get_cred(scm->cred);
+       UNIXCB(skb).uid = scm->creds.uid;
+       UNIXCB(skb).gid = scm->creds.gid;
        UNIXCB(skb).fp = NULL;
        if (scm->fp && send_fds)
                err = unix_attach_fds(scm, skb);
@@ -1409,13 +1408,13 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
 static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
                            const struct sock *other)
 {
-       if (UNIXCB(skb).cred)
+       if (UNIXCB(skb).pid)
                return;
        if (test_bit(SOCK_PASSCRED, &sock->flags) ||
            !other->sk_socket ||
            test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) {
                UNIXCB(skb).pid  = get_pid(task_tgid(current));
-               UNIXCB(skb).cred = get_current_cred();
+               current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
        }
 }
 
@@ -1819,7 +1818,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
                siocb->scm = &tmp_scm;
                memset(&tmp_scm, 0, sizeof(tmp_scm));
        }
-       scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
+       scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
        unix_set_secdata(siocb->scm, skb);
 
        if (!(flags & MSG_PEEK)) {
@@ -1991,11 +1990,12 @@ again:
                if (check_creds) {
                        /* Never glue messages from different writers */
                        if ((UNIXCB(skb).pid  != siocb->scm->pid) ||
-                           (UNIXCB(skb).cred != siocb->scm->cred))
+                           !uid_eq(UNIXCB(skb).uid, siocb->scm->creds.uid) ||
+                           !gid_eq(UNIXCB(skb).gid, siocb->scm->creds.gid))
                                break;
                } else if (test_bit(SOCK_PASSCRED, &sock->flags)) {
                        /* Copy credentials */
-                       scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
+                       scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
                        check_creds = 1;
                }
 
@@ -2196,7 +2196,9 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,
 
        /* exceptional events? */
        if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
-               mask |= POLLERR;
+               mask |= POLLERR |
+                       (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
+
        if (sk->sk_shutdown & RCV_SHUTDOWN)
                mask |= POLLRDHUP | POLLIN | POLLRDNORM;
        if (sk->sk_shutdown == SHUTDOWN_MASK)
index 5e04d3d96285daaa01ff8d35e58081ce4807f093..daff75200e256705b3e0e9605cf6e6baf6ac17e3 100644 (file)
@@ -123,6 +123,14 @@ static s32 vmci_transport_error_to_vsock_error(s32 vmci_error)
        return err > 0 ? -err : err;
 }
 
+static u32 vmci_transport_peer_rid(u32 peer_cid)
+{
+       if (VMADDR_CID_HYPERVISOR == peer_cid)
+               return VMCI_TRANSPORT_HYPERVISOR_PACKET_RID;
+
+       return VMCI_TRANSPORT_PACKET_RID;
+}
+
 static inline void
 vmci_transport_packet_init(struct vmci_transport_packet *pkt,
                           struct sockaddr_vm *src,
@@ -140,7 +148,7 @@ vmci_transport_packet_init(struct vmci_transport_packet *pkt,
        pkt->dg.src = vmci_make_handle(VMADDR_CID_ANY,
                                       VMCI_TRANSPORT_PACKET_RID);
        pkt->dg.dst = vmci_make_handle(dst->svm_cid,
-                                      VMCI_TRANSPORT_PACKET_RID);
+                                      vmci_transport_peer_rid(dst->svm_cid));
        pkt->dg.payload_size = sizeof(*pkt) - sizeof(pkt->dg);
        pkt->version = VMCI_TRANSPORT_PACKET_VERSION;
        pkt->type = type;
@@ -508,6 +516,9 @@ static bool vmci_transport_is_trusted(struct vsock_sock *vsock, u32 peer_cid)
 
 static bool vmci_transport_allow_dgram(struct vsock_sock *vsock, u32 peer_cid)
 {
+       if (VMADDR_CID_HYPERVISOR == peer_cid)
+               return true;
+
        if (vsock->cached_peer != peer_cid) {
                vsock->cached_peer = peer_cid;
                if (!vmci_transport_is_trusted(vsock, peer_cid) &&
@@ -628,7 +639,6 @@ static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg)
 static bool vmci_transport_stream_allow(u32 cid, u32 port)
 {
        static const u32 non_socket_contexts[] = {
-               VMADDR_CID_HYPERVISOR,
                VMADDR_CID_RESERVED,
        };
        int i;
@@ -667,7 +677,7 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
         */
 
        if (!vmci_transport_stream_allow(dg->src.context, -1)
-           || VMCI_TRANSPORT_PACKET_RID != dg->src.resource)
+           || vmci_transport_peer_rid(dg->src.context) != dg->src.resource)
                return VMCI_ERROR_NO_ACCESS;
 
        if (VMCI_DG_SIZE(dg) < sizeof(*pkt))
index 1bf991803ec0ddb0d7147f09f832effcd7670d75..fd88ea8924e4dd89d8f0dc7939e5ccc75e39a747 100644 (file)
@@ -28,6 +28,9 @@
 /* The resource ID on which control packets are sent. */
 #define VMCI_TRANSPORT_PACKET_RID 1
 
+/* The resource ID on which control packets are sent to the hypervisor. */
+#define VMCI_TRANSPORT_HYPERVISOR_PACKET_RID 15
+
 #define VSOCK_PROTO_INVALID        0
 #define VSOCK_PROTO_PKT_ON_NOTIFY (1 << 0)
 #define VSOCK_PROTO_ALL_SUPPORTED (VSOCK_PROTO_PKT_ON_NOTIFY)
index a4a14e8f55cc42f2c77146f73b4c55b8434337b5..324e8d851dc4ca67fd0ded84a927e782c3647c8c 100644 (file)
@@ -46,65 +46,3 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
 
        return err;
 }
-
-void cfg80211_ch_switch_notify(struct net_device *dev,
-                              struct cfg80211_chan_def *chandef)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_ch_switch_notify(dev, chandef);
-
-       wdev_lock(wdev);
-
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO))
-               goto out;
-
-       wdev->channel = chandef->chan;
-       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
-out:
-       wdev_unlock(wdev);
-       return;
-}
-EXPORT_SYMBOL(cfg80211_ch_switch_notify);
-
-bool cfg80211_rx_spurious_frame(struct net_device *dev,
-                               const u8 *addr, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       bool ret;
-
-       trace_cfg80211_rx_spurious_frame(dev, addr);
-
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
-               trace_cfg80211_return_bool(false);
-               return false;
-       }
-       ret = nl80211_unexpected_frame(dev, addr, gfp);
-       trace_cfg80211_return_bool(ret);
-       return ret;
-}
-EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
-
-bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
-                                       const u8 *addr, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       bool ret;
-
-       trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
-
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
-                   wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
-               trace_cfg80211_return_bool(false);
-               return false;
-       }
-       ret = nl80211_unexpected_4addr_frame(dev, addr, gfp);
-       trace_cfg80211_return_bool(ret);
-       return ret;
-}
-EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
index 6ddf74f0ae1e5ace4346f3c0cf6b4f1ce13fa580..84c9ad7e1dca0de703c7517774be813be7b5be2f 100644 (file)
@@ -842,6 +842,46 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                rdev->num_running_monitor_ifaces += num;
 }
 
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+                  struct wireless_dev *wdev)
+{
+       struct net_device *dev = wdev->netdev;
+
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_ADHOC:
+               cfg80211_leave_ibss(rdev, dev, true);
+               break;
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_STATION:
+               mutex_lock(&rdev->sched_scan_mtx);
+               __cfg80211_stop_sched_scan(rdev, false);
+               mutex_unlock(&rdev->sched_scan_mtx);
+
+               wdev_lock(wdev);
+#ifdef CONFIG_CFG80211_WEXT
+               kfree(wdev->wext.ie);
+               wdev->wext.ie = NULL;
+               wdev->wext.ie_len = 0;
+               wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+#endif
+               __cfg80211_disconnect(rdev, dev,
+                                     WLAN_REASON_DEAUTH_LEAVING, true);
+               cfg80211_mlme_down(rdev, dev);
+               wdev_unlock(wdev);
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               cfg80211_leave_mesh(rdev, dev);
+               break;
+       case NL80211_IFTYPE_AP:
+               cfg80211_stop_ap(rdev, dev);
+               break;
+       default:
+               break;
+       }
+
+       wdev->beacon_interval = 0;
+}
+
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                                         unsigned long state,
                                         void *ndev)
@@ -910,38 +950,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        dev->priv_flags |= IFF_DONT_BRIDGE;
                break;
        case NETDEV_GOING_DOWN:
-               switch (wdev->iftype) {
-               case NL80211_IFTYPE_ADHOC:
-                       cfg80211_leave_ibss(rdev, dev, true);
-                       break;
-               case NL80211_IFTYPE_P2P_CLIENT:
-               case NL80211_IFTYPE_STATION:
-                       mutex_lock(&rdev->sched_scan_mtx);
-                       __cfg80211_stop_sched_scan(rdev, false);
-                       mutex_unlock(&rdev->sched_scan_mtx);
-
-                       wdev_lock(wdev);
-#ifdef CONFIG_CFG80211_WEXT
-                       kfree(wdev->wext.ie);
-                       wdev->wext.ie = NULL;
-                       wdev->wext.ie_len = 0;
-                       wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
-#endif
-                       __cfg80211_disconnect(rdev, dev,
-                                             WLAN_REASON_DEAUTH_LEAVING, true);
-                       cfg80211_mlme_down(rdev, dev);
-                       wdev_unlock(wdev);
-                       break;
-               case NL80211_IFTYPE_MESH_POINT:
-                       cfg80211_leave_mesh(rdev, dev);
-                       break;
-               case NL80211_IFTYPE_AP:
-                       cfg80211_stop_ap(rdev, dev);
-                       break;
-               default:
-                       break;
-               }
-               wdev->beacon_interval = 0;
+               cfg80211_leave(rdev, wdev);
                break;
        case NETDEV_DOWN:
                cfg80211_update_iface_num(rdev, wdev->iftype, -1);
@@ -1117,8 +1126,10 @@ static int __init cfg80211_init(void)
                goto out_fail_reg;
 
        cfg80211_wq = create_singlethread_workqueue("cfg80211");
-       if (!cfg80211_wq)
+       if (!cfg80211_wq) {
+               err = -ENOMEM;
                goto out_fail_wq;
+       }
 
        return 0;
 
index 5845c2b37aa832b444906ad4fb0e4e74f06c3ee3..124e5e773fbc1e7fc4332b7e0ef79de669c0c784 100644 (file)
@@ -330,20 +330,15 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
 int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
                          struct net_device *dev,
                          struct ieee80211_channel *chan,
-                         const u8 *bssid, const u8 *prev_bssid,
+                         const u8 *bssid,
                          const u8 *ssid, int ssid_len,
-                         const u8 *ie, int ie_len, bool use_mfp,
-                         struct cfg80211_crypto_settings *crypt,
-                         u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
-                         struct ieee80211_ht_cap *ht_capa_mask);
+                         struct cfg80211_assoc_request *req);
 int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
-                       struct net_device *dev, struct ieee80211_channel *chan,
-                       const u8 *bssid, const u8 *prev_bssid,
+                       struct net_device *dev,
+                       struct ieee80211_channel *chan,
+                       const u8 *bssid,
                        const u8 *ssid, int ssid_len,
-                       const u8 *ie, int ie_len, bool use_mfp,
-                       struct cfg80211_crypto_settings *crypt,
-                       u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
-                       struct ieee80211_ht_cap *ht_capa_mask);
+                       struct cfg80211_assoc_request *req);
 int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
                           struct net_device *dev, const u8 *bssid,
                           const u8 *ie, int ie_len, u16 reason,
@@ -375,6 +370,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
                          bool no_cck, bool dont_wait_for_ack, u64 *cookie);
 void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
                               const struct ieee80211_ht_cap *ht_capa_mask);
+void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
+                               const struct ieee80211_vht_cap *vht_capa_mask);
 
 /* SME */
 int __cfg80211_connect(struct cfg80211_registered_device *rdev,
@@ -503,6 +500,9 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
 void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                               enum nl80211_iftype iftype, int num);
 
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+                   struct wireless_dev *wdev);
+
 void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev);
 
index 55957a284f6c7e82c9b403dae01798316224ad49..0bb93f3061a4353db1da049a384aa0cd2f9d10ab 100644 (file)
@@ -85,6 +85,7 @@ const struct mesh_setup default_mesh_setup = {
        .ie = NULL,
        .ie_len = 0,
        .is_secure = false,
+       .user_mpm = false,
        .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
        .dtim_period = MESH_DEFAULT_DTIM_PERIOD,
 };
@@ -233,20 +234,6 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
        return 0;
 }
 
-void cfg80211_notify_new_peer_candidate(struct net_device *dev,
-               const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-
-       trace_cfg80211_notify_new_peer_candidate(dev, macaddr);
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
-               return;
-
-       nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev,
-                       macaddr, ie, ie_len, gfp);
-}
-EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
-
 static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
                                 struct net_device *dev)
 {
index caddca35d6865b4506d5d98466f54c7b82eabeba..390198bf4b36d2318695603c7adb9d2e5db445d6 100644 (file)
@@ -187,30 +187,6 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
 }
 EXPORT_SYMBOL(cfg80211_send_disassoc);
 
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
-                                size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_send_unprot_deauth(dev);
-       nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC);
-}
-EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
-
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
-                                  size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_send_unprot_disassoc(dev);
-       nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC);
-}
-EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
-
 void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
@@ -367,27 +343,38 @@ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
                p1[i] &= p2[i];
 }
 
+/*  Do a logical ht_capa &= ht_capa_mask.  */
+void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
+                               const struct ieee80211_vht_cap *vht_capa_mask)
+{
+       int i;
+       u8 *p1, *p2;
+       if (!vht_capa_mask) {
+               memset(vht_capa, 0, sizeof(*vht_capa));
+               return;
+       }
+
+       p1 = (u8*)(vht_capa);
+       p2 = (u8*)(vht_capa_mask);
+       for (i = 0; i < sizeof(*vht_capa); i++)
+               p1[i] &= p2[i];
+}
+
 int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
                          struct net_device *dev,
                          struct ieee80211_channel *chan,
-                         const u8 *bssid, const u8 *prev_bssid,
+                         const u8 *bssid,
                          const u8 *ssid, int ssid_len,
-                         const u8 *ie, int ie_len, bool use_mfp,
-                         struct cfg80211_crypto_settings *crypt,
-                         u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
-                         struct ieee80211_ht_cap *ht_capa_mask)
+                         struct cfg80211_assoc_request *req)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_assoc_request req;
        int err;
        bool was_connected = false;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       memset(&req, 0, sizeof(req));
-
-       if (wdev->current_bss && prev_bssid &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, prev_bssid)) {
+       if (wdev->current_bss && req->prev_bssid &&
+           ether_addr_equal(wdev->current_bss->pub.bssid, req->prev_bssid)) {
                /*
                 * Trying to reassociate: Allow this to proceed and let the old
                 * association to be dropped when the new one is completed.
@@ -399,40 +386,30 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
        } else if (wdev->current_bss)
                return -EALREADY;
 
-       req.ie = ie;
-       req.ie_len = ie_len;
-       memcpy(&req.crypto, crypt, sizeof(req.crypto));
-       req.use_mfp = use_mfp;
-       req.prev_bssid = prev_bssid;
-       req.flags = assoc_flags;
-       if (ht_capa)
-               memcpy(&req.ht_capa, ht_capa, sizeof(req.ht_capa));
-       if (ht_capa_mask)
-               memcpy(&req.ht_capa_mask, ht_capa_mask,
-                      sizeof(req.ht_capa_mask));
-       cfg80211_oper_and_ht_capa(&req.ht_capa_mask,
+       cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
                                  rdev->wiphy.ht_capa_mod_mask);
+       cfg80211_oper_and_vht_capa(&req->vht_capa_mask,
+                                  rdev->wiphy.vht_capa_mod_mask);
 
-       req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
-                                  WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
-       if (!req.bss) {
+       req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+                                   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+       if (!req->bss) {
                if (was_connected)
                        wdev->sme_state = CFG80211_SME_CONNECTED;
                return -ENOENT;
        }
 
-       err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel,
-                                   CHAN_MODE_SHARED);
+       err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
        if (err)
                goto out;
 
-       err = rdev_assoc(rdev, dev, &req);
+       err = rdev_assoc(rdev, dev, req);
 
 out:
        if (err) {
                if (was_connected)
                        wdev->sme_state = CFG80211_SME_CONNECTED;
-               cfg80211_put_bss(&rdev->wiphy, req.bss);
+               cfg80211_put_bss(&rdev->wiphy, req->bss);
        }
 
        return err;
@@ -441,21 +418,17 @@ out:
 int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
                        struct net_device *dev,
                        struct ieee80211_channel *chan,
-                       const u8 *bssid, const u8 *prev_bssid,
+                       const u8 *bssid,
                        const u8 *ssid, int ssid_len,
-                       const u8 *ie, int ie_len, bool use_mfp,
-                       struct cfg80211_crypto_settings *crypt,
-                       u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
-                       struct ieee80211_ht_cap *ht_capa_mask)
+                       struct cfg80211_assoc_request *req)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
 
        mutex_lock(&rdev->devlist_mtx);
        wdev_lock(wdev);
-       err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
-                                   ssid, ssid_len, ie, ie_len, use_mfp, crypt,
-                                   assoc_flags, ht_capa, ht_capa_mask);
+       err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid,
+                                   ssid, ssid_len, req);
        wdev_unlock(wdev);
        mutex_unlock(&rdev->devlist_mtx);
 
@@ -577,62 +550,6 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
        }
 }
 
-void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
-                              struct ieee80211_channel *chan,
-                              unsigned int duration, gfp_t gfp)
-{
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
-       nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, duration, gfp);
-}
-EXPORT_SYMBOL(cfg80211_ready_on_channel);
-
-void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
-                                       struct ieee80211_channel *chan,
-                                       gfp_t gfp)
-{
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
-       nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, gfp);
-}
-EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
-
-void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
-                     struct station_info *sinfo, gfp_t gfp)
-{
-       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_new_sta(dev, mac_addr, sinfo);
-       nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
-}
-EXPORT_SYMBOL(cfg80211_new_sta);
-
-void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
-{
-       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_del_sta(dev, mac_addr);
-       nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp);
-}
-EXPORT_SYMBOL(cfg80211_del_sta);
-
-void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
-                         enum nl80211_connect_failed_reason reason,
-                         gfp_t gfp)
-{
-       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       nl80211_send_conn_failed_event(rdev, dev, mac_addr, reason, gfp);
-}
-EXPORT_SYMBOL(cfg80211_conn_failed);
-
 struct cfg80211_mgmt_registration {
        struct list_head list;
 
@@ -909,85 +826,6 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
 }
 EXPORT_SYMBOL(cfg80211_rx_mgmt);
 
-void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
-                            const u8 *buf, size_t len, bool ack, gfp_t gfp)
-{
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
-
-       /* Indicate TX status of the Action frame to user space */
-       nl80211_send_mgmt_tx_status(rdev, wdev, cookie, buf, len, ack, gfp);
-}
-EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
-
-void cfg80211_cqm_rssi_notify(struct net_device *dev,
-                             enum nl80211_cqm_rssi_threshold_event rssi_event,
-                             gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
-
-       /* Indicate roaming trigger event to user space */
-       nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
-}
-EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
-
-void cfg80211_cqm_pktloss_notify(struct net_device *dev,
-                                const u8 *peer, u32 num_packets, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
-
-       /* Indicate roaming trigger event to user space */
-       nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);
-}
-EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
-
-void cfg80211_cqm_txe_notify(struct net_device *dev,
-                            const u8 *peer, u32 num_packets,
-                            u32 rate, u32 intvl, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       nl80211_send_cqm_txe_notify(rdev, dev, peer, num_packets,
-                                   rate, intvl, gfp);
-}
-EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
-
-void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
-                              const u8 *replay_ctr, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_gtk_rekey_notify(dev, bssid);
-       nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
-}
-EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
-
-void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
-                                    const u8 *bssid, bool preauth, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
-       nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
-}
-EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
-
 void cfg80211_dfs_channels_update_work(struct work_struct *work)
 {
        struct delayed_work *delayed_work;
index 58e13a8c95f90a7b880a9d65530799fc02451f1e..671b69a3c13646e39e4fc00209b4d588f1d37a1a 100644 (file)
@@ -370,6 +370,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
        [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
        [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
+       [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
+       [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_VHT_CAPABILITY_MASK] = {
+               .len = NL80211_VHT_CAPABILITY_LEN,
+       },
+       [NL80211_ATTR_MDID] = { .type = NLA_U16 },
+       [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
+                                 .len = IEEE80211_MAX_DATA_LEN },
 };
 
 /* policy for the key attributes */
@@ -539,7 +547,8 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
 }
 
 static int nl80211_msg_put_channel(struct sk_buff *msg,
-                                  struct ieee80211_channel *chan)
+                                  struct ieee80211_channel *chan,
+                                  bool large)
 {
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
                        chan->center_freq))
@@ -554,9 +563,37 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
        if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
            nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
                goto nla_put_failure;
-       if ((chan->flags & IEEE80211_CHAN_RADAR) &&
-           nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
-               goto nla_put_failure;
+       if (chan->flags & IEEE80211_CHAN_RADAR) {
+               if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
+                       goto nla_put_failure;
+               if (large) {
+                       u32 time;
+
+                       time = elapsed_jiffies_msecs(chan->dfs_state_entered);
+
+                       if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
+                                       chan->dfs_state))
+                               goto nla_put_failure;
+                       if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
+                                       time))
+                               goto nla_put_failure;
+               }
+       }
+
+       if (large) {
+               if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
+                       goto nla_put_failure;
+       }
 
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
                        DBM_TO_MBM(chan->max_power)))
@@ -832,7 +869,8 @@ nla_put_failure:
 }
 
 static int nl80211_put_iface_combinations(struct wiphy *wiphy,
-                                         struct sk_buff *msg)
+                                         struct sk_buff *msg,
+                                         bool large)
 {
        struct nlattr *nl_combis;
        int i, j;
@@ -881,6 +919,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
                    nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
                                c->max_interfaces))
                        goto nla_put_failure;
+               if (large &&
+                   nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+                               c->radar_detect_widths))
+                       goto nla_put_failure;
 
                nla_nest_end(msg, nl_combi);
        }
@@ -892,412 +934,611 @@ nla_put_failure:
        return -ENOBUFS;
 }
 
-static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags,
-                             struct cfg80211_registered_device *dev)
+#ifdef CONFIG_PM
+static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
+                                       struct sk_buff *msg)
 {
-       void *hdr;
-       struct nlattr *nl_bands, *nl_band;
-       struct nlattr *nl_freqs, *nl_freq;
-       struct nlattr *nl_rates, *nl_rate;
-       struct nlattr *nl_cmds;
-       enum ieee80211_band band;
-       struct ieee80211_channel *chan;
-       struct ieee80211_rate *rate;
-       int i;
-       const struct ieee80211_txrx_stypes *mgmt_stypes =
-                               dev->wiphy.mgmt_stypes;
+       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
+       struct nlattr *nl_tcp;
 
-       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
-       if (!hdr)
-               return -1;
+       if (!tcp)
+               return 0;
 
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
-           nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) ||
-           nla_put_u32(msg, NL80211_ATTR_GENERATION,
-                       cfg80211_rdev_list_generation) ||
-           nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
-                      dev->wiphy.retry_short) ||
-           nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
-                      dev->wiphy.retry_long) ||
-           nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
-                       dev->wiphy.frag_threshold) ||
-           nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
-                       dev->wiphy.rts_threshold) ||
-           nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
-                      dev->wiphy.coverage_class) ||
-           nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
-                      dev->wiphy.max_scan_ssids) ||
-           nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
-                      dev->wiphy.max_sched_scan_ssids) ||
-           nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
-                       dev->wiphy.max_scan_ie_len) ||
-           nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
-                       dev->wiphy.max_sched_scan_ie_len) ||
-           nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
-                      dev->wiphy.max_match_sets))
-               goto nla_put_failure;
+       nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
+       if (!nl_tcp)
+               return -ENOBUFS;
 
-       if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
-           nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
-               goto nla_put_failure;
-       if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
-           nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
-               goto nla_put_failure;
-       if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
-           nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
-               goto nla_put_failure;
-       if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
-           nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
-               goto nla_put_failure;
-       if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
-           nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
-               goto nla_put_failure;
-       if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
-           nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
-               goto nla_put_failure;
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+                       tcp->data_payload_max))
+               return -ENOBUFS;
 
-       if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
-                   sizeof(u32) * dev->wiphy.n_cipher_suites,
-                   dev->wiphy.cipher_suites))
-               goto nla_put_failure;
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+                       tcp->data_payload_max))
+               return -ENOBUFS;
 
-       if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
-                      dev->wiphy.max_num_pmkids))
-               goto nla_put_failure;
+       if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
+               return -ENOBUFS;
 
-       if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
-           nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
-               goto nla_put_failure;
+       if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+                               sizeof(*tcp->tok), tcp->tok))
+               return -ENOBUFS;
 
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
-                       dev->wiphy.available_antennas_tx) ||
-           nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
-                       dev->wiphy.available_antennas_rx))
-               goto nla_put_failure;
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
+                       tcp->data_interval_max))
+               return -ENOBUFS;
 
-       if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
-           nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
-                       dev->wiphy.probe_resp_offload))
-               goto nla_put_failure;
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+                       tcp->wake_payload_max))
+               return -ENOBUFS;
 
-       if ((dev->wiphy.available_antennas_tx ||
-            dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
-               u32 tx_ant = 0, rx_ant = 0;
-               int res;
-               res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
-               if (!res) {
-                       if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
-                                       tx_ant) ||
-                           nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX,
-                                       rx_ant))
-                               goto nla_put_failure;
-               }
+       nla_nest_end(msg, nl_tcp);
+       return 0;
+}
+
+static int nl80211_send_wowlan(struct sk_buff *msg,
+                              struct cfg80211_registered_device *dev,
+                              bool large)
+{
+       struct nlattr *nl_wowlan;
+
+       if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
+               return 0;
+
+       nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
+       if (!nl_wowlan)
+               return -ENOBUFS;
+
+       if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
+           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
+               return -ENOBUFS;
+
+       if (dev->wiphy.wowlan.n_patterns) {
+               struct nl80211_wowlan_pattern_support pat = {
+                       .max_patterns = dev->wiphy.wowlan.n_patterns,
+                       .min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
+                       .max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
+                       .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
+               };
+
+               if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
+                           sizeof(pat), &pat))
+                       return -ENOBUFS;
        }
 
-       if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
-                               dev->wiphy.interface_modes))
-               goto nla_put_failure;
+       if (large && nl80211_send_wowlan_tcp_caps(dev, msg))
+               return -ENOBUFS;
 
-       nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
-       if (!nl_bands)
-               goto nla_put_failure;
+       nla_nest_end(msg, nl_wowlan);
 
-       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-               if (!dev->wiphy.bands[band])
-                       continue;
+       return 0;
+}
+#endif
 
-               nl_band = nla_nest_start(msg, band);
-               if (!nl_band)
-                       goto nla_put_failure;
+static int nl80211_send_band_rateinfo(struct sk_buff *msg,
+                                     struct ieee80211_supported_band *sband)
+{
+       struct nlattr *nl_rates, *nl_rate;
+       struct ieee80211_rate *rate;
+       int i;
 
-               /* add HT info */
-               if (dev->wiphy.bands[band]->ht_cap.ht_supported &&
-                   (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
-                            sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
-                            &dev->wiphy.bands[band]->ht_cap.mcs) ||
-                    nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
-                                dev->wiphy.bands[band]->ht_cap.cap) ||
-                    nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
-                               dev->wiphy.bands[band]->ht_cap.ampdu_factor) ||
-                    nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
-                               dev->wiphy.bands[band]->ht_cap.ampdu_density)))
-                       goto nla_put_failure;
+       /* add HT info */
+       if (sband->ht_cap.ht_supported &&
+           (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
+                    sizeof(sband->ht_cap.mcs),
+                    &sband->ht_cap.mcs) ||
+            nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
+                        sband->ht_cap.cap) ||
+            nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+                       sband->ht_cap.ampdu_factor) ||
+            nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+                       sband->ht_cap.ampdu_density)))
+               return -ENOBUFS;
 
-               /* add VHT info */
-               if (dev->wiphy.bands[band]->vht_cap.vht_supported &&
-                   (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
-                            sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs),
-                            &dev->wiphy.bands[band]->vht_cap.vht_mcs) ||
-                    nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
-                                dev->wiphy.bands[band]->vht_cap.cap)))
-                       goto nla_put_failure;
+       /* add VHT info */
+       if (sband->vht_cap.vht_supported &&
+           (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
+                    sizeof(sband->vht_cap.vht_mcs),
+                    &sband->vht_cap.vht_mcs) ||
+            nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
+                        sband->vht_cap.cap)))
+               return -ENOBUFS;
 
-               /* add frequencies */
-               nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
-               if (!nl_freqs)
-                       goto nla_put_failure;
+       /* add bitrates */
+       nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
+       if (!nl_rates)
+               return -ENOBUFS;
 
-               for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
-                       nl_freq = nla_nest_start(msg, i);
-                       if (!nl_freq)
-                               goto nla_put_failure;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               nl_rate = nla_nest_start(msg, i);
+               if (!nl_rate)
+                       return -ENOBUFS;
 
-                       chan = &dev->wiphy.bands[band]->channels[i];
+               rate = &sband->bitrates[i];
+               if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
+                               rate->bitrate))
+                       return -ENOBUFS;
+               if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
+                   nla_put_flag(msg,
+                                NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
+                       return -ENOBUFS;
 
-                       if (nl80211_msg_put_channel(msg, chan))
-                               goto nla_put_failure;
+               nla_nest_end(msg, nl_rate);
+       }
 
-                       nla_nest_end(msg, nl_freq);
-               }
+       nla_nest_end(msg, nl_rates);
 
-               nla_nest_end(msg, nl_freqs);
+       return 0;
+}
 
-               /* add bitrates */
-               nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
-               if (!nl_rates)
-                       goto nla_put_failure;
+static int
+nl80211_send_mgmt_stypes(struct sk_buff *msg,
+                        const struct ieee80211_txrx_stypes *mgmt_stypes)
+{
+       u16 stypes;
+       struct nlattr *nl_ftypes, *nl_ifs;
+       enum nl80211_iftype ift;
+       int i;
 
-               for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
-                       nl_rate = nla_nest_start(msg, i);
-                       if (!nl_rate)
-                               goto nla_put_failure;
+       if (!mgmt_stypes)
+               return 0;
 
-                       rate = &dev->wiphy.bands[band]->bitrates[i];
-                       if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
-                                       rate->bitrate))
-                               goto nla_put_failure;
-                       if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
-                           nla_put_flag(msg,
-                                        NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
-                               goto nla_put_failure;
+       nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
+       if (!nl_ifs)
+               return -ENOBUFS;
+
+       for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
+               nl_ftypes = nla_nest_start(msg, ift);
+               if (!nl_ftypes)
+                       return -ENOBUFS;
+               i = 0;
+               stypes = mgmt_stypes[ift].tx;
+               while (stypes) {
+                       if ((stypes & 1) &&
+                           nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
+                                       (i << 4) | IEEE80211_FTYPE_MGMT))
+                               return -ENOBUFS;
+                       stypes >>= 1;
+                       i++;
+               }
+               nla_nest_end(msg, nl_ftypes);
+       }
+
+       nla_nest_end(msg, nl_ifs);
+
+       nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
+       if (!nl_ifs)
+               return -ENOBUFS;
 
-                       nla_nest_end(msg, nl_rate);
+       for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
+               nl_ftypes = nla_nest_start(msg, ift);
+               if (!nl_ftypes)
+                       return -ENOBUFS;
+               i = 0;
+               stypes = mgmt_stypes[ift].rx;
+               while (stypes) {
+                       if ((stypes & 1) &&
+                           nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
+                                       (i << 4) | IEEE80211_FTYPE_MGMT))
+                               return -ENOBUFS;
+                       stypes >>= 1;
+                       i++;
                }
+               nla_nest_end(msg, nl_ftypes);
+       }
+       nla_nest_end(msg, nl_ifs);
+
+       return 0;
+}
 
-               nla_nest_end(msg, nl_rates);
+static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
+                             struct sk_buff *msg, u32 portid, u32 seq,
+                             int flags, bool split, long *split_start,
+                             long *band_start, long *chan_start)
+{
+       void *hdr;
+       struct nlattr *nl_bands, *nl_band;
+       struct nlattr *nl_freqs, *nl_freq;
+       struct nlattr *nl_cmds;
+       enum ieee80211_band band;
+       struct ieee80211_channel *chan;
+       int i;
+       const struct ieee80211_txrx_stypes *mgmt_stypes =
+                               dev->wiphy.mgmt_stypes;
+       long start = 0, start_chan = 0, start_band = 0;
+       u32 features;
 
-               nla_nest_end(msg, nl_band);
+       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
+       if (!hdr)
+               return -ENOBUFS;
+
+       /* allow always using the variables */
+       if (!split) {
+               split_start = &start;
+               band_start = &start_band;
+               chan_start = &start_chan;
        }
-       nla_nest_end(msg, nl_bands);
 
-       nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
-       if (!nl_cmds)
-               goto nla_put_failure;
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
+           nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
+                          wiphy_name(&dev->wiphy)) ||
+           nla_put_u32(msg, NL80211_ATTR_GENERATION,
+                       cfg80211_rdev_list_generation))
+               goto nla_put_failure;
+
+       switch (*split_start) {
+       case 0:
+               if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
+                              dev->wiphy.retry_short) ||
+                   nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
+                              dev->wiphy.retry_long) ||
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+                               dev->wiphy.frag_threshold) ||
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+                               dev->wiphy.rts_threshold) ||
+                   nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
+                              dev->wiphy.coverage_class) ||
+                   nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+                              dev->wiphy.max_scan_ssids) ||
+                   nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+                              dev->wiphy.max_sched_scan_ssids) ||
+                   nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
+                               dev->wiphy.max_scan_ie_len) ||
+                   nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+                               dev->wiphy.max_sched_scan_ie_len) ||
+                   nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
+                              dev->wiphy.max_match_sets))
+                       goto nla_put_failure;
 
-       i = 0;
-#define CMD(op, n)                                             \
-        do {                                                   \
-               if (dev->ops->op) {                             \
-                       i++;                                    \
-                       if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
-                               goto nla_put_failure;           \
-               }                                               \
-       } while (0)
-
-       CMD(add_virtual_intf, NEW_INTERFACE);
-       CMD(change_virtual_intf, SET_INTERFACE);
-       CMD(add_key, NEW_KEY);
-       CMD(start_ap, START_AP);
-       CMD(add_station, NEW_STATION);
-       CMD(add_mpath, NEW_MPATH);
-       CMD(update_mesh_config, SET_MESH_CONFIG);
-       CMD(change_bss, SET_BSS);
-       CMD(auth, AUTHENTICATE);
-       CMD(assoc, ASSOCIATE);
-       CMD(deauth, DEAUTHENTICATE);
-       CMD(disassoc, DISASSOCIATE);
-       CMD(join_ibss, JOIN_IBSS);
-       CMD(join_mesh, JOIN_MESH);
-       CMD(set_pmksa, SET_PMKSA);
-       CMD(del_pmksa, DEL_PMKSA);
-       CMD(flush_pmksa, FLUSH_PMKSA);
-       if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
-               CMD(remain_on_channel, REMAIN_ON_CHANNEL);
-       CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
-       CMD(mgmt_tx, FRAME);
-       CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
-       if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
-               i++;
-               if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
+               if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
+                   nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
                        goto nla_put_failure;
-       }
-       if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
-           dev->ops->join_mesh) {
-               i++;
-               if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
+               if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
+                   nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
                        goto nla_put_failure;
-       }
-       CMD(set_wds_peer, SET_WDS_PEER);
-       if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
-               CMD(tdls_mgmt, TDLS_MGMT);
-               CMD(tdls_oper, TDLS_OPER);
-       }
-       if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
-               CMD(sched_scan_start, START_SCHED_SCAN);
-       CMD(probe_client, PROBE_CLIENT);
-       CMD(set_noack_map, SET_NOACK_MAP);
-       if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
-               i++;
-               if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
+               if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
+                   nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
+                       goto nla_put_failure;
+               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
+                   nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
+                       goto nla_put_failure;
+               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
+                   nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
+                       goto nla_put_failure;
+               if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
+                   nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
                        goto nla_put_failure;
-       }
-       CMD(start_p2p_device, START_P2P_DEVICE);
-       CMD(set_mcast_rate, SET_MCAST_RATE);
 
-#ifdef CONFIG_NL80211_TESTMODE
-       CMD(testmode_cmd, TESTMODE);
-#endif
+               (*split_start)++;
+               if (split)
+                       break;
+       case 1:
+               if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
+                           sizeof(u32) * dev->wiphy.n_cipher_suites,
+                           dev->wiphy.cipher_suites))
+                       goto nla_put_failure;
 
-#undef CMD
+               if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
+                              dev->wiphy.max_num_pmkids))
+                       goto nla_put_failure;
 
-       if (dev->ops->connect || dev->ops->auth) {
-               i++;
-               if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
+               if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
+                   nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
                        goto nla_put_failure;
-       }
 
-       if (dev->ops->disconnect || dev->ops->deauth) {
-               i++;
-               if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
+               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+                               dev->wiphy.available_antennas_tx) ||
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+                               dev->wiphy.available_antennas_rx))
+                       goto nla_put_failure;
+
+               if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
+                   nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
+                               dev->wiphy.probe_resp_offload))
                        goto nla_put_failure;
-       }
 
-       nla_nest_end(msg, nl_cmds);
+               if ((dev->wiphy.available_antennas_tx ||
+                    dev->wiphy.available_antennas_rx) &&
+                   dev->ops->get_antenna) {
+                       u32 tx_ant = 0, rx_ant = 0;
+                       int res;
+                       res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
+                       if (!res) {
+                               if (nla_put_u32(msg,
+                                               NL80211_ATTR_WIPHY_ANTENNA_TX,
+                                               tx_ant) ||
+                                   nla_put_u32(msg,
+                                               NL80211_ATTR_WIPHY_ANTENNA_RX,
+                                               rx_ant))
+                                       goto nla_put_failure;
+                       }
+               }
 
-       if (dev->ops->remain_on_channel &&
-           (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
-           nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
-                       dev->wiphy.max_remain_on_channel_duration))
-               goto nla_put_failure;
+               (*split_start)++;
+               if (split)
+                       break;
+       case 2:
+               if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
+                                       dev->wiphy.interface_modes))
+                               goto nla_put_failure;
+               (*split_start)++;
+               if (split)
+                       break;
+       case 3:
+               nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
+               if (!nl_bands)
+                       goto nla_put_failure;
 
-       if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
-           nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
-               goto nla_put_failure;
+               for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) {
+                       struct ieee80211_supported_band *sband;
 
-       if (mgmt_stypes) {
-               u16 stypes;
-               struct nlattr *nl_ftypes, *nl_ifs;
-               enum nl80211_iftype ift;
+                       sband = dev->wiphy.bands[band];
 
-               nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
-               if (!nl_ifs)
-                       goto nla_put_failure;
+                       if (!sband)
+                               continue;
 
-               for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
-                       nl_ftypes = nla_nest_start(msg, ift);
-                       if (!nl_ftypes)
+                       nl_band = nla_nest_start(msg, band);
+                       if (!nl_band)
                                goto nla_put_failure;
-                       i = 0;
-                       stypes = mgmt_stypes[ift].tx;
-                       while (stypes) {
-                               if ((stypes & 1) &&
-                                   nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
-                                               (i << 4) | IEEE80211_FTYPE_MGMT))
+
+                       switch (*chan_start) {
+                       case 0:
+                               if (nl80211_send_band_rateinfo(msg, sband))
                                        goto nla_put_failure;
-                               stypes >>= 1;
-                               i++;
+                               (*chan_start)++;
+                               if (split)
+                                       break;
+                       default:
+                               /* add frequencies */
+                               nl_freqs = nla_nest_start(
+                                       msg, NL80211_BAND_ATTR_FREQS);
+                               if (!nl_freqs)
+                                       goto nla_put_failure;
+
+                               for (i = *chan_start - 1;
+                                    i < sband->n_channels;
+                                    i++) {
+                                       nl_freq = nla_nest_start(msg, i);
+                                       if (!nl_freq)
+                                               goto nla_put_failure;
+
+                                       chan = &sband->channels[i];
+
+                                       if (nl80211_msg_put_channel(msg, chan,
+                                                                   split))
+                                               goto nla_put_failure;
+
+                                       nla_nest_end(msg, nl_freq);
+                                       if (split)
+                                               break;
+                               }
+                               if (i < sband->n_channels)
+                                       *chan_start = i + 2;
+                               else
+                                       *chan_start = 0;
+                               nla_nest_end(msg, nl_freqs);
+                       }
+
+                       nla_nest_end(msg, nl_band);
+
+                       if (split) {
+                               /* start again here */
+                               if (*chan_start)
+                                       band--;
+                               break;
                        }
-                       nla_nest_end(msg, nl_ftypes);
                }
+               nla_nest_end(msg, nl_bands);
 
-               nla_nest_end(msg, nl_ifs);
+               if (band < IEEE80211_NUM_BANDS)
+                       *band_start = band + 1;
+               else
+                       *band_start = 0;
 
-               nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
-               if (!nl_ifs)
+               /* if bands & channels are done, continue outside */
+               if (*band_start == 0 && *chan_start == 0)
+                       (*split_start)++;
+               if (split)
+                       break;
+       case 4:
+               nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
+               if (!nl_cmds)
                        goto nla_put_failure;
 
-               for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
-                       nl_ftypes = nla_nest_start(msg, ift);
-                       if (!nl_ftypes)
+               i = 0;
+#define CMD(op, n)                                                     \
+                do {                                                   \
+                       if (dev->ops->op) {                             \
+                               i++;                                    \
+                               if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
+                                       goto nla_put_failure;           \
+                       }                                               \
+               } while (0)
+
+               CMD(add_virtual_intf, NEW_INTERFACE);
+               CMD(change_virtual_intf, SET_INTERFACE);
+               CMD(add_key, NEW_KEY);
+               CMD(start_ap, START_AP);
+               CMD(add_station, NEW_STATION);
+               CMD(add_mpath, NEW_MPATH);
+               CMD(update_mesh_config, SET_MESH_CONFIG);
+               CMD(change_bss, SET_BSS);
+               CMD(auth, AUTHENTICATE);
+               CMD(assoc, ASSOCIATE);
+               CMD(deauth, DEAUTHENTICATE);
+               CMD(disassoc, DISASSOCIATE);
+               CMD(join_ibss, JOIN_IBSS);
+               CMD(join_mesh, JOIN_MESH);
+               CMD(set_pmksa, SET_PMKSA);
+               CMD(del_pmksa, DEL_PMKSA);
+               CMD(flush_pmksa, FLUSH_PMKSA);
+               if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
+                       CMD(remain_on_channel, REMAIN_ON_CHANNEL);
+               CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
+               CMD(mgmt_tx, FRAME);
+               CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
+               if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
+                       i++;
+                       if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
                                goto nla_put_failure;
-                       i = 0;
-                       stypes = mgmt_stypes[ift].rx;
-                       while (stypes) {
-                               if ((stypes & 1) &&
-                                   nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
-                                               (i << 4) | IEEE80211_FTYPE_MGMT))
-                                       goto nla_put_failure;
-                               stypes >>= 1;
-                               i++;
-                       }
-                       nla_nest_end(msg, nl_ftypes);
                }
-               nla_nest_end(msg, nl_ifs);
-       }
+               if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
+                   dev->ops->join_mesh) {
+                       i++;
+                       if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
+                               goto nla_put_failure;
+               }
+               CMD(set_wds_peer, SET_WDS_PEER);
+               if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+                       CMD(tdls_mgmt, TDLS_MGMT);
+                       CMD(tdls_oper, TDLS_OPER);
+               }
+               if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+                       CMD(sched_scan_start, START_SCHED_SCAN);
+               CMD(probe_client, PROBE_CLIENT);
+               CMD(set_noack_map, SET_NOACK_MAP);
+               if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
+                       i++;
+                       if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
+                               goto nla_put_failure;
+               }
+               CMD(start_p2p_device, START_P2P_DEVICE);
+               CMD(set_mcast_rate, SET_MCAST_RATE);
 
-#ifdef CONFIG_PM
-       if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
-               struct nlattr *nl_wowlan;
+#ifdef CONFIG_NL80211_TESTMODE
+               CMD(testmode_cmd, TESTMODE);
+#endif
 
-               nl_wowlan = nla_nest_start(msg,
-                               NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
-               if (!nl_wowlan)
-                       goto nla_put_failure;
+#undef CMD
 
-               if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
-                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-                   ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
-                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-                   ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
-                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
-                   ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
-                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
-                   ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
-                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
-                   ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
-                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
-                   ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
-                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-                   ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
-                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
-                   goto nla_put_failure;
-               if (dev->wiphy.wowlan.n_patterns) {
-                       struct nl80211_wowlan_pattern_support pat = {
-                               .max_patterns = dev->wiphy.wowlan.n_patterns,
-                               .min_pattern_len =
-                                       dev->wiphy.wowlan.pattern_min_len,
-                               .max_pattern_len =
-                                       dev->wiphy.wowlan.pattern_max_len,
-                               .max_pkt_offset =
-                                       dev->wiphy.wowlan.max_pkt_offset,
-                       };
-                       if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
-                                   sizeof(pat), &pat))
+               if (dev->ops->connect || dev->ops->auth) {
+                       i++;
+                       if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
                                goto nla_put_failure;
                }
 
-               nla_nest_end(msg, nl_wowlan);
-       }
+               if (dev->ops->disconnect || dev->ops->deauth) {
+                       i++;
+                       if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
+                               goto nla_put_failure;
+               }
+
+               nla_nest_end(msg, nl_cmds);
+               (*split_start)++;
+               if (split)
+                       break;
+       case 5:
+               if (dev->ops->remain_on_channel &&
+                   (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
+                   nla_put_u32(msg,
+                               NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+                               dev->wiphy.max_remain_on_channel_duration))
+                       goto nla_put_failure;
+
+               if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
+                   nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
+                       goto nla_put_failure;
+
+               if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
+                       goto nla_put_failure;
+               (*split_start)++;
+               if (split)
+                       break;
+       case 6:
+#ifdef CONFIG_PM
+               if (nl80211_send_wowlan(msg, dev, split))
+                       goto nla_put_failure;
+               (*split_start)++;
+               if (split)
+                       break;
+#else
+               (*split_start)++;
 #endif
+       case 7:
+               if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
+                                       dev->wiphy.software_iftypes))
+                       goto nla_put_failure;
 
-       if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
-                               dev->wiphy.software_iftypes))
-               goto nla_put_failure;
+               if (nl80211_put_iface_combinations(&dev->wiphy, msg, split))
+                       goto nla_put_failure;
 
-       if (nl80211_put_iface_combinations(&dev->wiphy, msg))
-               goto nla_put_failure;
+               (*split_start)++;
+               if (split)
+                       break;
+       case 8:
+               if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
+                   nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
+                               dev->wiphy.ap_sme_capa))
+                       goto nla_put_failure;
 
-       if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
-           nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
-                       dev->wiphy.ap_sme_capa))
-               goto nla_put_failure;
+               features = dev->wiphy.features;
+               /*
+                * We can only add the per-channel limit information if the
+                * dump is split, otherwise it makes it too big. Therefore
+                * only advertise it in that case.
+                */
+               if (split)
+                       features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
+               if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
+                       goto nla_put_failure;
 
-       if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS,
-                       dev->wiphy.features))
-               goto nla_put_failure;
+               if (dev->wiphy.ht_capa_mod_mask &&
+                   nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
+                           sizeof(*dev->wiphy.ht_capa_mod_mask),
+                           dev->wiphy.ht_capa_mod_mask))
+                       goto nla_put_failure;
 
-       if (dev->wiphy.ht_capa_mod_mask &&
-           nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
-                   sizeof(*dev->wiphy.ht_capa_mod_mask),
-                   dev->wiphy.ht_capa_mod_mask))
-               goto nla_put_failure;
+               if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+                   dev->wiphy.max_acl_mac_addrs &&
+                   nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
+                               dev->wiphy.max_acl_mac_addrs))
+                       goto nla_put_failure;
 
-       if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
-           dev->wiphy.max_acl_mac_addrs &&
-           nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
-                       dev->wiphy.max_acl_mac_addrs))
-               goto nla_put_failure;
+               /*
+                * Any information below this point is only available to
+                * applications that can deal with it being split. This
+                * helps ensure that newly added capabilities don't break
+                * older tools by overrunning their buffers.
+                *
+                * We still increment split_start so that in the split
+                * case we'll continue with more data in the next round,
+                * but break unconditionally so unsplit data stops here.
+                */
+               (*split_start)++;
+               break;
+       case 9:
+               if (dev->wiphy.extended_capabilities &&
+                   (nla_put(msg, NL80211_ATTR_EXT_CAPA,
+                            dev->wiphy.extended_capabilities_len,
+                            dev->wiphy.extended_capabilities) ||
+                    nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+                            dev->wiphy.extended_capabilities_len,
+                            dev->wiphy.extended_capabilities_mask)))
+                       goto nla_put_failure;
+
+               if (dev->wiphy.vht_capa_mod_mask &&
+                   nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
+                           sizeof(*dev->wiphy.vht_capa_mod_mask),
+                           dev->wiphy.vht_capa_mod_mask))
+                       goto nla_put_failure;
 
+               /* done */
+               *split_start = 0;
+               break;
+       }
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -1310,39 +1551,80 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
        int idx = 0, ret;
        int start = cb->args[0];
        struct cfg80211_registered_device *dev;
+       s64 filter_wiphy = -1;
+       bool split = false;
+       struct nlattr **tb = nl80211_fam.attrbuf;
+       int res;
 
        mutex_lock(&cfg80211_mutex);
+       res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+                         tb, nl80211_fam.maxattr, nl80211_policy);
+       if (res == 0) {
+               split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
+               if (tb[NL80211_ATTR_WIPHY])
+                       filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+               if (tb[NL80211_ATTR_WDEV])
+                       filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
+               if (tb[NL80211_ATTR_IFINDEX]) {
+                       struct net_device *netdev;
+                       int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+                       netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
+                       if (!netdev) {
+                               mutex_unlock(&cfg80211_mutex);
+                               return -ENODEV;
+                       }
+                       if (netdev->ieee80211_ptr) {
+                               dev = wiphy_to_dev(
+                                       netdev->ieee80211_ptr->wiphy);
+                               filter_wiphy = dev->wiphy_idx;
+                       }
+                       dev_put(netdev);
+               }
+       }
+
        list_for_each_entry(dev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
                        continue;
                if (++idx <= start)
                        continue;
-               ret = nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid,
-                                        cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                        dev);
-               if (ret < 0) {
-                       /*
-                        * If sending the wiphy data didn't fit (ENOBUFS or
-                        * EMSGSIZE returned), this SKB is still empty (so
-                        * it's not too big because another wiphy dataset is
-                        * already in the skb) and we've not tried to adjust
-                        * the dump allocation yet ... then adjust the alloc
-                        * size to be bigger, and return 1 but with the empty
-                        * skb. This results in an empty message being RX'ed
-                        * in userspace, but that is ignored.
-                        *
-                        * We can then retry with the larger buffer.
-                        */
-                       if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
-                           !skb->len &&
-                           cb->min_dump_alloc < 4096) {
-                               cb->min_dump_alloc = 4096;
-                               mutex_unlock(&cfg80211_mutex);
-                               return 1;
+               if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy)
+                       continue;
+               /* attempt to fit multiple wiphy data chunks into the skb */
+               do {
+                       ret = nl80211_send_wiphy(dev, skb,
+                                                NETLINK_CB(cb->skb).portid,
+                                                cb->nlh->nlmsg_seq,
+                                                NLM_F_MULTI,
+                                                split, &cb->args[1],
+                                                &cb->args[2],
+                                                &cb->args[3]);
+                       if (ret < 0) {
+                               /*
+                                * If sending the wiphy data didn't fit (ENOBUFS
+                                * or EMSGSIZE returned), this SKB is still
+                                * empty (so it's not too big because another
+                                * wiphy dataset is already in the skb) and
+                                * we've not tried to adjust the dump allocation
+                                * yet ... then adjust the alloc size to be
+                                * bigger, and return 1 but with the empty skb.
+                                * This results in an empty message being RX'ed
+                                * in userspace, but that is ignored.
+                                *
+                                * We can then retry with the larger buffer.
+                                */
+                               if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+                                   !skb->len &&
+                                   cb->min_dump_alloc < 4096) {
+                                       cb->min_dump_alloc = 4096;
+                                       mutex_unlock(&cfg80211_mutex);
+                                       return 1;
+                               }
+                               idx--;
+                               break;
                        }
-                       idx--;
-                       break;
-               }
+               } while (cb->args[1] > 0);
+               break;
        }
        mutex_unlock(&cfg80211_mutex);
 
@@ -1360,7 +1642,8 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
        if (!msg)
                return -ENOMEM;
 
-       if (nl80211_send_wiphy(msg, info->snd_portid, info->snd_seq, 0, dev) < 0) {
+       if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0,
+                              false, NULL, NULL, NULL) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -2967,6 +3250,7 @@ static int parse_station_flags(struct genl_info *info,
                sta_flags = nla_data(nla);
                params->sta_flags_mask = sta_flags->mask;
                params->sta_flags_set = sta_flags->set;
+               params->sta_flags_set &= params->sta_flags_mask;
                if ((params->sta_flags_mask |
                     params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
                        return -EINVAL;
@@ -3320,6 +3604,136 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
        return genlmsg_reply(msg, info);
 }
 
+int cfg80211_check_station_change(struct wiphy *wiphy,
+                                 struct station_parameters *params,
+                                 enum cfg80211_station_type statype)
+{
+       if (params->listen_interval != -1)
+               return -EINVAL;
+       if (params->aid)
+               return -EINVAL;
+
+       /* When you run into this, adjust the code below for the new flag */
+       BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
+
+       switch (statype) {
+       case CFG80211_STA_MESH_PEER_KERNEL:
+       case CFG80211_STA_MESH_PEER_USER:
+               /*
+                * No ignoring the TDLS flag here -- the userspace mesh
+                * code doesn't have the bug of including TDLS in the
+                * mask everywhere.
+                */
+               if (params->sta_flags_mask &
+                               ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                                 BIT(NL80211_STA_FLAG_MFP) |
+                                 BIT(NL80211_STA_FLAG_AUTHORIZED)))
+                       return -EINVAL;
+               break;
+       case CFG80211_STA_TDLS_PEER_SETUP:
+       case CFG80211_STA_TDLS_PEER_ACTIVE:
+               if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+                       return -EINVAL;
+               /* ignore since it can't change */
+               params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+               break;
+       default:
+               /* disallow mesh-specific things */
+               if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
+                       return -EINVAL;
+               if (params->local_pm)
+                       return -EINVAL;
+               if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
+                       return -EINVAL;
+       }
+
+       if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
+           statype != CFG80211_STA_TDLS_PEER_ACTIVE) {
+               /* TDLS can't be set, ... */
+               if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+                       return -EINVAL;
+               /*
+                * ... but don't bother the driver with it. This works around
+                * a hostapd/wpa_supplicant issue -- it always includes the
+                * TLDS_PEER flag in the mask even for AP mode.
+                */
+               params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+       }
+
+       if (statype != CFG80211_STA_TDLS_PEER_SETUP) {
+               /* reject other things that can't change */
+               if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
+                       return -EINVAL;
+               if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY)
+                       return -EINVAL;
+               if (params->supported_rates)
+                       return -EINVAL;
+               if (params->ext_capab || params->ht_capa || params->vht_capa)
+                       return -EINVAL;
+       }
+
+       if (statype != CFG80211_STA_AP_CLIENT) {
+               if (params->vlan)
+                       return -EINVAL;
+       }
+
+       switch (statype) {
+       case CFG80211_STA_AP_MLME_CLIENT:
+               /* Use this only for authorizing/unauthorizing a station */
+               if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
+                       return -EOPNOTSUPP;
+               break;
+       case CFG80211_STA_AP_CLIENT:
+               /* accept only the listed bits */
+               if (params->sta_flags_mask &
+                               ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+                                 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                                 BIT(NL80211_STA_FLAG_ASSOCIATED) |
+                                 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
+                                 BIT(NL80211_STA_FLAG_WME) |
+                                 BIT(NL80211_STA_FLAG_MFP)))
+                       return -EINVAL;
+
+               /* but authenticated/associated only if driver handles it */
+               if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
+                   params->sta_flags_mask &
+                               (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                                BIT(NL80211_STA_FLAG_ASSOCIATED)))
+                       return -EINVAL;
+               break;
+       case CFG80211_STA_IBSS:
+       case CFG80211_STA_AP_STA:
+               /* reject any changes other than AUTHORIZED */
+               if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
+                       return -EINVAL;
+               break;
+       case CFG80211_STA_TDLS_PEER_SETUP:
+               /* reject any changes other than AUTHORIZED or WME */
+               if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+                                              BIT(NL80211_STA_FLAG_WME)))
+                       return -EINVAL;
+               /* force (at least) rates when authorizing */
+               if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) &&
+                   !params->supported_rates)
+                       return -EINVAL;
+               break;
+       case CFG80211_STA_TDLS_PEER_ACTIVE:
+               /* reject any changes */
+               return -EINVAL;
+       case CFG80211_STA_MESH_PEER_KERNEL:
+               if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
+                       return -EINVAL;
+               break;
+       case CFG80211_STA_MESH_PEER_USER:
+               if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
+                       return -EINVAL;
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(cfg80211_check_station_change);
+
 /*
  * Get vlan interface making sure it is running and on the right wiphy.
  */
@@ -3342,6 +3756,13 @@ static struct net_device *get_vlan(struct genl_info *info,
                goto error;
        }
 
+       if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
+           v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+               ret = -EINVAL;
+               goto error;
+       }
+
        if (!netif_running(v)) {
                ret = -ENETDOWN;
                goto error;
@@ -3359,21 +3780,13 @@ nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
        [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
 };
 
-static int nl80211_set_station_tdls(struct genl_info *info,
-                                   struct station_parameters *params)
+static int nl80211_parse_sta_wme(struct genl_info *info,
+                                struct station_parameters *params)
 {
        struct nlattr *tb[NL80211_STA_WME_MAX + 1];
        struct nlattr *nla;
        int err;
 
-       /* Dummy STA entry gets updated once the peer capabilities are known */
-       if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
-               params->ht_capa =
-                       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
-       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
-               params->vht_capa =
-                       nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
-
        /* parse WME attributes if present */
        if (!info->attrs[NL80211_ATTR_STA_WME])
                return 0;
@@ -3401,18 +3814,34 @@ static int nl80211_set_station_tdls(struct genl_info *info,
        return 0;
 }
 
+static int nl80211_set_station_tdls(struct genl_info *info,
+                                   struct station_parameters *params)
+{
+       /* Dummy STA entry gets updated once the peer capabilities are known */
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
+               params->ht_capa =
+                       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
+               params->vht_capa =
+                       nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
+
+       return nl80211_parse_sta_wme(info, params);
+}
+
 static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       int err;
        struct net_device *dev = info->user_ptr[1];
        struct station_parameters params;
-       u8 *mac_addr = NULL;
+       u8 *mac_addr;
+       int err;
 
        memset(&params, 0, sizeof(params));
 
        params.listen_interval = -1;
-       params.plink_state = -1;
+
+       if (!rdev->ops->change_station)
+               return -EOPNOTSUPP;
 
        if (info->attrs[NL80211_ATTR_STA_AID])
                return -EINVAL;
@@ -3445,152 +3874,62 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
                return -EINVAL;
 
-       if (!rdev->ops->change_station)
-               return -EOPNOTSUPP;
-
        if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
                return -EINVAL;
 
-       if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
+       if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
                params.plink_action =
-                   nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
-
-       if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
-               params.plink_state =
-                   nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
-
-       if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
-               enum nl80211_mesh_power_mode pm = nla_get_u32(
-                       info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
-
-               if (pm <= NL80211_MESH_POWER_UNKNOWN ||
-                   pm > NL80211_MESH_POWER_MAX)
-                       return -EINVAL;
-
-               params.local_pm = pm;
-       }
-
-       switch (dev->ieee80211_ptr->iftype) {
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_AP_VLAN:
-       case NL80211_IFTYPE_P2P_GO:
-               /* disallow mesh-specific things */
-               if (params.plink_action)
-                       return -EINVAL;
-               if (params.local_pm)
-                       return -EINVAL;
-
-               /* TDLS can't be set, ... */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
-                       return -EINVAL;
-               /*
-                * ... but don't bother the driver with it. This works around
-                * a hostapd/wpa_supplicant issue -- it always includes the
-                * TLDS_PEER flag in the mask even for AP mode.
-                */
-               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
-
-               /* accept only the listed bits */
-               if (params.sta_flags_mask &
-                               ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
-                                 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
-                                 BIT(NL80211_STA_FLAG_ASSOCIATED) |
-                                 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
-                                 BIT(NL80211_STA_FLAG_WME) |
-                                 BIT(NL80211_STA_FLAG_MFP)))
-                       return -EINVAL;
-
-               /* but 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;
-
-               /* reject other things that can't change */
-               if (params.supported_rates)
-                       return -EINVAL;
-               if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
+                       nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+               if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
                        return -EINVAL;
-               if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
+       }
+
+       if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) {
+               params.plink_state =
+                       nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
+               if (params.plink_state >= NUM_NL80211_PLINK_STATES)
                        return -EINVAL;
-               if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
-                   info->attrs[NL80211_ATTR_VHT_CAPABILITY])
+               params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE;
+       }
+
+       if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
+               enum nl80211_mesh_power_mode pm = nla_get_u32(
+                       info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
+
+               if (pm <= NL80211_MESH_POWER_UNKNOWN ||
+                   pm > NL80211_MESH_POWER_MAX)
                        return -EINVAL;
 
-               /* must be last in here for error handling */
-               params.vlan = get_vlan(info, rdev);
-               if (IS_ERR(params.vlan))
-                       return PTR_ERR(params.vlan);
-               break;
+               params.local_pm = pm;
+       }
+
+       /* Include parameters for TDLS peer (will check later) */
+       err = nl80211_set_station_tdls(info, &params);
+       if (err)
+               return err;
+
+       params.vlan = get_vlan(info, rdev);
+       if (IS_ERR(params.vlan))
+               return PTR_ERR(params.vlan);
+
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
-               /*
-                * Don't allow userspace to change the TDLS_PEER flag,
-                * but silently ignore attempts to change it since we
-                * don't have state here to verify that it doesn't try
-                * to change the flag.
-                */
-               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
-               /* Include parameters for TDLS peer (driver will check) */
-               err = nl80211_set_station_tdls(info, &params);
-               if (err)
-                       return err;
-               /* disallow things sta doesn't support */
-               if (params.plink_action)
-                       return -EINVAL;
-               if (params.local_pm)
-                       return -EINVAL;
-               /* reject any changes other than AUTHORIZED or WME (for TDLS) */
-               if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
-                                             BIT(NL80211_STA_FLAG_WME)))
-                       return -EINVAL;
-               break;
        case NL80211_IFTYPE_ADHOC:
-               /* disallow things sta doesn't support */
-               if (params.plink_action)
-                       return -EINVAL;
-               if (params.local_pm)
-                       return -EINVAL;
-               if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
-                   info->attrs[NL80211_ATTR_VHT_CAPABILITY])
-                       return -EINVAL;
-               /* reject any changes other than AUTHORIZED */
-               if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
-                       return -EINVAL;
-               break;
        case NL80211_IFTYPE_MESH_POINT:
-               /* disallow things mesh doesn't support */
-               if (params.vlan)
-                       return -EINVAL;
-               if (params.supported_rates)
-                       return -EINVAL;
-               if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
-                       return -EINVAL;
-               if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
-                       return -EINVAL;
-               if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
-                   info->attrs[NL80211_ATTR_VHT_CAPABILITY])
-                       return -EINVAL;
-               /*
-                * No special handling for TDLS here -- the userspace
-                * mesh code doesn't have this bug.
-                */
-               if (params.sta_flags_mask &
-                               ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
-                                 BIT(NL80211_STA_FLAG_MFP) |
-                                 BIT(NL80211_STA_FLAG_AUTHORIZED)))
-                       return -EINVAL;
                break;
        default:
-               return -EOPNOTSUPP;
+               err = -EOPNOTSUPP;
+               goto out_put_vlan;
        }
 
-       /* be aware of params.vlan when changing code here */
-
+       /* driver will call cfg80211_check_station_change() */
        err = rdev_change_station(rdev, dev, mac_addr, &params);
 
+ out_put_vlan:
        if (params.vlan)
                dev_put(params.vlan);
 
@@ -3607,6 +3946,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 
        memset(&params, 0, sizeof(params));
 
+       if (!rdev->ops->add_station)
+               return -EOPNOTSUPP;
+
        if (!info->attrs[NL80211_ATTR_MAC])
                return -EINVAL;
 
@@ -3652,50 +3994,32 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                params.vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 
-       if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
+       if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
                params.plink_action =
-                   nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+                       nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+               if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
+                       return -EINVAL;
+       }
 
-       if (!rdev->ops->add_station)
-               return -EOPNOTSUPP;
+       err = nl80211_parse_sta_wme(info, &params);
+       if (err)
+               return err;
 
        if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
                return -EINVAL;
 
+       /* When you run into this, adjust the code below for the new flag */
+       BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
+
        switch (dev->ieee80211_ptr->iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_P2P_GO:
-               /* parse WME attributes if sta is WME capable */
-               if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
-                   (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
-                   info->attrs[NL80211_ATTR_STA_WME]) {
-                       struct nlattr *tb[NL80211_STA_WME_MAX + 1];
-                       struct nlattr *nla;
-
-                       nla = info->attrs[NL80211_ATTR_STA_WME];
-                       err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
-                                              nl80211_sta_wme_policy);
-                       if (err)
-                               return err;
-
-                       if (tb[NL80211_STA_WME_UAPSD_QUEUES])
-                               params.uapsd_queues =
-                                    nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
-                       if (params.uapsd_queues &
-                                       ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
-                               return -EINVAL;
-
-                       if (tb[NL80211_STA_WME_MAX_SP])
-                               params.max_sp =
-                                    nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
+               /* ignore WME attributes if iface/sta is not capable */
+               if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
+                   !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
+                       params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
 
-                       if (params.max_sp &
-                                       ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
-                               return -EINVAL;
-
-                       params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
-               }
                /* TDLS peers cannot be added */
                if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
                        return -EINVAL;
@@ -3716,6 +4040,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        return PTR_ERR(params.vlan);
                break;
        case NL80211_IFTYPE_MESH_POINT:
+               /* ignore uAPSD data */
+               params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
+
                /* associated is disallowed */
                if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
                        return -EINVAL;
@@ -3724,8 +4051,14 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
-               /* associated is disallowed */
-               if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
+       case NL80211_IFTYPE_P2P_CLIENT:
+               /* ignore uAPSD data */
+               params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
+
+               /* these are disallowed */
+               if (params.sta_flags_mask &
+                               (BIT(NL80211_STA_FLAG_ASSOCIATED) |
+                                BIT(NL80211_STA_FLAG_AUTHENTICATED)))
                        return -EINVAL;
                /* Only TDLS peers can be added */
                if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
@@ -3736,6 +4069,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                /* ... with external setup is supported */
                if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
                        return -EOPNOTSUPP;
+               /*
+                * Older wpa_supplicant versions always mark the TDLS peer
+                * as authorized, but it shouldn't yet be.
+                */
+               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
                break;
        default:
                return -EOPNOTSUPP;
@@ -4280,6 +4618,7 @@ static const struct nla_policy
        [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+       [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
        [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
                                    .len = IEEE80211_MAX_DATA_LEN },
        [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
@@ -4418,6 +4757,7 @@ do {                                                                          \
 static int nl80211_parse_mesh_setup(struct genl_info *info,
                                     struct mesh_setup *setup)
 {
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
 
        if (!info->attrs[NL80211_ATTR_MESH_SETUP])
@@ -4454,8 +4794,14 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
                setup->ie = nla_data(ieattr);
                setup->ie_len = nla_len(ieattr);
        }
+       if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] &&
+           !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM))
+               return -EINVAL;
+       setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]);
        setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
        setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
+       if (setup->is_secure)
+               setup->user_mpm = true;
 
        return 0;
 }
@@ -5663,14 +6009,10 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
-       struct cfg80211_crypto_settings crypto;
        struct ieee80211_channel *chan;
-       const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
-       int err, ssid_len, ie_len = 0;
-       bool use_mfp = false;
-       u32 flags = 0;
-       struct ieee80211_ht_cap *ht_capa = NULL;
-       struct ieee80211_ht_cap *ht_capa_mask = NULL;
+       struct cfg80211_assoc_request req = {};
+       const u8 *bssid, *ssid;
+       int err, ssid_len = 0;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -5698,41 +6040,58 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
        if (info->attrs[NL80211_ATTR_IE]) {
-               ie = nla_data(info->attrs[NL80211_ATTR_IE]);
-               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
        if (info->attrs[NL80211_ATTR_USE_MFP]) {
                enum nl80211_mfp mfp =
                        nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
                if (mfp == NL80211_MFP_REQUIRED)
-                       use_mfp = true;
+                       req.use_mfp = true;
                else if (mfp != NL80211_MFP_NO)
                        return -EINVAL;
        }
 
        if (info->attrs[NL80211_ATTR_PREV_BSSID])
-               prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+               req.prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
 
        if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
-               flags |= ASSOC_REQ_DISABLE_HT;
+               req.flags |= ASSOC_REQ_DISABLE_HT;
 
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
-               ht_capa_mask =
-                       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
+               memcpy(&req.ht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+                      sizeof(req.ht_capa_mask));
 
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
-               if (!ht_capa_mask)
+               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
                        return -EINVAL;
-               ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+               memcpy(&req.ht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+                      sizeof(req.ht_capa));
+       }
+
+       if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+               req.flags |= ASSOC_REQ_DISABLE_VHT;
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+               memcpy(&req.vht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+                      sizeof(req.vht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+                       return -EINVAL;
+               memcpy(&req.vht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+                      sizeof(req.vht_capa));
        }
 
-       err = nl80211_crypto_settings(rdev, info, &crypto, 1);
+       err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
        if (!err)
-               err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
-                                         ssid, ssid_len, ie, ie_len, use_mfp,
-                                         &crypto, flags, ht_capa,
-                                         ht_capa_mask);
+               err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
+                                         ssid, ssid_len, &req);
 
        return err;
 }
@@ -6312,6 +6671,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                       sizeof(connect.ht_capa));
        }
 
+       if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+               connect.flags |= ASSOC_REQ_DISABLE_VHT;
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+               memcpy(&connect.vht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+                      sizeof(connect.vht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
+                       kfree(connkeys);
+                       return -EINVAL;
+               }
+               memcpy(&connect.vht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+                      sizeof(connect.vht_capa));
+       }
+
        err = cfg80211_connect(rdev, dev, &connect, connkeys);
        if (err)
                kfree(connkeys);
@@ -7085,6 +7462,9 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                        return err;
        }
 
+       if (setup.user_mpm)
+               cfg.auto_open_plinks = false;
+
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
                err = nl80211_parse_chandef(rdev, info, &setup.chandef);
                if (err)
@@ -7284,7 +7664,8 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
                return -EINVAL;
 
        if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
-                       rdev->wiphy.wowlan.tcp->data_interval_max)
+                       rdev->wiphy.wowlan.tcp->data_interval_max ||
+           nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
                return -EINVAL;
 
        wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
@@ -7769,6 +8150,54 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
        return 0;
 }
 
+static int nl80211_get_protocol_features(struct sk_buff *skb,
+                                        struct genl_info *info)
+{
+       void *hdr;
+       struct sk_buff *msg;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_GET_PROTOCOL_FEATURES);
+       if (!hdr)
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
+                       NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+       kfree_skb(msg);
+       return -ENOBUFS;
+}
+
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct cfg80211_update_ft_ies_params ft_params;
+       struct net_device *dev = info->user_ptr[1];
+
+       if (!rdev->ops->update_ft_ies)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_MDID] ||
+           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       memset(&ft_params, 0, sizeof(ft_params));
+       ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
+       ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+       ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+
+       return rdev_update_ft_ies(rdev, dev, &ft_params);
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -8445,6 +8874,19 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
+               .doit = nl80211_get_protocol_features,
+               .policy = nl80211_policy,
+       },
+       {
+               .cmd = NL80211_CMD_UPDATE_FT_IES,
+               .doit = nl80211_update_ft_ies,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -8472,7 +8914,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
        if (!msg)
                return;
 
-       if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
+       if (nl80211_send_wiphy(rdev, msg, 0, 0, 0,
+                              false, NULL, NULL, NULL) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -8796,21 +9239,31 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                                NL80211_CMD_DISASSOCIATE, gfp);
 }
 
-void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
-                               struct net_device *netdev, const u8 *buf,
-                               size_t len, gfp_t gfp)
+void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
+                                size_t len)
 {
-       nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_send_unprot_deauth(dev);
+       nl80211_send_mlme_event(rdev, dev, buf, len,
+                               NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
 }
+EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
 
-void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
-                                 struct net_device *netdev, const u8 *buf,
-                                 size_t len, gfp_t gfp)
+void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
+                                  size_t len)
 {
-       nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_send_unprot_disassoc(dev);
+       nl80211_send_mlme_event(rdev, dev, buf, len,
+                               NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
 }
+EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
 
 static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
@@ -9013,14 +9466,19 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
-               struct net_device *netdev,
-               const u8 *macaddr, const u8* ie, u8 ie_len,
-               gfp_t gfp)
+void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
+                                       const u8* ie, u8 ie_len, gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
+               return;
+
+       trace_cfg80211_notify_new_peer_candidate(dev, addr);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9032,8 +9490,8 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
-           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
            (ie_len && ie &&
             nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
                goto nla_put_failure;
@@ -9048,6 +9506,7 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
 
 void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
                                 struct net_device *netdev, const u8 *addr,
@@ -9116,7 +9575,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_before))
+       if (nl80211_msg_put_channel(msg, channel_before, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -9124,7 +9583,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_after))
+       if (nl80211_msg_put_channel(msg, channel_after, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -9186,31 +9645,42 @@ static void nl80211_send_remain_on_chan_event(
        nlmsg_free(msg);
 }
 
-void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
-                                   struct wireless_dev *wdev, u64 cookie,
-                                   struct ieee80211_channel *chan,
-                                   unsigned int duration, gfp_t gfp)
+void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
+                              struct ieee80211_channel *chan,
+                              unsigned int duration, gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
        nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
                                          rdev, wdev, cookie, chan,
                                          duration, gfp);
 }
+EXPORT_SYMBOL(cfg80211_ready_on_channel);
 
-void nl80211_send_remain_on_channel_cancel(
-       struct cfg80211_registered_device *rdev,
-       struct wireless_dev *wdev,
-       u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
+void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
+                                       struct ieee80211_channel *chan,
+                                       gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
        nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
                                          rdev, wdev, cookie, chan, 0, gfp);
 }
+EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
 
-void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
-                           struct net_device *dev, const u8 *mac_addr,
-                           struct station_info *sinfo, gfp_t gfp)
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+                     struct station_info *sinfo, gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
 
+       trace_cfg80211_new_sta(dev, mac_addr, sinfo);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9224,14 +9694,17 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
 }
+EXPORT_SYMBOL(cfg80211_new_sta);
 
-void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
-                               struct net_device *dev, const u8 *mac_addr,
-                               gfp_t gfp)
+void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
+       trace_cfg80211_del_sta(dev, mac_addr);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9256,12 +9729,14 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_del_sta);
 
-void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
-                                   struct net_device *dev, const u8 *mac_addr,
-                                   enum nl80211_connect_failed_reason reason,
-                                   gfp_t gfp)
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+                         enum nl80211_connect_failed_reason reason,
+                         gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -9290,6 +9765,7 @@ void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_conn_failed);
 
 static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
                                       const u8 *addr, gfp_t gfp)
@@ -9334,19 +9810,47 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
        return true;
 }
 
-bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+                               const u8 *addr, gfp_t gfp)
 {
-       return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
-                                         addr, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool ret;
+
+       trace_cfg80211_rx_spurious_frame(dev, addr);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+               trace_cfg80211_return_bool(false);
+               return false;
+       }
+       ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
+                                        addr, gfp);
+       trace_cfg80211_return_bool(ret);
+       return ret;
 }
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
 
-bool nl80211_unexpected_4addr_frame(struct net_device *dev,
-                                   const u8 *addr, gfp_t gfp)
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+                                       const u8 *addr, gfp_t gfp)
 {
-       return __nl80211_unexpected_frame(dev,
-                                         NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
-                                         addr, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool ret;
+
+       trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+                   wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
+               trace_cfg80211_return_bool(false);
+               return false;
+       }
+       ret = __nl80211_unexpected_frame(dev,
+                                        NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+                                        addr, gfp);
+       trace_cfg80211_return_bool(ret);
+       return ret;
 }
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
 
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
                      struct wireless_dev *wdev, u32 nlportid,
@@ -9386,15 +9890,17 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
        return -ENOBUFS;
 }
 
-void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
-                                struct wireless_dev *wdev, u64 cookie,
-                                const u8 *buf, size_t len, bool ack,
-                                gfp_t gfp)
+void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
+                            const u8 *buf, size_t len, bool ack, gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct net_device *netdev = wdev->netdev;
        struct sk_buff *msg;
        void *hdr;
 
+       trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9422,17 +9928,21 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
 
-void
-nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
-                            struct net_device *netdev,
-                            enum nl80211_cqm_rssi_threshold_event rssi_event,
-                            gfp_t gfp)
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+                             enum nl80211_cqm_rssi_threshold_event rssi_event,
+                             gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
 
+       trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9444,7 +9954,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
                goto nla_put_failure;
 
        pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
@@ -9467,10 +9977,11 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
 
-void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev, const u8 *bssid,
-                             const u8 *replay_ctr, gfp_t gfp)
+static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+                                    struct net_device *netdev, const u8 *bssid,
+                                    const u8 *replay_ctr, gfp_t gfp)
 {
        struct sk_buff *msg;
        struct nlattr *rekey_attr;
@@ -9512,9 +10023,22 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
-                                   struct net_device *netdev, int index,
-                                   const u8 *bssid, bool preauth, gfp_t gfp)
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+                              const u8 *replay_ctr, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_gtk_rekey_notify(dev, bssid);
+       nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
+
+static void
+nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
+                              struct net_device *netdev, int index,
+                              const u8 *bssid, bool preauth, gfp_t gfp)
 {
        struct sk_buff *msg;
        struct nlattr *attr;
@@ -9557,9 +10081,22 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev,
-                             struct cfg80211_chan_def *chandef, gfp_t gfp)
+void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
+                                    const u8 *bssid, bool preauth, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
+       nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
+}
+EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+
+static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+                                    struct net_device *netdev,
+                                    struct cfg80211_chan_def *chandef,
+                                    gfp_t gfp)
 {
        struct sk_buff *msg;
        void *hdr;
@@ -9591,11 +10128,36 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void
-nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
-                           struct net_device *netdev, const u8 *peer,
-                           u32 num_packets, u32 rate, u32 intvl, gfp_t gfp)
+void cfg80211_ch_switch_notify(struct net_device *dev,
+                              struct cfg80211_chan_def *chandef)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ch_switch_notify(dev, chandef);
+
+       wdev_lock(wdev);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO))
+               goto out;
+
+       wdev->channel = chandef->chan;
+       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
+out:
+       wdev_unlock(wdev);
+       return;
+}
+EXPORT_SYMBOL(cfg80211_ch_switch_notify);
+
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+                            const u8 *peer, u32 num_packets,
+                            u32 rate, u32 intvl, gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -9611,7 +10173,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
                goto nla_put_failure;
 
@@ -9640,6 +10202,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
 
 void
 nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -9692,15 +10255,18 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void
-nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
-                               struct net_device *netdev, const u8 *peer,
-                               u32 num_packets, gfp_t gfp)
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+                                const u8 *peer, u32 num_packets, gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
 
+       trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9712,7 +10278,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
                goto nla_put_failure;
 
@@ -9735,6 +10301,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
 
 void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
                           u64 cookie, bool acked, gfp_t gfp)
@@ -10021,6 +10588,50 @@ static struct notifier_block nl80211_netlink_notifier = {
        .notifier_call = nl80211_netlink_notify,
 };
 
+void cfg80211_ft_event(struct net_device *netdev,
+                      struct cfg80211_ft_event_params *ft_event)
+{
+       struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+       int err;
+
+       trace_cfg80211_ft_event(wiphy, netdev, ft_event);
+
+       if (!ft_event->target_ap)
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap);
+       if (ft_event->ies)
+               nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies);
+       if (ft_event->ric_ies)
+               nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
+                       ft_event->ric_ies);
+
+       err = genlmsg_end(msg, hdr);
+       if (err < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, GFP_KERNEL);
+}
+EXPORT_SYMBOL(cfg80211_ft_event);
+
 /* initialisation/exit functions */
 
 int nl80211_init(void)
index b061da4919e1c4dbeb8503282e123753b1270603..a4073e808c13c24bca525617d4de3cba8b18874e 100644 (file)
@@ -29,12 +29,6 @@ void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
 void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                           struct net_device *netdev,
                           const u8 *buf, size_t len, gfp_t gfp);
-void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
-                               struct net_device *netdev,
-                               const u8 *buf, size_t len, gfp_t gfp);
-void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
-                                 struct net_device *netdev,
-                                 const u8 *buf, size_t len, gfp_t gfp);
 void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
                               struct net_device *netdev,
                               const u8 *addr, gfp_t gfp);
@@ -54,10 +48,6 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
                               struct net_device *netdev, u16 reason,
                               const u8 *ie, size_t ie_len, bool from_ap);
 
-void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
-                                    struct net_device *netdev,
-                                    const u8 *macaddr, const u8* ie, u8 ie_len,
-                                    gfp_t gfp);
 void
 nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
                            struct net_device *netdev, const u8 *addr,
@@ -73,41 +63,10 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
                             struct net_device *netdev, const u8 *bssid,
                             gfp_t gfp);
 
-void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
-                                   struct wireless_dev *wdev, u64 cookie,
-                                   struct ieee80211_channel *chan,
-                                   unsigned int duration, gfp_t gfp);
-void nl80211_send_remain_on_channel_cancel(
-       struct cfg80211_registered_device *rdev,
-       struct wireless_dev *wdev,
-       u64 cookie, struct ieee80211_channel *chan, gfp_t gfp);
-
-void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
-                           struct net_device *dev, const u8 *mac_addr,
-                           struct station_info *sinfo, gfp_t gfp);
-void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
-                               struct net_device *dev, const u8 *mac_addr,
-                               gfp_t gfp);
-
-void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
-                                   struct net_device *dev, const u8 *mac_addr,
-                                   enum nl80211_connect_failed_reason reason,
-                                   gfp_t gfp);
-
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
                      struct wireless_dev *wdev, u32 nlpid,
                      int freq, int sig_dbm,
                      const u8 *buf, size_t len, gfp_t gfp);
-void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
-                                struct wireless_dev *wdev, u64 cookie,
-                                const u8 *buf, size_t len, bool ack,
-                                gfp_t gfp);
-
-void
-nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
-                            struct net_device *netdev,
-                            enum nl80211_cqm_rssi_threshold_event rssi_event,
-                            gfp_t gfp);
 
 void
 nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -115,31 +74,4 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
                     enum nl80211_radar_event event,
                     struct net_device *netdev, gfp_t gfp);
 
-void
-nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
-                               struct net_device *netdev, const u8 *peer,
-                               u32 num_packets, gfp_t gfp);
-
-void
-nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
-                           struct net_device *netdev, const u8 *peer,
-                           u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
-
-void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev, const u8 *bssid,
-                             const u8 *replay_ctr, gfp_t gfp);
-
-void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
-                                   struct net_device *netdev, int index,
-                                   const u8 *bssid, bool preauth, gfp_t gfp);
-
-void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *dev,
-                             struct cfg80211_chan_def *chandef, gfp_t gfp);
-
-bool nl80211_unexpected_frame(struct net_device *dev,
-                             const u8 *addr, gfp_t gfp);
-bool nl80211_unexpected_4addr_frame(struct net_device *dev,
-                                   const u8 *addr, gfp_t gfp);
-
 #endif /* __NET_WIRELESS_NL80211_H */
index 422d38291d66e3eb00c8cf2cdb5b9369182530fd..d77e1c1d3a0ed35770d4da5a1a1be19a93e4a6b2 100644 (file)
@@ -6,11 +6,12 @@
 #include "core.h"
 #include "trace.h"
 
-static inline int rdev_suspend(struct cfg80211_registered_device *rdev)
+static inline int rdev_suspend(struct cfg80211_registered_device *rdev,
+                              struct cfg80211_wowlan *wowlan)
 {
        int ret;
-       trace_rdev_suspend(&rdev->wiphy, rdev->wowlan);
-       ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan);
+       trace_rdev_suspend(&rdev->wiphy, wowlan);
+       ret = rdev->ops->suspend(&rdev->wiphy, wowlan);
        trace_rdev_return_int(&rdev->wiphy, ret);
        return ret;
 }
@@ -887,4 +888,17 @@ static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
        trace_rdev_return_int(&rdev->wiphy, ret);
        return ret;
 }
+
+static inline int rdev_update_ft_ies(struct cfg80211_registered_device *rdev,
+                                    struct net_device *dev,
+                                    struct cfg80211_update_ft_ies_params *ftie)
+{
+       int ret;
+
+       trace_rdev_update_ft_ies(&rdev->wiphy, dev, ftie);
+       ret = rdev->ops->update_ft_ies(&rdev->wiphy, dev, ftie);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
 #endif /* __CFG80211_RDEV_OPS */
index 98532c00242dd4a1a134e12f388a340434b38c04..e6df52dc8c6972a6c418f5fed9111e040b0e35b4 100644 (file)
@@ -184,14 +184,14 @@ static const struct ieee80211_regdomain world_regdom = {
                        NL80211_RRF_NO_IBSS |
                        NL80211_RRF_NO_OFDM),
                /* IEEE 802.11a, channel 36..48 */
-               REG_RULE(5180-10, 5240+10, 40, 6, 20,
+               REG_RULE(5180-10, 5240+10, 80, 6, 20,
                         NL80211_RRF_PASSIVE_SCAN |
                         NL80211_RRF_NO_IBSS),
 
-               /* NB: 5260 MHz - 5700 MHz requies DFS */
+               /* NB: 5260 MHz - 5700 MHz requires DFS */
 
                /* IEEE 802.11a, channel 149..165 */
-               REG_RULE(5745-10, 5825+10, 40, 6, 20,
+               REG_RULE(5745-10, 5825+10, 80, 6, 20,
                        NL80211_RRF_PASSIVE_SCAN |
                        NL80211_RRF_NO_IBSS),
 
index 482c70e7012727fe66e400872379e115b7a7caad..a9dc5c736df061466463e01bb890fed8f28e5389 100644 (file)
@@ -160,7 +160,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct cfg80211_connect_params *params;
-       const u8 *prev_bssid = NULL;
+       struct cfg80211_assoc_request req = {};
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -187,16 +187,20 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                BUG_ON(!rdev->ops->assoc);
                wdev->conn->state = CFG80211_CONN_ASSOCIATING;
                if (wdev->conn->prev_bssid_valid)
-                       prev_bssid = wdev->conn->prev_bssid;
-               err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
-                                           params->channel, params->bssid,
-                                           prev_bssid,
-                                           params->ssid, params->ssid_len,
-                                           params->ie, params->ie_len,
-                                           params->mfp != NL80211_MFP_NO,
-                                           &params->crypto,
-                                           params->flags, &params->ht_capa,
-                                           &params->ht_capa_mask);
+                       req.prev_bssid = wdev->conn->prev_bssid;
+               req.ie = params->ie;
+               req.ie_len = params->ie_len;
+               req.use_mfp = params->mfp != NL80211_MFP_NO;
+               req.crypto = params->crypto;
+               req.flags = params->flags;
+               req.ht_capa = params->ht_capa;
+               req.ht_capa_mask = params->ht_capa_mask;
+               req.vht_capa = params->vht_capa;
+               req.vht_capa_mask = params->vht_capa_mask;
+
+               err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
+                                           params->bssid, params->ssid,
+                                           params->ssid_len, &req);
                if (err)
                        __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                               NULL, 0,
@@ -232,7 +236,7 @@ void cfg80211_conn_work(struct work_struct *work)
                        wdev_unlock(wdev);
                        continue;
                }
-               if (wdev->sme_state != CFG80211_SME_CONNECTING) {
+               if (wdev->sme_state != CFG80211_SME_CONNECTING || !wdev->conn) {
                        wdev_unlock(wdev);
                        continue;
                }
index 238ee49b3868b8d675e3328f279608eee970fd7f..8f28b9f798d88a48281a7b633406b99bad4b5e98 100644 (file)
@@ -83,6 +83,14 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
        return 0;
 }
 
+static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
+{
+       struct wireless_dev *wdev;
+
+       list_for_each_entry(wdev, &rdev->wdev_list, list)
+               cfg80211_leave(rdev, wdev);
+}
+
 static int wiphy_suspend(struct device *dev, pm_message_t state)
 {
        struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
@@ -90,12 +98,19 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
 
        rdev->suspend_at = get_seconds();
 
-       if (rdev->ops->suspend) {
-               rtnl_lock();
-               if (rdev->wiphy.registered)
-                       ret = rdev_suspend(rdev);
-               rtnl_unlock();
+       rtnl_lock();
+       if (rdev->wiphy.registered) {
+               if (!rdev->wowlan)
+                       cfg80211_leave_all(rdev);
+               if (rdev->ops->suspend)
+                       ret = rdev_suspend(rdev, rdev->wowlan);
+               if (ret == 1) {
+                       /* Driver refuse to configure wowlan */
+                       cfg80211_leave_all(rdev);
+                       ret = rdev_suspend(rdev, NULL);
+               }
        }
+       rtnl_unlock();
 
        return ret;
 }
index 7586de77a2f8b57ebb9b832b76563ab66a59111a..3c2033b8f596675b69db5efeba07f6cae03dca0d 100644 (file)
@@ -1786,6 +1786,26 @@ TRACE_EVENT(rdev_set_mac_acl,
                  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy)
 );
 
+TRACE_EVENT(rdev_update_ft_ies,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                struct cfg80211_update_ft_ies_params *ftie),
+       TP_ARGS(wiphy, netdev, ftie),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               __field(u16, md)
+               __dynamic_array(u8, ie, ftie->ie_len)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               NETDEV_ASSIGN;
+               __entry->md = ftie->md;
+               memcpy(__get_dynamic_array(ie), ftie->ie, ftie->ie_len);
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", md: 0x%x",
+                 WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->md)
+);
+
 /*************************************************************
  *          cfg80211 exported functions traces              *
  *************************************************************/
@@ -2414,6 +2434,32 @@ TRACE_EVENT(cfg80211_report_wowlan_wakeup,
        TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
 );
 
+TRACE_EVENT(cfg80211_ft_event,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                struct cfg80211_ft_event_params *ft_event),
+       TP_ARGS(wiphy, netdev, ft_event),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               __dynamic_array(u8, ies, ft_event->ies_len)
+               MAC_ENTRY(target_ap)
+               __dynamic_array(u8, ric_ies, ft_event->ric_ies_len)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               NETDEV_ASSIGN;
+               if (ft_event->ies)
+                       memcpy(__get_dynamic_array(ies), ft_event->ies,
+                              ft_event->ies_len);
+               MAC_ASSIGN(target_ap, ft_event->target_ap);
+               if (ft_event->ric_ies)
+                       memcpy(__get_dynamic_array(ric_ies), ft_event->ric_ies,
+                              ft_event->ric_ies_len);
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", target_ap: " MAC_PR_FMT,
+                 WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))
+);
+
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH
index 37a56ee1e1ede8cc5f698901edabe26a6b397f62..6cbac99ae03d25e2714eaa1b043f362f3e245387 100644 (file)
@@ -511,7 +511,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
                encaps_data = bridge_tunnel_header;
                encaps_len = sizeof(bridge_tunnel_header);
                skip_header_bytes -= 2;
-       } else if (ethertype > 0x600) {
+       } else if (ethertype >= ETH_P_802_3_MIN) {
                encaps_data = rfc1042_header;
                encaps_len = sizeof(rfc1042_header);
                skip_header_bytes -= 2;
index 167c67d46c6a56a87baed9d3f6be17d137ffbe1a..23cea0f74336c72b332e909994e242d216e5e2ae 100644 (file)
@@ -1037,6 +1037,24 @@ __xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir
        return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
 }
 
+static int flow_to_policy_dir(int dir)
+{
+       if (XFRM_POLICY_IN == FLOW_DIR_IN &&
+           XFRM_POLICY_OUT == FLOW_DIR_OUT &&
+           XFRM_POLICY_FWD == FLOW_DIR_FWD)
+               return dir;
+
+       switch (dir) {
+       default:
+       case FLOW_DIR_IN:
+               return XFRM_POLICY_IN;
+       case FLOW_DIR_OUT:
+               return XFRM_POLICY_OUT;
+       case FLOW_DIR_FWD:
+               return XFRM_POLICY_FWD;
+       }
+}
+
 static struct flow_cache_object *
 xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family,
                   u8 dir, struct flow_cache_object *old_obj, void *ctx)
@@ -1046,7 +1064,7 @@ xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family,
        if (old_obj)
                xfrm_pol_put(container_of(old_obj, struct xfrm_policy, flo));
 
-       pol = __xfrm_policy_lookup(net, fl, family, dir);
+       pol = __xfrm_policy_lookup(net, fl, family, flow_to_policy_dir(dir));
        if (IS_ERR_OR_NULL(pol))
                return ERR_CAST(pol);
 
@@ -1932,7 +1950,8 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
         * previous cache entry */
        if (xdst == NULL) {
                num_pols = 1;
-               pols[0] = __xfrm_policy_lookup(net, fl, family, dir);
+               pols[0] = __xfrm_policy_lookup(net, fl, family,
+                                              flow_to_policy_dir(dir));
                err = xfrm_expand_policies(fl, family, pols,
                                           &num_pols, &num_xfrms);
                if (err < 0)
index 2c341bdaf47cbabfe1b8801e65a3b4a2ea571a66..78f66fa92449c92865dfa8314020854ed704744f 100644 (file)
@@ -1187,6 +1187,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
                goto error;
 
        x->props.flags = orig->props.flags;
+       x->props.extra_flags = orig->props.extra_flags;
 
        x->curlft.add_time = orig->curlft.add_time;
        x->km.state = orig->km.state;
index fbd9e6cd0fd7a1297d99b48c37cc6de8ce938bbf..aa778748c56592728866ce24096bf46447c2d13c 100644 (file)
@@ -515,6 +515,9 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
 
        copy_from_user_state(x, p);
 
+       if (attrs[XFRMA_SA_EXTRA_FLAGS])
+               x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]);
+
        if ((err = attach_aead(&x->aead, &x->props.ealgo,
                               attrs[XFRMA_ALG_AEAD])))
                goto error;
@@ -779,6 +782,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
 
        copy_to_user_state(x, p);
 
+       if (x->props.extra_flags) {
+               ret = nla_put_u32(skb, XFRMA_SA_EXTRA_FLAGS,
+                                 x->props.extra_flags);
+               if (ret)
+                       goto out;
+       }
+
        if (x->coaddr) {
                ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr);
                if (ret)
@@ -2302,9 +2312,10 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
        [XFRMA_MARK]            = { .len = sizeof(struct xfrm_mark) },
        [XFRMA_TFCPAD]          = { .type = NLA_U32 },
        [XFRMA_REPLAY_ESN_VAL]  = { .len = sizeof(struct xfrm_replay_state_esn) },
+       [XFRMA_SA_EXTRA_FLAGS]  = { .type = NLA_U32 },
 };
 
-static struct xfrm_link {
+static const struct xfrm_link {
        int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
        int (*dump)(struct sk_buff *, struct netlink_callback *);
        int (*done)(struct netlink_callback *);
@@ -2338,7 +2349,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *attrs[XFRMA_MAX+1];
-       struct xfrm_link *link;
+       const struct xfrm_link *link;
        int type, err;
 
        type = nlh->nlmsg_type;
@@ -2495,6 +2506,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
                                    x->security->ctx_len);
        if (x->coaddr)
                l += nla_total_size(sizeof(*x->coaddr));
+       if (x->props.extra_flags)
+               l += nla_total_size(sizeof(x->props.extra_flags));
 
        /* Must count x->lastused as it may become non-zero behind our back. */
        l += nla_total_size(sizeof(u64));
index 7171a957b9335694c1bbd1c5c9a41e2ec13bbda9..bf889ee51509654f1ac6550a54eec7cfa6be861d 100644 (file)
@@ -61,7 +61,7 @@
 #include <linux/bitops.h>
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>   /* for network interface checks */
-#include <linux/netlink.h>
+#include <net/netlink.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/dccp.h>
@@ -4481,7 +4481,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
        struct nlmsghdr *nlh;
        struct sk_security_struct *sksec = sk->sk_security;
 
-       if (skb->len < NLMSG_SPACE(0)) {
+       if (skb->len < NLMSG_HDRLEN) {
                err = -EINVAL;
                goto out;
        }
index 14d810ead42078482807666a41902e4cb24161c0..828fb6a4e9410e6a0bae91c2126a7645db4f4daa 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/kernel.h>
 #include <linux/export.h>
 #include <linux/skbuff.h>
-#include <linux/netlink.h>
 #include <linux/selinux_netlink.h>
 #include <net/net_namespace.h>
 #include <net/netlink.h>
@@ -77,7 +76,7 @@ static void selnl_notify(int msgtype, void *data)
 
        len = selnl_msglen(msgtype);
 
-       skb = alloc_skb(NLMSG_SPACE(len), GFP_USER);
+       skb = nlmsg_new(len, GFP_USER);
        if (!skb)
                goto oom;
 
index fa36565b209d80e8e07c14a6e48dd87a5d5d14de..c73c6357481cb9b0a662fef125ae2113067dc8e3 100644 (file)
@@ -12,6 +12,7 @@ help:
        @echo '  turbostat  - Intel CPU idle stats and freq reporting tool'
        @echo '  usb        - USB testing tools'
        @echo '  virtio     - vhost test module'
+       @echo '  net        - misc networking tools'
        @echo '  vm         - misc vm tools'
        @echo '  x86_energy_perf_policy - Intel energy policy tool'
        @echo ''
@@ -34,7 +35,7 @@ help:
 cpupower: FORCE
        $(call descend,power/$@)
 
-cgroup firewire lguest perf usb virtio vm: FORCE
+cgroup firewire lguest perf usb virtio vm net: FORCE
        $(call descend,$@)
 
 selftests: FORCE
@@ -46,7 +47,7 @@ turbostat x86_energy_perf_policy: FORCE
 cpupower_install:
        $(call descend,power/$(@:_install=),install)
 
-cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install:
+cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install net_install:
        $(call descend,$(@:_install=),install)
 
 selftests_install:
@@ -57,12 +58,12 @@ turbostat_install x86_energy_perf_policy_install:
 
 install: cgroup_install cpupower_install firewire_install lguest_install \
                perf_install selftests_install turbostat_install usb_install \
-               virtio_install vm_install x86_energy_perf_policy_install
+               virtio_install vm_install net_install x86_energy_perf_policy_install
 
 cpupower_clean:
        $(call descend,power/cpupower,clean)
 
-cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean:
+cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean net_clean:
        $(call descend,$(@:_clean=),clean)
 
 selftests_clean:
@@ -73,6 +74,6 @@ turbostat_clean x86_energy_perf_policy_clean:
 
 clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \
                selftests_clean turbostat_clean usb_clean virtio_clean \
-               vm_clean x86_energy_perf_policy_clean
+               vm_clean net_clean x86_energy_perf_policy_clean
 
 .PHONY: FORCE
diff --git a/tools/net/Makefile b/tools/net/Makefile
new file mode 100644 (file)
index 0000000..b4444d5
--- /dev/null
@@ -0,0 +1,15 @@
+prefix = /usr
+
+CC = gcc
+
+all : bpf_jit_disasm
+
+bpf_jit_disasm : CFLAGS = -Wall -O2
+bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
+bpf_jit_disasm : bpf_jit_disasm.o
+
+clean :
+       rm -rf *.o bpf_jit_disasm
+
+install :
+       install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm
diff --git a/tools/net/bpf_jit_disasm.c b/tools/net/bpf_jit_disasm.c
new file mode 100644 (file)
index 0000000..cfe0cdc
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Minimal BPF JIT image disassembler
+ *
+ * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
+ * debugging or verification purposes.
+ *
+ * To get the disassembly of the JIT code, do the following:
+ *
+ *  1) `echo 2 > /proc/sys/net/core/bpf_jit_enable`
+ *  2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`)
+ *  3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
+ *
+ * Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
+ * Licensed under the GNU General Public License, version 2.0 (GPLv2)
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <bfd.h>
+#include <dis-asm.h>
+#include <sys/klog.h>
+#include <sys/types.h>
+#include <regex.h>
+
+static void get_exec_path(char *tpath, size_t size)
+{
+       char *path;
+       ssize_t len;
+
+       snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
+       tpath[size - 1] = 0;
+
+       path = strdup(tpath);
+       assert(path);
+
+       len = readlink(path, tpath, size);
+       tpath[len] = 0;
+
+       free(path);
+}
+
+static void get_asm_insns(uint8_t *image, size_t len, unsigned long base,
+                         int opcodes)
+{
+       int count, i, pc = 0;
+       char tpath[256];
+       struct disassemble_info info;
+       disassembler_ftype disassemble;
+       bfd *bfdf;
+
+       memset(tpath, 0, sizeof(tpath));
+       get_exec_path(tpath, sizeof(tpath));
+
+       bfdf = bfd_openr(tpath, NULL);
+       assert(bfdf);
+       assert(bfd_check_format(bfdf, bfd_object));
+
+       init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
+       info.arch = bfd_get_arch(bfdf);
+       info.mach = bfd_get_mach(bfdf);
+       info.buffer = image;
+       info.buffer_length = len;
+
+       disassemble_init_for_target(&info);
+
+       disassemble = disassembler(bfdf);
+       assert(disassemble);
+
+       do {
+               printf("%4x:\t", pc);
+
+               count = disassemble(pc, &info);
+
+               if (opcodes) {
+                       printf("\n\t");
+                       for (i = 0; i < count; ++i)
+                               printf("%02x ", (uint8_t) image[pc + i]);
+               }
+               printf("\n");
+
+               pc += count;
+       } while(count > 0 && pc < len);
+
+       bfd_close(bfdf);
+}
+
+static char *get_klog_buff(int *klen)
+{
+       int ret, len = klogctl(10, NULL, 0);
+       char *buff = malloc(len);
+
+       assert(buff && klen);
+       ret = klogctl(3, buff, len);
+       assert(ret >= 0);
+       *klen = ret;
+
+       return buff;
+}
+
+static void put_klog_buff(char *buff)
+{
+       free(buff);
+}
+
+static int get_last_jit_image(char *haystack, size_t hlen,
+                             uint8_t *image, size_t ilen,
+                             unsigned long *base)
+{
+       char *ptr, *pptr, *tmp;
+       off_t off = 0;
+       int ret, flen, proglen, pass, ulen = 0;
+       regmatch_t pmatch[1];
+       regex_t regex;
+
+       if (hlen == 0)
+               return 0;
+
+       ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
+                     "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
+       assert(ret == 0);
+
+       ptr = haystack;
+       while (1) {
+               ret = regexec(&regex, ptr, 1, pmatch, 0);
+               if (ret == 0) {
+                       ptr += pmatch[0].rm_eo;
+                       off += pmatch[0].rm_eo;
+                       assert(off < hlen);
+               } else
+                       break;
+       }
+
+       ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
+       ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
+                    &flen, &proglen, &pass, base);
+       if (ret != 4)
+               return 0;
+
+       tmp = ptr = haystack + off;
+       while ((ptr = strtok(tmp, "\n")) != NULL && ulen < ilen) {
+               tmp = NULL;
+               if (!strstr(ptr, "JIT code"))
+                       continue;
+               pptr = ptr;
+               while ((ptr = strstr(pptr, ":")))
+                       pptr = ptr + 1;
+               ptr = pptr;
+               do {
+                       image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
+                       if (ptr == pptr || ulen >= ilen) {
+                               ulen--;
+                               break;
+                       }
+                       ptr = pptr;
+               } while (1);
+       }
+
+       assert(ulen == proglen);
+       printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
+              proglen, pass, flen);
+       printf("%lx + <x>:\n", *base);
+
+       regfree(&regex);
+       return ulen;
+}
+
+int main(int argc, char **argv)
+{
+       int len, klen, opcodes = 0;
+       char *kbuff;
+       unsigned long base;
+       uint8_t image[4096];
+
+       if (argc > 1) {
+               if (!strncmp("-o", argv[argc - 1], 2)) {
+                       opcodes = 1;
+               } else {
+                       printf("usage: bpf_jit_disasm [-o: show opcodes]\n");
+                       exit(0);
+               }
+       }
+
+       bfd_init();
+       memset(image, 0, sizeof(image));
+
+       kbuff = get_klog_buff(&klen);
+
+       len = get_last_jit_image(kbuff, klen, image, sizeof(image), &base);
+       if (len > 0 && base > 0)
+               get_asm_insns(image, len, base, opcodes);
+
+       put_klog_buff(kbuff);
+
+       return 0;
+}
index 3cc0ad7ae863d09eec9a4c536c93b1d2c38c916c..a4805932972b3b1a610c7914d91ab290e3bcc967 100644 (file)
@@ -5,6 +5,7 @@ TARGETS += vm
 TARGETS += cpu-hotplug
 TARGETS += memory-hotplug
 TARGETS += efivarfs
+TARGETS += net
 
 all:
        for TARGET in $(TARGETS); do \
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
new file mode 100644 (file)
index 0000000..0032662
--- /dev/null
@@ -0,0 +1,3 @@
+socket
+psock_fanout
+psock_tpacket
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
new file mode 100644 (file)
index 0000000..750512b
--- /dev/null
@@ -0,0 +1,19 @@
+# Makefile for net selftests
+
+CC = $(CROSS_COMPILE)gcc
+CFLAGS = -Wall -O2 -g
+
+CFLAGS += -I../../../../usr/include/
+
+NET_PROGS = socket psock_fanout psock_tpacket
+
+all: $(NET_PROGS)
+%: %.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+run_tests: all
+       @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]"
+       @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]"
+
+clean:
+       $(RM) $(NET_PROGS)
diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c
new file mode 100644 (file)
index 0000000..57b9c2b
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2013 Google Inc.
+ * Author: Willem de Bruijn (willemb@google.com)
+ *
+ * A basic test of packet socket fanout behavior.
+ *
+ * Control:
+ * - create fanout fails as expected with illegal flag combinations
+ * - join   fanout fails as expected with diverging types or flags
+ *
+ * Datapath:
+ *   Open a pair of packet sockets and a pair of INET sockets, send a known
+ *   number of packets across the two INET sockets and count the number of
+ *   packets enqueued onto the two packet sockets.
+ *
+ *   The test currently runs for
+ *   - PACKET_FANOUT_HASH
+ *   - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER
+ *   - PACKET_FANOUT_LB
+ *   - PACKET_FANOUT_CPU
+ *   - PACKET_FANOUT_ROLLOVER
+ *
+ * Todo:
+ * - functionality: PACKET_FANOUT_FLAG_DEFRAG
+ *
+ * License (GPLv2):
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE            /* for sched_setaffinity */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/filter.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <poll.h>
+#include <sched.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "psock_lib.h"
+
+#define RING_NUM_FRAMES                        20
+
+/* Open a socket in a given fanout mode.
+ * @return -1 if mode is bad, a valid socket otherwise */
+static int sock_fanout_open(uint16_t typeflags, int num_packets)
+{
+       int fd, val;
+
+       fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+       if (fd < 0) {
+               perror("socket packet");
+               exit(1);
+       }
+
+       /* fanout group ID is always 0: tests whether old groups are deleted */
+       val = ((int) typeflags) << 16;
+       if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) {
+               if (close(fd)) {
+                       perror("close packet");
+                       exit(1);
+               }
+               return -1;
+       }
+
+       pair_udp_setfilter(fd);
+       return fd;
+}
+
+static char *sock_fanout_open_ring(int fd)
+{
+       struct tpacket_req req = {
+               .tp_block_size = getpagesize(),
+               .tp_frame_size = getpagesize(),
+               .tp_block_nr   = RING_NUM_FRAMES,
+               .tp_frame_nr   = RING_NUM_FRAMES,
+       };
+       char *ring;
+       int val = TPACKET_V2;
+
+       if (setsockopt(fd, SOL_PACKET, PACKET_VERSION, (void *) &val,
+                      sizeof(val))) {
+               perror("packetsock ring setsockopt version");
+               exit(1);
+       }
+       if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req,
+                      sizeof(req))) {
+               perror("packetsock ring setsockopt");
+               exit(1);
+       }
+
+       ring = mmap(0, req.tp_block_size * req.tp_block_nr,
+                   PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       if (!ring) {
+               fprintf(stderr, "packetsock ring mmap\n");
+               exit(1);
+       }
+
+       return ring;
+}
+
+static int sock_fanout_read_ring(int fd, void *ring)
+{
+       struct tpacket2_hdr *header = ring;
+       int count = 0;
+
+       while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) {
+               count++;
+               header = ring + (count * getpagesize());
+       }
+
+       return count;
+}
+
+static int sock_fanout_read(int fds[], char *rings[], const int expect[])
+{
+       int ret[2];
+
+       ret[0] = sock_fanout_read_ring(fds[0], rings[0]);
+       ret[1] = sock_fanout_read_ring(fds[1], rings[1]);
+
+       fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n",
+                       ret[0], ret[1], expect[0], expect[1]);
+
+       if ((!(ret[0] == expect[0] && ret[1] == expect[1])) &&
+           (!(ret[0] == expect[1] && ret[1] == expect[0]))) {
+               fprintf(stderr, "ERROR: incorrect queue lengths\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+/* Test illegal mode + flag combination */
+static void test_control_single(void)
+{
+       fprintf(stderr, "test: control single socket\n");
+
+       if (sock_fanout_open(PACKET_FANOUT_ROLLOVER |
+                              PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) {
+               fprintf(stderr, "ERROR: opened socket with dual rollover\n");
+               exit(1);
+       }
+}
+
+/* Test illegal group with different modes or flags */
+static void test_control_group(void)
+{
+       int fds[2];
+
+       fprintf(stderr, "test: control multiple sockets\n");
+
+       fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20);
+       if (fds[0] == -1) {
+               fprintf(stderr, "ERROR: failed to open HASH socket\n");
+               exit(1);
+       }
+       if (sock_fanout_open(PACKET_FANOUT_HASH |
+                              PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) {
+               fprintf(stderr, "ERROR: joined group with wrong flag defrag\n");
+               exit(1);
+       }
+       if (sock_fanout_open(PACKET_FANOUT_HASH |
+                              PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) {
+               fprintf(stderr, "ERROR: joined group with wrong flag ro\n");
+               exit(1);
+       }
+       if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) {
+               fprintf(stderr, "ERROR: joined group with wrong mode\n");
+               exit(1);
+       }
+       fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20);
+       if (fds[1] == -1) {
+               fprintf(stderr, "ERROR: failed to join group\n");
+               exit(1);
+       }
+       if (close(fds[1]) || close(fds[0])) {
+               fprintf(stderr, "ERROR: closing sockets\n");
+               exit(1);
+       }
+}
+
+static int test_datapath(uint16_t typeflags, int port_off,
+                        const int expect1[], const int expect2[])
+{
+       const int expect0[] = { 0, 0 };
+       char *rings[2];
+       int fds[2], fds_udp[2][2], ret;
+
+       fprintf(stderr, "test: datapath 0x%hx\n", typeflags);
+
+       fds[0] = sock_fanout_open(typeflags, 20);
+       fds[1] = sock_fanout_open(typeflags, 20);
+       if (fds[0] == -1 || fds[1] == -1) {
+               fprintf(stderr, "ERROR: failed open\n");
+               exit(1);
+       }
+       rings[0] = sock_fanout_open_ring(fds[0]);
+       rings[1] = sock_fanout_open_ring(fds[1]);
+       pair_udp_open(fds_udp[0], PORT_BASE);
+       pair_udp_open(fds_udp[1], PORT_BASE + port_off);
+       sock_fanout_read(fds, rings, expect0);
+
+       /* Send data, but not enough to overflow a queue */
+       pair_udp_send(fds_udp[0], 15);
+       pair_udp_send(fds_udp[1], 5);
+       ret = sock_fanout_read(fds, rings, expect1);
+
+       /* Send more data, overflow the queue */
+       pair_udp_send(fds_udp[0], 15);
+       /* TODO: ensure consistent order between expect1 and expect2 */
+       ret |= sock_fanout_read(fds, rings, expect2);
+
+       if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) ||
+           munmap(rings[0], RING_NUM_FRAMES * getpagesize())) {
+               fprintf(stderr, "close rings\n");
+               exit(1);
+       }
+       if (close(fds_udp[1][1]) || close(fds_udp[1][0]) ||
+           close(fds_udp[0][1]) || close(fds_udp[0][0]) ||
+           close(fds[1]) || close(fds[0])) {
+               fprintf(stderr, "close datapath\n");
+               exit(1);
+       }
+
+       return ret;
+}
+
+static int set_cpuaffinity(int cpuid)
+{
+       cpu_set_t mask;
+
+       CPU_ZERO(&mask);
+       CPU_SET(cpuid, &mask);
+       if (sched_setaffinity(0, sizeof(mask), &mask)) {
+               if (errno != EINVAL) {
+                       fprintf(stderr, "setaffinity %d\n", cpuid);
+                       exit(1);
+               }
+               return 1;
+       }
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       const int expect_hash[2][2]     = { { 15, 5 },  { 20, 5 } };
+       const int expect_hash_rb[2][2]  = { { 15, 5 },  { 20, 15 } };
+       const int expect_lb[2][2]       = { { 10, 10 }, { 18, 17 } };
+       const int expect_rb[2][2]       = { { 20, 0 },  { 20, 15 } };
+       const int expect_cpu0[2][2]     = { { 20, 0 },  { 20, 0 } };
+       const int expect_cpu1[2][2]     = { { 0, 20 },  { 0, 20 } };
+       int port_off = 2, tries = 5, ret;
+
+       test_control_single();
+       test_control_group();
+
+       /* find a set of ports that do not collide onto the same socket */
+       ret = test_datapath(PACKET_FANOUT_HASH, port_off,
+                           expect_hash[0], expect_hash[1]);
+       while (ret && tries--) {
+               fprintf(stderr, "info: trying alternate ports (%d)\n", tries);
+               ret = test_datapath(PACKET_FANOUT_HASH, ++port_off,
+                                   expect_hash[0], expect_hash[1]);
+       }
+
+       ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER,
+                            port_off, expect_hash_rb[0], expect_hash_rb[1]);
+       ret |= test_datapath(PACKET_FANOUT_LB,
+                            port_off, expect_lb[0], expect_lb[1]);
+       ret |= test_datapath(PACKET_FANOUT_ROLLOVER,
+                            port_off, expect_rb[0], expect_rb[1]);
+
+       set_cpuaffinity(0);
+       ret |= test_datapath(PACKET_FANOUT_CPU, port_off,
+                            expect_cpu0[0], expect_cpu0[1]);
+       if (!set_cpuaffinity(1))
+               /* TODO: test that choice alternates with previous */
+               ret |= test_datapath(PACKET_FANOUT_CPU, port_off,
+                                    expect_cpu1[0], expect_cpu1[1]);
+
+       if (ret)
+               return 1;
+
+       printf("OK. All tests passed\n");
+       return 0;
+}
diff --git a/tools/testing/selftests/net/psock_lib.h b/tools/testing/selftests/net/psock_lib.h
new file mode 100644 (file)
index 0000000..37da54a
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2013 Google Inc.
+ * Author: Willem de Bruijn <willemb@google.com>
+ *         Daniel Borkmann <dborkman@redhat.com>
+ *
+ * License (GPLv2):
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PSOCK_LIB_H
+#define PSOCK_LIB_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#define DATA_LEN                       100
+#define DATA_CHAR                      'a'
+
+#define PORT_BASE                      8000
+
+#ifndef __maybe_unused
+# define __maybe_unused                __attribute__ ((__unused__))
+#endif
+
+static __maybe_unused void pair_udp_setfilter(int fd)
+{
+       struct sock_filter bpf_filter[] = {
+               { 0x80, 0, 0, 0x00000000 },  /* LD  pktlen                    */
+               { 0x35, 0, 5, DATA_LEN   },  /* JGE DATA_LEN  [f goto nomatch]*/
+               { 0x30, 0, 0, 0x00000050 },  /* LD  ip[80]                    */
+               { 0x15, 0, 3, DATA_CHAR  },  /* JEQ DATA_CHAR [f goto nomatch]*/
+               { 0x30, 0, 0, 0x00000051 },  /* LD  ip[81]                    */
+               { 0x15, 0, 1, DATA_CHAR  },  /* JEQ DATA_CHAR [f goto nomatch]*/
+               { 0x06, 0, 0, 0x00000060 },  /* RET match                     */
+               { 0x06, 0, 0, 0x00000000 },  /* RET no match                  */
+       };
+       struct sock_fprog bpf_prog;
+
+       bpf_prog.filter = bpf_filter;
+       bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter);
+       if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog,
+                      sizeof(bpf_prog))) {
+               perror("setsockopt SO_ATTACH_FILTER");
+               exit(1);
+       }
+}
+
+static __maybe_unused void pair_udp_open(int fds[], uint16_t port)
+{
+       struct sockaddr_in saddr, daddr;
+
+       fds[0] = socket(PF_INET, SOCK_DGRAM, 0);
+       fds[1] = socket(PF_INET, SOCK_DGRAM, 0);
+       if (fds[0] == -1 || fds[1] == -1) {
+               fprintf(stderr, "ERROR: socket dgram\n");
+               exit(1);
+       }
+
+       memset(&saddr, 0, sizeof(saddr));
+       saddr.sin_family = AF_INET;
+       saddr.sin_port = htons(port);
+       saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+       memset(&daddr, 0, sizeof(daddr));
+       daddr.sin_family = AF_INET;
+       daddr.sin_port = htons(port + 1);
+       daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+       /* must bind both to get consistent hash result */
+       if (bind(fds[1], (void *) &daddr, sizeof(daddr))) {
+               perror("bind");
+               exit(1);
+       }
+       if (bind(fds[0], (void *) &saddr, sizeof(saddr))) {
+               perror("bind");
+               exit(1);
+       }
+       if (connect(fds[0], (void *) &daddr, sizeof(daddr))) {
+               perror("connect");
+               exit(1);
+       }
+}
+
+static __maybe_unused void pair_udp_send(int fds[], int num)
+{
+       char buf[DATA_LEN], rbuf[DATA_LEN];
+
+       memset(buf, DATA_CHAR, sizeof(buf));
+       while (num--) {
+               /* Should really handle EINTR and EAGAIN */
+               if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) {
+                       fprintf(stderr, "ERROR: send failed left=%d\n", num);
+                       exit(1);
+               }
+               if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) {
+                       fprintf(stderr, "ERROR: recv failed left=%d\n", num);
+                       exit(1);
+               }
+               if (memcmp(buf, rbuf, sizeof(buf))) {
+                       fprintf(stderr, "ERROR: data failed left=%d\n", num);
+                       exit(1);
+               }
+       }
+}
+
+static __maybe_unused void pair_udp_close(int fds[])
+{
+       close(fds[0]);
+       close(fds[1]);
+}
+
+#endif /* PSOCK_LIB_H */
diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c
new file mode 100644 (file)
index 0000000..a8d7ffa
--- /dev/null
@@ -0,0 +1,824 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ * Author: Daniel Borkmann <dborkman@redhat.com>
+ *
+ * A basic test of packet socket's TPACKET_V1/TPACKET_V2/TPACKET_V3 behavior.
+ *
+ * Control:
+ *   Test the setup of the TPACKET socket with different patterns that are
+ *   known to fail (TODO) resp. succeed (OK).
+ *
+ * Datapath:
+ *   Open a pair of packet sockets and send resp. receive an a priori known
+ *   packet pattern accross the sockets and check if it was received resp.
+ *   sent correctly. Fanout in combination with RX_RING is currently not
+ *   tested here.
+ *
+ *   The test currently runs for
+ *   - TPACKET_V1: RX_RING, TX_RING
+ *   - TPACKET_V2: RX_RING, TX_RING
+ *   - TPACKET_V3: RX_RING
+ *
+ * License (GPLv2):
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <linux/if_packet.h>
+#include <linux/filter.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <bits/wordsize.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <net/if.h>
+#include <inttypes.h>
+#include <poll.h>
+
+#include "psock_lib.h"
+
+#ifndef bug_on
+# define bug_on(cond)          assert(!(cond))
+#endif
+
+#ifndef __aligned_tpacket
+# define __aligned_tpacket     __attribute__((aligned(TPACKET_ALIGNMENT)))
+#endif
+
+#ifndef __align_tpacket
+# define __align_tpacket(x)    __attribute__((aligned(TPACKET_ALIGN(x))))
+#endif
+
+#define BLOCK_STATUS(x)                ((x)->h1.block_status)
+#define BLOCK_NUM_PKTS(x)      ((x)->h1.num_pkts)
+#define BLOCK_O2FP(x)          ((x)->h1.offset_to_first_pkt)
+#define BLOCK_LEN(x)           ((x)->h1.blk_len)
+#define BLOCK_SNUM(x)          ((x)->h1.seq_num)
+#define BLOCK_O2PRIV(x)                ((x)->offset_to_priv)
+#define BLOCK_PRIV(x)          ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x)))
+#define BLOCK_HDR_LEN          (ALIGN_8(sizeof(struct block_desc)))
+#define ALIGN_8(x)             (((x) + 8 - 1) & ~(8 - 1))
+#define BLOCK_PLUS_PRIV(sz_pri)        (BLOCK_HDR_LEN + ALIGN_8((sz_pri)))
+
+#define NUM_PACKETS            100
+
+struct ring {
+       struct iovec *rd;
+       uint8_t *mm_space;
+       size_t mm_len, rd_len;
+       struct sockaddr_ll ll;
+       void (*walk)(int sock, struct ring *ring);
+       int type, rd_num, flen, version;
+       union {
+               struct tpacket_req  req;
+               struct tpacket_req3 req3;
+       };
+};
+
+struct block_desc {
+       uint32_t version;
+       uint32_t offset_to_priv;
+       struct tpacket_hdr_v1 h1;
+};
+
+union frame_map {
+       struct {
+               struct tpacket_hdr tp_h __aligned_tpacket;
+               struct sockaddr_ll s_ll __align_tpacket(sizeof(struct tpacket_hdr));
+       } *v1;
+       struct {
+               struct tpacket2_hdr tp_h __aligned_tpacket;
+               struct sockaddr_ll s_ll __align_tpacket(sizeof(struct tpacket2_hdr));
+       } *v2;
+       void *raw;
+};
+
+static unsigned int total_packets, total_bytes;
+
+static int pfsocket(int ver)
+{
+       int ret, sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+       if (sock == -1) {
+               perror("socket");
+               exit(1);
+       }
+
+       ret = setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver));
+       if (ret == -1) {
+               perror("setsockopt");
+               exit(1);
+       }
+
+       return sock;
+}
+
+static void status_bar_update(void)
+{
+       if (total_packets % 10 == 0) {
+               fprintf(stderr, ".");
+               fflush(stderr);
+       }
+}
+
+static void test_payload(void *pay, size_t len)
+{
+       struct ethhdr *eth = pay;
+
+       if (len < sizeof(struct ethhdr)) {
+               fprintf(stderr, "test_payload: packet too "
+                       "small: %zu bytes!\n", len);
+               exit(1);
+       }
+
+       if (eth->h_proto != htons(ETH_P_IP)) {
+               fprintf(stderr, "test_payload: wrong ethernet "
+                       "type: 0x%x!\n", ntohs(eth->h_proto));
+               exit(1);
+       }
+}
+
+static void create_payload(void *pay, size_t *len)
+{
+       int i;
+       struct ethhdr *eth = pay;
+       struct iphdr *ip = pay + sizeof(*eth);
+
+       /* Lets create some broken crap, that still passes
+        * our BPF filter.
+        */
+
+       *len = DATA_LEN + 42;
+
+       memset(pay, 0xff, ETH_ALEN * 2);
+       eth->h_proto = htons(ETH_P_IP);
+
+       for (i = 0; i < sizeof(*ip); ++i)
+               ((uint8_t *) pay)[i + sizeof(*eth)] = (uint8_t) rand();
+
+       ip->ihl = 5;
+       ip->version = 4;
+       ip->protocol = 0x11;
+       ip->frag_off = 0;
+       ip->ttl = 64;
+       ip->tot_len = htons((uint16_t) *len - sizeof(*eth));
+
+       ip->saddr = htonl(INADDR_LOOPBACK);
+       ip->daddr = htonl(INADDR_LOOPBACK);
+
+       memset(pay + sizeof(*eth) + sizeof(*ip),
+              DATA_CHAR, DATA_LEN);
+}
+
+static inline int __v1_rx_kernel_ready(struct tpacket_hdr *hdr)
+{
+       return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER);
+}
+
+static inline void __v1_rx_user_ready(struct tpacket_hdr *hdr)
+{
+       hdr->tp_status = TP_STATUS_KERNEL;
+       __sync_synchronize();
+}
+
+static inline int __v2_rx_kernel_ready(struct tpacket2_hdr *hdr)
+{
+       return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER);
+}
+
+static inline void __v2_rx_user_ready(struct tpacket2_hdr *hdr)
+{
+       hdr->tp_status = TP_STATUS_KERNEL;
+       __sync_synchronize();
+}
+
+static inline int __v1_v2_rx_kernel_ready(void *base, int version)
+{
+       switch (version) {
+       case TPACKET_V1:
+               return __v1_rx_kernel_ready(base);
+       case TPACKET_V2:
+               return __v2_rx_kernel_ready(base);
+       default:
+               bug_on(1);
+               return 0;
+       }
+}
+
+static inline void __v1_v2_rx_user_ready(void *base, int version)
+{
+       switch (version) {
+       case TPACKET_V1:
+               __v1_rx_user_ready(base);
+               break;
+       case TPACKET_V2:
+               __v2_rx_user_ready(base);
+               break;
+       }
+}
+
+static void walk_v1_v2_rx(int sock, struct ring *ring)
+{
+       struct pollfd pfd;
+       int udp_sock[2];
+       union frame_map ppd;
+       unsigned int frame_num = 0;
+
+       bug_on(ring->type != PACKET_RX_RING);
+
+       pair_udp_open(udp_sock, PORT_BASE);
+       pair_udp_setfilter(sock);
+
+       memset(&pfd, 0, sizeof(pfd));
+       pfd.fd = sock;
+       pfd.events = POLLIN | POLLERR;
+       pfd.revents = 0;
+
+       pair_udp_send(udp_sock, NUM_PACKETS);
+
+       while (total_packets < NUM_PACKETS * 2) {
+               while (__v1_v2_rx_kernel_ready(ring->rd[frame_num].iov_base,
+                                              ring->version)) {
+                       ppd.raw = ring->rd[frame_num].iov_base;
+
+                       switch (ring->version) {
+                       case TPACKET_V1:
+                               test_payload((uint8_t *) ppd.raw + ppd.v1->tp_h.tp_mac,
+                                            ppd.v1->tp_h.tp_snaplen);
+                               total_bytes += ppd.v1->tp_h.tp_snaplen;
+                               break;
+
+                       case TPACKET_V2:
+                               test_payload((uint8_t *) ppd.raw + ppd.v2->tp_h.tp_mac,
+                                            ppd.v2->tp_h.tp_snaplen);
+                               total_bytes += ppd.v2->tp_h.tp_snaplen;
+                               break;
+                       }
+
+                       status_bar_update();
+                       total_packets++;
+
+                       __v1_v2_rx_user_ready(ppd.raw, ring->version);
+
+                       frame_num = (frame_num + 1) % ring->rd_num;
+               }
+
+               poll(&pfd, 1, 1);
+       }
+
+       pair_udp_close(udp_sock);
+
+       if (total_packets != 2 * NUM_PACKETS) {
+               fprintf(stderr, "walk_v%d_rx: received %u out of %u pkts\n",
+                       ring->version, total_packets, NUM_PACKETS);
+               exit(1);
+       }
+
+       fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, total_bytes >> 1);
+}
+
+static inline int __v1_tx_kernel_ready(struct tpacket_hdr *hdr)
+{
+       return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE);
+}
+
+static inline void __v1_tx_user_ready(struct tpacket_hdr *hdr)
+{
+       hdr->tp_status = TP_STATUS_SEND_REQUEST;
+       __sync_synchronize();
+}
+
+static inline int __v2_tx_kernel_ready(struct tpacket2_hdr *hdr)
+{
+       return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE);
+}
+
+static inline void __v2_tx_user_ready(struct tpacket2_hdr *hdr)
+{
+       hdr->tp_status = TP_STATUS_SEND_REQUEST;
+       __sync_synchronize();
+}
+
+static inline int __v1_v2_tx_kernel_ready(void *base, int version)
+{
+       switch (version) {
+       case TPACKET_V1:
+               return __v1_tx_kernel_ready(base);
+       case TPACKET_V2:
+               return __v2_tx_kernel_ready(base);
+       default:
+               bug_on(1);
+               return 0;
+       }
+}
+
+static inline void __v1_v2_tx_user_ready(void *base, int version)
+{
+       switch (version) {
+       case TPACKET_V1:
+               __v1_tx_user_ready(base);
+               break;
+       case TPACKET_V2:
+               __v2_tx_user_ready(base);
+               break;
+       }
+}
+
+static void __v1_v2_set_packet_loss_discard(int sock)
+{
+       int ret, discard = 1;
+
+       ret = setsockopt(sock, SOL_PACKET, PACKET_LOSS, (void *) &discard,
+                        sizeof(discard));
+       if (ret == -1) {
+               perror("setsockopt");
+               exit(1);
+       }
+}
+
+static void walk_v1_v2_tx(int sock, struct ring *ring)
+{
+       struct pollfd pfd;
+       int rcv_sock, ret;
+       size_t packet_len;
+       union frame_map ppd;
+       char packet[1024];
+       unsigned int frame_num = 0, got = 0;
+       struct sockaddr_ll ll = {
+               .sll_family = PF_PACKET,
+               .sll_halen = ETH_ALEN,
+       };
+
+       bug_on(ring->type != PACKET_TX_RING);
+       bug_on(ring->rd_num < NUM_PACKETS);
+
+       rcv_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+       if (rcv_sock == -1) {
+               perror("socket");
+               exit(1);
+       }
+
+       pair_udp_setfilter(rcv_sock);
+
+       ll.sll_ifindex = if_nametoindex("lo");
+       ret = bind(rcv_sock, (struct sockaddr *) &ll, sizeof(ll));
+       if (ret == -1) {
+               perror("bind");
+               exit(1);
+       }
+
+       memset(&pfd, 0, sizeof(pfd));
+       pfd.fd = sock;
+       pfd.events = POLLOUT | POLLERR;
+       pfd.revents = 0;
+
+       total_packets = NUM_PACKETS;
+       create_payload(packet, &packet_len);
+
+       while (total_packets > 0) {
+               while (__v1_v2_tx_kernel_ready(ring->rd[frame_num].iov_base,
+                                              ring->version) &&
+                      total_packets > 0) {
+                       ppd.raw = ring->rd[frame_num].iov_base;
+
+                       switch (ring->version) {
+                       case TPACKET_V1:
+                               ppd.v1->tp_h.tp_snaplen = packet_len;
+                               ppd.v1->tp_h.tp_len = packet_len;
+
+                               memcpy((uint8_t *) ppd.raw + TPACKET_HDRLEN -
+                                      sizeof(struct sockaddr_ll), packet,
+                                      packet_len);
+                               total_bytes += ppd.v1->tp_h.tp_snaplen;
+                               break;
+
+                       case TPACKET_V2:
+                               ppd.v2->tp_h.tp_snaplen = packet_len;
+                               ppd.v2->tp_h.tp_len = packet_len;
+
+                               memcpy((uint8_t *) ppd.raw + TPACKET2_HDRLEN -
+                                      sizeof(struct sockaddr_ll), packet,
+                                      packet_len);
+                               total_bytes += ppd.v2->tp_h.tp_snaplen;
+                               break;
+                       }
+
+                       status_bar_update();
+                       total_packets--;
+
+                       __v1_v2_tx_user_ready(ppd.raw, ring->version);
+
+                       frame_num = (frame_num + 1) % ring->rd_num;
+               }
+
+               poll(&pfd, 1, 1);
+       }
+
+       bug_on(total_packets != 0);
+
+       ret = sendto(sock, NULL, 0, 0, NULL, 0);
+       if (ret == -1) {
+               perror("sendto");
+               exit(1);
+       }
+
+       while ((ret = recvfrom(rcv_sock, packet, sizeof(packet),
+                              0, NULL, NULL)) > 0 &&
+              total_packets < NUM_PACKETS) {
+               got += ret;
+               test_payload(packet, ret);
+
+               status_bar_update();
+               total_packets++;
+       }
+
+       close(rcv_sock);
+
+       if (total_packets != NUM_PACKETS) {
+               fprintf(stderr, "walk_v%d_rx: received %u out of %u pkts\n",
+                       ring->version, total_packets, NUM_PACKETS);
+               exit(1);
+       }
+
+       fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, got);
+}
+
+static void walk_v1_v2(int sock, struct ring *ring)
+{
+       if (ring->type == PACKET_RX_RING)
+               walk_v1_v2_rx(sock, ring);
+       else
+               walk_v1_v2_tx(sock, ring);
+}
+
+static uint64_t __v3_prev_block_seq_num = 0;
+
+void __v3_test_block_seq_num(struct block_desc *pbd)
+{
+       if (__v3_prev_block_seq_num + 1 != BLOCK_SNUM(pbd)) {
+               fprintf(stderr, "\nprev_block_seq_num:%"PRIu64", expected "
+                       "seq:%"PRIu64" != actual seq:%"PRIu64"\n",
+                       __v3_prev_block_seq_num, __v3_prev_block_seq_num + 1,
+                       (uint64_t) BLOCK_SNUM(pbd));
+               exit(1);
+       }
+
+       __v3_prev_block_seq_num = BLOCK_SNUM(pbd);
+}
+
+static void __v3_test_block_len(struct block_desc *pbd, uint32_t bytes, int block_num)
+{
+       if (BLOCK_NUM_PKTS(pbd)) {
+               if (bytes != BLOCK_LEN(pbd)) {
+                       fprintf(stderr, "\nblock:%u with %upackets, expected "
+                               "len:%u != actual len:%u\n", block_num,
+                               BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd));
+                       exit(1);
+               }
+       } else {
+               if (BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(13)) {
+                       fprintf(stderr, "\nblock:%u, expected len:%lu != "
+                               "actual len:%u\n", block_num, BLOCK_HDR_LEN,
+                               BLOCK_LEN(pbd));
+                       exit(1);
+               }
+       }
+}
+
+static void __v3_test_block_header(struct block_desc *pbd, const int block_num)
+{
+       uint32_t block_status = BLOCK_STATUS(pbd);
+
+       if ((block_status & TP_STATUS_USER) == 0) {
+               fprintf(stderr, "\nblock %u: not in TP_STATUS_USER\n", block_num);
+               exit(1);
+       }
+
+       __v3_test_block_seq_num(pbd);
+}
+
+static void __v3_walk_block(struct block_desc *pbd, const int block_num)
+{
+       int num_pkts = BLOCK_NUM_PKTS(pbd), i;
+       unsigned long bytes = 0;
+       unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(13);
+       struct tpacket3_hdr *ppd;
+
+       __v3_test_block_header(pbd, block_num);
+
+       ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd));
+       for (i = 0; i < num_pkts; ++i) {
+               bytes += ppd->tp_snaplen;
+
+               if (ppd->tp_next_offset)
+                       bytes_with_padding += ppd->tp_next_offset;
+               else
+                       bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac);
+
+               test_payload((uint8_t *) ppd + ppd->tp_mac, ppd->tp_snaplen);
+
+               status_bar_update();
+               total_packets++;
+
+               ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset);
+               __sync_synchronize();
+       }
+
+       __v3_test_block_len(pbd, bytes_with_padding, block_num);
+       total_bytes += bytes;
+}
+
+void __v3_flush_block(struct block_desc *pbd)
+{
+       BLOCK_STATUS(pbd) = TP_STATUS_KERNEL;
+       __sync_synchronize();
+}
+
+static void walk_v3_rx(int sock, struct ring *ring)
+{
+       unsigned int block_num = 0;
+       struct pollfd pfd;
+       struct block_desc *pbd;
+       int udp_sock[2];
+
+       bug_on(ring->type != PACKET_RX_RING);
+
+       pair_udp_open(udp_sock, PORT_BASE);
+       pair_udp_setfilter(sock);
+
+       memset(&pfd, 0, sizeof(pfd));
+       pfd.fd = sock;
+       pfd.events = POLLIN | POLLERR;
+       pfd.revents = 0;
+
+       pair_udp_send(udp_sock, NUM_PACKETS);
+
+       while (total_packets < NUM_PACKETS * 2) {
+               pbd = (struct block_desc *) ring->rd[block_num].iov_base;
+
+               while ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0)
+                       poll(&pfd, 1, 1);
+
+               __v3_walk_block(pbd, block_num);
+               __v3_flush_block(pbd);
+
+               block_num = (block_num + 1) % ring->rd_num;
+       }
+
+       pair_udp_close(udp_sock);
+
+       if (total_packets != 2 * NUM_PACKETS) {
+               fprintf(stderr, "walk_v3_rx: received %u out of %u pkts\n",
+                       total_packets, NUM_PACKETS);
+               exit(1);
+       }
+
+       fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, total_bytes >> 1);
+}
+
+static void walk_v3(int sock, struct ring *ring)
+{
+       if (ring->type == PACKET_RX_RING)
+               walk_v3_rx(sock, ring);
+       else
+               bug_on(1);
+}
+
+static void __v1_v2_fill(struct ring *ring, unsigned int blocks)
+{
+       ring->req.tp_block_size = getpagesize() << 2;
+       ring->req.tp_frame_size = TPACKET_ALIGNMENT << 7;
+       ring->req.tp_block_nr = blocks;
+
+       ring->req.tp_frame_nr = ring->req.tp_block_size /
+                               ring->req.tp_frame_size *
+                               ring->req.tp_block_nr;
+
+       ring->mm_len = ring->req.tp_block_size * ring->req.tp_block_nr;
+       ring->walk = walk_v1_v2;
+       ring->rd_num = ring->req.tp_frame_nr;
+       ring->flen = ring->req.tp_frame_size;
+}
+
+static void __v3_fill(struct ring *ring, unsigned int blocks)
+{
+       ring->req3.tp_retire_blk_tov = 64;
+       ring->req3.tp_sizeof_priv = 13;
+       ring->req3.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH;
+
+       ring->req3.tp_block_size = getpagesize() << 2;
+       ring->req3.tp_frame_size = TPACKET_ALIGNMENT << 7;
+       ring->req3.tp_block_nr = blocks;
+
+       ring->req3.tp_frame_nr = ring->req3.tp_block_size /
+                                ring->req3.tp_frame_size *
+                                ring->req3.tp_block_nr;
+
+       ring->mm_len = ring->req3.tp_block_size * ring->req3.tp_block_nr;
+       ring->walk = walk_v3;
+       ring->rd_num = ring->req3.tp_block_nr;
+       ring->flen = ring->req3.tp_block_size;
+}
+
+static void setup_ring(int sock, struct ring *ring, int version, int type)
+{
+       int ret = 0;
+       unsigned int blocks = 256;
+
+       ring->type = type;
+       ring->version = version;
+
+       switch (version) {
+       case TPACKET_V1:
+       case TPACKET_V2:
+               if (type == PACKET_TX_RING)
+                       __v1_v2_set_packet_loss_discard(sock);
+               __v1_v2_fill(ring, blocks);
+               ret = setsockopt(sock, SOL_PACKET, type, &ring->req,
+                                sizeof(ring->req));
+               break;
+
+       case TPACKET_V3:
+               __v3_fill(ring, blocks);
+               ret = setsockopt(sock, SOL_PACKET, type, &ring->req3,
+                                sizeof(ring->req3));
+               break;
+       }
+
+       if (ret == -1) {
+               perror("setsockopt");
+               exit(1);
+       }
+
+       ring->rd_len = ring->rd_num * sizeof(*ring->rd);
+       ring->rd = malloc(ring->rd_len);
+       if (ring->rd == NULL) {
+               perror("malloc");
+               exit(1);
+       }
+
+       total_packets = 0;
+       total_bytes = 0;
+}
+
+static void mmap_ring(int sock, struct ring *ring)
+{
+       int i;
+
+       ring->mm_space = mmap(0, ring->mm_len, PROT_READ | PROT_WRITE,
+                             MAP_SHARED | MAP_LOCKED | MAP_POPULATE, sock, 0);
+       if (ring->mm_space == MAP_FAILED) {
+               perror("mmap");
+               exit(1);
+       }
+
+       memset(ring->rd, 0, ring->rd_len);
+       for (i = 0; i < ring->rd_num; ++i) {
+               ring->rd[i].iov_base = ring->mm_space + (i * ring->flen);
+               ring->rd[i].iov_len = ring->flen;
+       }
+}
+
+static void bind_ring(int sock, struct ring *ring)
+{
+       int ret;
+
+       ring->ll.sll_family = PF_PACKET;
+       ring->ll.sll_protocol = htons(ETH_P_ALL);
+       ring->ll.sll_ifindex = if_nametoindex("lo");
+       ring->ll.sll_hatype = 0;
+       ring->ll.sll_pkttype = 0;
+       ring->ll.sll_halen = 0;
+
+       ret = bind(sock, (struct sockaddr *) &ring->ll, sizeof(ring->ll));
+       if (ret == -1) {
+               perror("bind");
+               exit(1);
+       }
+}
+
+static void walk_ring(int sock, struct ring *ring)
+{
+       ring->walk(sock, ring);
+}
+
+static void unmap_ring(int sock, struct ring *ring)
+{
+       munmap(ring->mm_space, ring->mm_len);
+       free(ring->rd);
+}
+
+static int test_kernel_bit_width(void)
+{
+       char in[512], *ptr;
+       int num = 0, fd;
+       ssize_t ret;
+
+       fd = open("/proc/kallsyms", O_RDONLY);
+       if (fd == -1) {
+               perror("open");
+               exit(1);
+       }
+
+       ret = read(fd, in, sizeof(in));
+       if (ret <= 0) {
+               perror("read");
+               exit(1);
+       }
+
+       close(fd);
+
+       ptr = in;
+       while(!isspace(*ptr)) {
+               num++;
+               ptr++;
+       }
+
+       return num * 4;
+}
+
+static int test_user_bit_width(void)
+{
+       return __WORDSIZE;
+}
+
+static const char *tpacket_str[] = {
+       [TPACKET_V1] = "TPACKET_V1",
+       [TPACKET_V2] = "TPACKET_V2",
+       [TPACKET_V3] = "TPACKET_V3",
+};
+
+static const char *type_str[] = {
+       [PACKET_RX_RING] = "PACKET_RX_RING",
+       [PACKET_TX_RING] = "PACKET_TX_RING",
+};
+
+static int test_tpacket(int version, int type)
+{
+       int sock;
+       struct ring ring;
+
+       fprintf(stderr, "test: %s with %s ", tpacket_str[version],
+               type_str[type]);
+       fflush(stderr);
+
+       if (version == TPACKET_V1 &&
+           test_kernel_bit_width() != test_user_bit_width()) {
+               fprintf(stderr, "test: skip %s %s since user and kernel "
+                       "space have different bit width\n",
+                       tpacket_str[version], type_str[type]);
+               return 0;
+       }
+
+       sock = pfsocket(version);
+       memset(&ring, 0, sizeof(ring));
+       setup_ring(sock, &ring, version, type);
+       mmap_ring(sock, &ring);
+       bind_ring(sock, &ring);
+       walk_ring(sock, &ring);
+       unmap_ring(sock, &ring);
+       close(sock);
+
+       fprintf(stderr, "\n");
+       return 0;
+}
+
+int main(void)
+{
+       int ret = 0;
+
+       ret |= test_tpacket(TPACKET_V1, PACKET_RX_RING);
+       ret |= test_tpacket(TPACKET_V1, PACKET_TX_RING);
+
+       ret |= test_tpacket(TPACKET_V2, PACKET_RX_RING);
+       ret |= test_tpacket(TPACKET_V2, PACKET_TX_RING);
+
+       ret |= test_tpacket(TPACKET_V3, PACKET_RX_RING);
+
+       if (ret)
+               return 1;
+
+       printf("OK. All tests passed\n");
+       return 0;
+}
diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests
new file mode 100644 (file)
index 0000000..5246e78
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+if [ $(id -u) != 0 ]; then
+       echo $msg must be run as root >&2
+       exit 0
+fi
+
+echo "--------------------"
+echo "running psock_fanout test"
+echo "--------------------"
+./psock_fanout
+if [ $? -ne 0 ]; then
+       echo "[FAIL]"
+else
+       echo "[PASS]"
+fi
+
+echo "--------------------"
+echo "running psock_tpacket test"
+echo "--------------------"
+./psock_tpacket
+if [ $? -ne 0 ]; then
+       echo "[FAIL]"
+else
+       echo "[PASS]"
+fi
diff --git a/tools/testing/selftests/net/run_netsocktests b/tools/testing/selftests/net/run_netsocktests
new file mode 100644 (file)
index 0000000..c09a682
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+echo "--------------------"
+echo "running socket test"
+echo "--------------------"
+./socket
+if [ $? -ne 0 ]; then
+       echo "[FAIL]"
+else
+       echo "[PASS]"
+fi
+
diff --git a/tools/testing/selftests/net/socket.c b/tools/testing/selftests/net/socket.c
new file mode 100644 (file)
index 0000000..0f227f2
--- /dev/null
@@ -0,0 +1,92 @@
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+struct socket_testcase {
+       int     domain;
+       int     type;
+       int     protocol;
+
+       /* 0    = valid file descriptor
+        * -foo = error foo
+        */
+       int     expect;
+
+       /* If non-zero, accept EAFNOSUPPORT to handle the case
+        * of the protocol not being configured into the kernel.
+        */
+       int     nosupport_ok;
+};
+
+static struct socket_testcase tests[] = {
+       { AF_MAX,  0,           0,           -EAFNOSUPPORT,    0 },
+       { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0,                1  },
+       { AF_INET, SOCK_DGRAM,  IPPROTO_TCP, -EPROTONOSUPPORT, 1  },
+       { AF_INET, SOCK_DGRAM,  IPPROTO_UDP, 0,                1  },
+       { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1  },
+};
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#define ERR_STRING_SZ  64
+
+static int run_tests(void)
+{
+       char err_string1[ERR_STRING_SZ];
+       char err_string2[ERR_STRING_SZ];
+       int i, err;
+
+       err = 0;
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
+               struct socket_testcase *s = &tests[i];
+               int fd;
+
+               fd = socket(s->domain, s->type, s->protocol);
+               if (fd < 0) {
+                       if (s->nosupport_ok &&
+                           errno == EAFNOSUPPORT)
+                               continue;
+
+                       if (s->expect < 0 &&
+                           errno == -s->expect)
+                               continue;
+
+                       strerror_r(-s->expect, err_string1, ERR_STRING_SZ);
+                       strerror_r(errno, err_string2, ERR_STRING_SZ);
+
+                       fprintf(stderr, "socket(%d, %d, %d) expected "
+                               "err (%s) got (%s)\n",
+                               s->domain, s->type, s->protocol,
+                               err_string1, err_string2);
+
+                       err = -1;
+                       break;
+               } else {
+                       close(fd);
+
+                       if (s->expect < 0) {
+                               strerror_r(errno, err_string1, ERR_STRING_SZ);
+
+                               fprintf(stderr, "socket(%d, %d, %d) expected "
+                                       "success got err (%s)\n",
+                                       s->domain, s->type, s->protocol,
+                                       err_string1);
+
+                               err = -1;
+                               break;
+                       }
+               }
+       }
+
+       return err;
+}
+
+int main(void)
+{
+       int err = run_tests();
+
+       return err;
+}